or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mderror-handling.mdevaluation-contexts.mdexpression-evaluation.mdindex.mdmethod-constructor-resolution.mdproperty-index-access.mdspel-configuration.mdtype-system-support.md

method-constructor-resolution.mddocs/

0

# Method and Constructor Resolution

1

2

This document covers SpEL's method and constructor resolution capabilities, including resolver interfaces, standard implementations, and custom resolver development.

3

4

## Core Resolution Interfaces

5

6

### MethodResolver Interface

7

8

```java

9

@FunctionalInterface

10

public interface MethodResolver {

11

MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,

12

List<TypeDescriptor> argumentTypes) throws AccessException;

13

}

14

```

15

{ .api }

16

17

### ConstructorResolver Interface

18

19

```java

20

public interface ConstructorResolver {

21

ConstructorExecutor resolve(EvaluationContext context, String typeName,

22

List<TypeDescriptor> argumentTypes) throws AccessException;

23

}

24

```

25

{ .api }

26

27

### Executor Interfaces

28

29

```java

30

public interface MethodExecutor {

31

TypedValue execute(EvaluationContext context, Object target, Object... arguments)

32

throws AccessException;

33

}

34

35

public interface ConstructorExecutor {

36

TypedValue execute(EvaluationContext context, Object... arguments) throws AccessException;

37

}

38

```

39

{ .api }

40

41

## Standard Method Resolution

42

43

### ReflectiveMethodResolver Class

44

45

```java

46

public class ReflectiveMethodResolver implements MethodResolver {

47

public ReflectiveMethodResolver();

48

public ReflectiveMethodResolver(boolean useDistance);

49

50

public void registerMethodFilter(Class<?> type, MethodFilter filter);

51

52

@Override

53

public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,

54

List<TypeDescriptor> argumentTypes) throws AccessException;

55

}

56

```

57

{ .api }

58

59

The default method resolver that uses Java reflection to find and invoke methods.

60

61

### DataBindingMethodResolver Class

62

63

```java

64

public class DataBindingMethodResolver implements MethodResolver {

65

@Override

66

public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,

67

List<TypeDescriptor> argumentTypes) throws AccessException;

68

}

69

```

70

{ .api }

71

72

Optimized method resolver for data-binding scenarios with restricted method access.

73

74

### Method Resolution Examples

75

76

```java

77

public class Calculator {

78

public int add(int a, int b) {

79

return a + b;

80

}

81

82

public double add(double a, double b) {

83

return a + b;

84

}

85

86

public String concatenate(String... parts) {

87

return String.join("", parts);

88

}

89

90

public static int multiply(int a, int b) {

91

return a * b;

92

}

93

}

94

95

StandardEvaluationContext context = new StandardEvaluationContext();

96

context.addMethodResolver(new ReflectiveMethodResolver());

97

98

Calculator calc = new Calculator();

99

ExpressionParser parser = new SpelExpressionParser();

100

101

// Instance method invocation

102

Expression exp = parser.parseExpression("add(5, 3)");

103

Integer result = exp.getValue(context, calc, Integer.class); // 8

104

105

// Method overloading resolution

106

exp = parser.parseExpression("add(5.5, 3.2)");

107

Double doubleResult = exp.getValue(context, calc, Double.class); // 8.7

108

109

// Varargs method invocation

110

exp = parser.parseExpression("concatenate('Hello', ' ', 'World')");

111

String text = exp.getValue(context, calc, String.class); // "Hello World"

112

113

// Static method invocation (requires type reference)

114

exp = parser.parseExpression("T(Calculator).multiply(4, 6)");

115

Integer product = exp.getValue(context, Integer.class); // 24

116

```

117

{ .api }

118

119

### Method Filtering

120

121

```java

122

public class RestrictedMethodResolver extends ReflectiveMethodResolver {

123

124

public RestrictedMethodResolver() {

125

super();

126

127

// Register method filters for security

128

registerMethodFilter(Object.class, new SafeMethodFilter());

129

registerMethodFilter(String.class, new StringMethodFilter());

130

}

131

}

132

133

// Filter that allows only safe Object methods

134

public class SafeMethodFilter implements MethodFilter {

135

private final Set<String> allowedMethods = Set.of(

136

"toString", "hashCode", "equals"

137

);

138

139

@Override

140

public List<Method> filter(List<Method> methods) {

141

return methods.stream()

142

.filter(method -> allowedMethods.contains(method.getName()))

143

.collect(Collectors.toList());

144

}

145

}

146

147

// Filter that restricts String methods

148

public class StringMethodFilter implements MethodFilter {

149

private final Set<String> allowedMethods = Set.of(

150

"length", "toUpperCase", "toLowerCase", "trim", "substring",

151

"indexOf", "startsWith", "endsWith", "contains"

152

);

153

154

@Override

155

public List<Method> filter(List<Method> methods) {

156

return methods.stream()

157

.filter(method -> allowedMethods.contains(method.getName()))

158

.collect(Collectors.toList());

159

}

160

}

161

162

// Usage

163

StandardEvaluationContext context = new StandardEvaluationContext();

164

context.addMethodResolver(new RestrictedMethodResolver());

165

166

String text = "Hello World";

167

ExpressionParser parser = new SpelExpressionParser();

168

169

// Allowed methods work

170

Expression exp = parser.parseExpression("length()");

171

Integer length = exp.getValue(context, text, Integer.class); // 11

172

173

exp = parser.parseExpression("toUpperCase()");

174

String upper = exp.getValue(context, text, String.class); // "HELLO WORLD"

175

176

// Restricted methods would fail resolution

177

// exp = parser.parseExpression("getClass()"); // Would fail

178

```

179

{ .api }

180

181

## Standard Constructor Resolution

182

183

### ReflectiveConstructorResolver Class

184

185

```java

186

public class ReflectiveConstructorResolver implements ConstructorResolver {

187

@Override

188

public ConstructorExecutor resolve(EvaluationContext context, String typeName,

189

List<TypeDescriptor> argumentTypes) throws AccessException;

190

}

191

```

192

{ .api }

193

194

### Constructor Resolution Examples

195

196

```java

197

StandardEvaluationContext context = new StandardEvaluationContext();

198

context.addConstructorResolver(new ReflectiveConstructorResolver());

199

200

ExpressionParser parser = new SpelExpressionParser();

201

202

// Constructor invocation with arguments

203

Expression exp = parser.parseExpression("new String('Hello World')");

204

String result = exp.getValue(context, String.class); // "Hello World"

205

206

// Default constructor

207

exp = parser.parseExpression("new java.util.ArrayList()");

208

ArrayList<?> list = exp.getValue(context, ArrayList.class); // empty list

209

210

// Constructor with multiple arguments

211

exp = parser.parseExpression("new java.util.Date(2023, 0, 1)");

212

Date date = exp.getValue(context, Date.class);

213

214

// Constructor chaining with method calls

215

exp = parser.parseExpression("new StringBuilder('Hello').append(' World').toString()");

216

String text = exp.getValue(context, String.class); // "Hello World"

217

```

218

{ .api }

219

220

## Standard Executors

221

222

### ReflectiveMethodExecutor Class

223

224

```java

225

public class ReflectiveMethodExecutor implements MethodExecutor {

226

public ReflectiveMethodExecutor(Method method);

227

228

@Override

229

public TypedValue execute(EvaluationContext context, Object target, Object... arguments)

230

throws AccessException;

231

232

public Method getMethod();

233

}

234

```

235

{ .api }

236

237

### ReflectiveConstructorExecutor Class

238

239

```java

240

public class ReflectiveConstructorExecutor implements ConstructorExecutor {

241

public ReflectiveConstructorExecutor(Constructor<?> constructor);

242

243

@Override

244

public TypedValue execute(EvaluationContext context, Object... arguments)

245

throws AccessException;

246

247

public Constructor<?> getConstructor();

248

}

249

```

250

{ .api }

251

252

## Custom Method Resolvers

253

254

### Cached Method Resolver

255

256

```java

257

public class CachingMethodResolver implements MethodResolver {

258

private final MethodResolver delegate;

259

private final Map<MethodKey, MethodExecutor> cache = new ConcurrentHashMap<>();

260

261

public CachingMethodResolver(MethodResolver delegate) {

262

this.delegate = delegate;

263

}

264

265

@Override

266

public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,

267

List<TypeDescriptor> argumentTypes) throws AccessException {

268

269

MethodKey key = new MethodKey(targetObject.getClass(), name, argumentTypes);

270

271

return cache.computeIfAbsent(key, k -> {

272

try {

273

return delegate.resolve(context, targetObject, name, argumentTypes);

274

} catch (AccessException e) {

275

return null; // Cache null results to avoid repeated failures

276

}

277

});

278

}

279

280

private static class MethodKey {

281

private final Class<?> targetClass;

282

private final String methodName;

283

private final List<Class<?>> argumentTypes;

284

285

public MethodKey(Class<?> targetClass, String methodName, List<TypeDescriptor> argTypes) {

286

this.targetClass = targetClass;

287

this.methodName = methodName;

288

this.argumentTypes = argTypes.stream()

289

.map(TypeDescriptor::getType)

290

.collect(Collectors.toList());

291

}

292

293

@Override

294

public boolean equals(Object obj) {

295

if (this == obj) return true;

296

if (!(obj instanceof MethodKey)) return false;

297

MethodKey other = (MethodKey) obj;

298

return Objects.equals(targetClass, other.targetClass) &&

299

Objects.equals(methodName, other.methodName) &&

300

Objects.equals(argumentTypes, other.argumentTypes);

301

}

302

303

@Override

304

public int hashCode() {

305

return Objects.hash(targetClass, methodName, argumentTypes);

306

}

307

}

308

}

309

```

310

{ .api }

311

312

### Function Registry Resolver

313

314

```java

315

public class FunctionRegistryResolver implements MethodResolver {

316

private final Map<String, Method> functions = new HashMap<>();

317

318

public void registerFunction(String name, Method method) {

319

functions.put(name, method);

320

}

321

322

public void registerFunction(String name, Class<?> clazz, String methodName,

323

Class<?>... parameterTypes) throws NoSuchMethodException {

324

Method method = clazz.getDeclaredMethod(methodName, parameterTypes);

325

functions.put(name, method);

326

}

327

328

@Override

329

public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,

330

List<TypeDescriptor> argumentTypes) throws AccessException {

331

332

Method function = functions.get(name);

333

if (function != null && isCompatible(function, argumentTypes)) {

334

return new FunctionExecutor(function);

335

}

336

337

return null; // Let other resolvers handle it

338

}

339

340

private boolean isCompatible(Method method, List<TypeDescriptor> argumentTypes) {

341

Class<?>[] paramTypes = method.getParameterTypes();

342

343

if (paramTypes.length != argumentTypes.size()) {

344

return false;

345

}

346

347

for (int i = 0; i < paramTypes.length; i++) {

348

if (!paramTypes[i].isAssignableFrom(argumentTypes.get(i).getType())) {

349

return false;

350

}

351

}

352

353

return true;

354

}

355

356

private static class FunctionExecutor implements MethodExecutor {

357

private final Method method;

358

359

public FunctionExecutor(Method method) {

360

this.method = method;

361

}

362

363

@Override

364

public TypedValue execute(EvaluationContext context, Object target, Object... arguments)

365

throws AccessException {

366

try {

367

Object result = method.invoke(null, arguments); // Static invocation

368

return new TypedValue(result);

369

} catch (Exception e) {

370

throw new AccessException("Function execution failed", e);

371

}

372

}

373

}

374

}

375

376

// Usage

377

public class MathFunctions {

378

public static double sqrt(double value) {

379

return Math.sqrt(value);

380

}

381

382

public static long factorial(int n) {

383

return n <= 1 ? 1 : n * factorial(n - 1);

384

}

385

386

public static boolean isPrime(int n) {

387

if (n < 2) return false;

388

for (int i = 2; i <= Math.sqrt(n); i++) {

389

if (n % i == 0) return false;

390

}

391

return true;

392

}

393

}

394

395

FunctionRegistryResolver functionResolver = new FunctionRegistryResolver();

396

functionResolver.registerFunction("sqrt", MathFunctions.class, "sqrt", double.class);

397

functionResolver.registerFunction("factorial", MathFunctions.class, "factorial", int.class);

398

functionResolver.registerFunction("isPrime", MathFunctions.class, "isPrime", int.class);

399

400

StandardEvaluationContext context = new StandardEvaluationContext();

401

context.addMethodResolver(functionResolver);

402

403

ExpressionParser parser = new SpelExpressionParser();

404

405

Expression exp = parser.parseExpression("sqrt(16)");

406

Double result = exp.getValue(context, Double.class); // 4.0

407

408

exp = parser.parseExpression("factorial(5)");

409

Long factorial = exp.getValue(context, Long.class); // 120

410

411

exp = parser.parseExpression("isPrime(17)");

412

Boolean prime = exp.getValue(context, Boolean.class); // true

413

```

414

{ .api }

415

416

### Domain-Specific Method Resolver

417

418

```java

419

public class DomainSpecificResolver implements MethodResolver {

420

private final Map<Class<?>, DomainHandler> domainHandlers = new HashMap<>();

421

422

public void registerDomainHandler(Class<?> domainClass, DomainHandler handler) {

423

domainHandlers.put(domainClass, handler);

424

}

425

426

@Override

427

public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,

428

List<TypeDescriptor> argumentTypes) throws AccessException {

429

430

DomainHandler handler = domainHandlers.get(targetObject.getClass());

431

if (handler != null && handler.canHandle(name, argumentTypes)) {

432

return new DomainMethodExecutor(handler, name);

433

}

434

435

return null;

436

}

437

438

public interface DomainHandler {

439

boolean canHandle(String methodName, List<TypeDescriptor> argumentTypes);

440

Object execute(String methodName, Object target, Object... arguments) throws Exception;

441

}

442

443

private static class DomainMethodExecutor implements MethodExecutor {

444

private final DomainHandler handler;

445

private final String methodName;

446

447

public DomainMethodExecutor(DomainHandler handler, String methodName) {

448

this.handler = handler;

449

this.methodName = methodName;

450

}

451

452

@Override

453

public TypedValue execute(EvaluationContext context, Object target, Object... arguments)

454

throws AccessException {

455

try {

456

Object result = handler.execute(methodName, target, arguments);

457

return new TypedValue(result);

458

} catch (Exception e) {

459

throw new AccessException("Domain method execution failed", e);

460

}

461

}

462

}

463

}

464

465

// Domain-specific handler for business objects

466

public class BusinessObjectHandler implements DomainSpecificResolver.DomainHandler {

467

468

@Override

469

public boolean canHandle(String methodName, List<TypeDescriptor> argumentTypes) {

470

return methodName.startsWith("calculate") ||

471

methodName.startsWith("validate") ||

472

methodName.startsWith("transform");

473

}

474

475

@Override

476

public Object execute(String methodName, Object target, Object... arguments) throws Exception {

477

BusinessObject bo = (BusinessObject) target;

478

479

return switch (methodName) {

480

case "calculateTotal" -> bo.getItems().stream()

481

.mapToDouble(Item::getPrice)

482

.sum();

483

case "validateRequired" -> bo.getName() != null && !bo.getName().isEmpty();

484

case "transformToUpper" -> bo.getName().toUpperCase();

485

default -> throw new UnsupportedOperationException("Unknown method: " + methodName);

486

};

487

}

488

}

489

490

// Usage

491

DomainSpecificResolver domainResolver = new DomainSpecificResolver();

492

domainResolver.registerDomainHandler(BusinessObject.class, new BusinessObjectHandler());

493

494

StandardEvaluationContext context = new StandardEvaluationContext();

495

context.addMethodResolver(domainResolver);

496

497

BusinessObject bo = new BusinessObject("Product");

498

bo.addItem(new Item("Item1", 10.0));

499

bo.addItem(new Item("Item2", 20.0));

500

501

ExpressionParser parser = new SpelExpressionParser();

502

503

Expression exp = parser.parseExpression("calculateTotal()");

504

Double total = exp.getValue(context, bo, Double.class); // 30.0

505

506

exp = parser.parseExpression("validateRequired()");

507

Boolean valid = exp.getValue(context, bo, Boolean.class); // true

508

509

exp = parser.parseExpression("transformToUpper()");

510

String upper = exp.getValue(context, bo, String.class); // "PRODUCT"

511

```

512

{ .api }

513

514

## Custom Constructor Resolvers

515

516

### Factory-based Constructor Resolver

517

518

```java

519

public class FactoryConstructorResolver implements ConstructorResolver {

520

private final Map<String, ObjectFactory<?>> factories = new HashMap<>();

521

522

public void registerFactory(String typeName, ObjectFactory<?> factory) {

523

factories.put(typeName, factory);

524

}

525

526

@Override

527

public ConstructorExecutor resolve(EvaluationContext context, String typeName,

528

List<TypeDescriptor> argumentTypes) throws AccessException {

529

530

ObjectFactory<?> factory = factories.get(typeName);

531

if (factory != null) {

532

return new FactoryExecutor(factory);

533

}

534

535

return null;

536

}

537

538

@FunctionalInterface

539

public interface ObjectFactory<T> {

540

T create(Object... arguments) throws Exception;

541

}

542

543

private static class FactoryExecutor implements ConstructorExecutor {

544

private final ObjectFactory<?> factory;

545

546

public FactoryExecutor(ObjectFactory<?> factory) {

547

this.factory = factory;

548

}

549

550

@Override

551

public TypedValue execute(EvaluationContext context, Object... arguments)

552

throws AccessException {

553

try {

554

Object result = factory.create(arguments);

555

return new TypedValue(result);

556

} catch (Exception e) {

557

throw new AccessException("Factory construction failed", e);

558

}

559

}

560

}

561

}

562

563

// Usage

564

FactoryConstructorResolver factoryResolver = new FactoryConstructorResolver();

565

566

// Register custom object factories

567

factoryResolver.registerFactory("Person", args -> {

568

if (args.length == 2) {

569

return new Person((String) args[0], (Integer) args[1]);

570

}

571

throw new IllegalArgumentException("Person requires name and age");

572

});

573

574

factoryResolver.registerFactory("Product", args -> {

575

if (args.length == 1) {

576

return new Product((String) args[0]);

577

}

578

throw new IllegalArgumentException("Product requires name");

579

});

580

581

StandardEvaluationContext context = new StandardEvaluationContext();

582

context.addConstructorResolver(factoryResolver);

583

584

ExpressionParser parser = new SpelExpressionParser();

585

586

// Use factory to create objects

587

Expression exp = parser.parseExpression("new Person('John', 30)");

588

Person person = exp.getValue(context, Person.class);

589

590

exp = parser.parseExpression("new Product('Widget')");

591

Product product = exp.getValue(context, Product.class);

592

```

593

{ .api }

594

595

## Advanced Resolution Patterns

596

597

### Chainable Method Resolver

598

599

```java

600

public class ChainableMethodResolver implements MethodResolver {

601

private final List<MethodResolver> resolvers = new ArrayList<>();

602

603

public ChainableMethodResolver addResolver(MethodResolver resolver) {

604

resolvers.add(resolver);

605

return this;

606

}

607

608

@Override

609

public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,

610

List<TypeDescriptor> argumentTypes) throws AccessException {

611

612

for (MethodResolver resolver : resolvers) {

613

try {

614

MethodExecutor executor = resolver.resolve(context, targetObject, name, argumentTypes);

615

if (executor != null) {

616

return executor;

617

}

618

} catch (AccessException e) {

619

// Continue to next resolver

620

}

621

}

622

623

return null; // No resolver could handle the method

624

}

625

}

626

627

// Usage

628

ChainableMethodResolver chainResolver = new ChainableMethodResolver()

629

.addResolver(new CachingMethodResolver(new ReflectiveMethodResolver()))

630

.addResolver(new FunctionRegistryResolver())

631

.addResolver(new DomainSpecificResolver());

632

633

StandardEvaluationContext context = new StandardEvaluationContext();

634

context.setMethodResolvers(Arrays.asList(chainResolver));

635

```

636

{ .api }

637

638

### Conditional Method Resolver

639

640

```java

641

public class ConditionalMethodResolver implements MethodResolver {

642

private final Predicate<ResolutionContext> condition;

643

private final MethodResolver resolver;

644

645

public ConditionalMethodResolver(Predicate<ResolutionContext> condition,

646

MethodResolver resolver) {

647

this.condition = condition;

648

this.resolver = resolver;

649

}

650

651

@Override

652

public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,

653

List<TypeDescriptor> argumentTypes) throws AccessException {

654

655

ResolutionContext resContext = new ResolutionContext(context, targetObject, name, argumentTypes);

656

657

if (condition.test(resContext)) {

658

return resolver.resolve(context, targetObject, name, argumentTypes);

659

}

660

661

return null;

662

}

663

664

public static class ResolutionContext {

665

private final EvaluationContext evaluationContext;

666

private final Object targetObject;

667

private final String methodName;

668

private final List<TypeDescriptor> argumentTypes;

669

670

public ResolutionContext(EvaluationContext evaluationContext, Object targetObject,

671

String methodName, List<TypeDescriptor> argumentTypes) {

672

this.evaluationContext = evaluationContext;

673

this.targetObject = targetObject;

674

this.methodName = methodName;

675

this.argumentTypes = argumentTypes;

676

}

677

678

// Getters...

679

public EvaluationContext getEvaluationContext() { return evaluationContext; }

680

public Object getTargetObject() { return targetObject; }

681

public String getMethodName() { return methodName; }

682

public List<TypeDescriptor> getArgumentTypes() { return argumentTypes; }

683

}

684

}

685

686

// Usage examples

687

MethodResolver secureResolver = new ConditionalMethodResolver(

688

ctx -> isSecureContext(ctx.getEvaluationContext()),

689

new RestrictedMethodResolver()

690

);

691

692

MethodResolver developmentResolver = new ConditionalMethodResolver(

693

ctx -> isDevelopmentMode(),

694

new ReflectiveMethodResolver()

695

);

696

697

private static boolean isSecureContext(EvaluationContext context) {

698

return context instanceof SimpleEvaluationContext;

699

}

700

701

private static boolean isDevelopmentMode() {

702

return "development".equals(System.getProperty("environment"));

703

}

704

```

705

{ .api }

706

707

## Performance Optimization

708

709

### Method Resolution Metrics

710

711

```java

712

public class MetricsMethodResolver implements MethodResolver {

713

private final MethodResolver delegate;

714

private final Map<String, AtomicLong> resolutionCounts = new ConcurrentHashMap<>();

715

private final Map<String, AtomicLong> resolutionTimes = new ConcurrentHashMap<>();

716

717

public MetricsMethodResolver(MethodResolver delegate) {

718

this.delegate = delegate;

719

}

720

721

@Override

722

public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,

723

List<TypeDescriptor> argumentTypes) throws AccessException {

724

725

String key = targetObject.getClass().getSimpleName() + "." + name;

726

long startTime = System.nanoTime();

727

728

try {

729

MethodExecutor executor = delegate.resolve(context, targetObject, name, argumentTypes);

730

731

if (executor != null) {

732

return new MetricsMethodExecutor(executor, key);

733

}

734

735

return null;

736

} finally {

737

long duration = System.nanoTime() - startTime;

738

resolutionCounts.computeIfAbsent(key, k -> new AtomicLong()).incrementAndGet();

739

resolutionTimes.computeIfAbsent(key, k -> new AtomicLong()).addAndGet(duration);

740

}

741

}

742

743

public void printMetrics() {

744

System.out.println("Method Resolution Metrics:");

745

resolutionCounts.forEach((method, count) -> {

746

long totalTime = resolutionTimes.get(method).get();

747

long avgTime = totalTime / count.get();

748

System.out.printf("%s: %d calls, avg %d ns%n", method, count.get(), avgTime);

749

});

750

}

751

752

private static class MetricsMethodExecutor implements MethodExecutor {

753

private final MethodExecutor delegate;

754

private final String methodKey;

755

756

public MetricsMethodExecutor(MethodExecutor delegate, String methodKey) {

757

this.delegate = delegate;

758

this.methodKey = methodKey;

759

}

760

761

@Override

762

public TypedValue execute(EvaluationContext context, Object target, Object... arguments)

763

throws AccessException {

764

// Could add execution metrics here as well

765

return delegate.execute(context, target, arguments);

766

}

767

}

768

}

769

```

770

{ .api }

771

772

## Best Practices

773

774

### Security Best Practices

775

776

```java

777

// 1. Use method filtering for security

778

public class SecurityAwareMethodResolver extends ReflectiveMethodResolver {

779

780

public SecurityAwareMethodResolver() {

781

super();

782

783

// Register security filters for dangerous classes

784

registerMethodFilter(Runtime.class, methods -> Collections.emptyList());

785

registerMethodFilter(ProcessBuilder.class, methods -> Collections.emptyList());

786

registerMethodFilter(System.class, this::filterSystemMethods);

787

registerMethodFilter(Class.class, this::filterClassMethods);

788

}

789

790

private List<Method> filterSystemMethods(List<Method> methods) {

791

// Only allow safe System methods

792

Set<String> allowedMethods = Set.of("currentTimeMillis", "nanoTime");

793

return methods.stream()

794

.filter(m -> allowedMethods.contains(m.getName()))

795

.collect(Collectors.toList());

796

}

797

798

private List<Method> filterClassMethods(List<Method> methods) {

799

// Prevent reflection access

800

return Collections.emptyList();

801

}

802

}

803

804

// 2. Validate method arguments

805

public class ValidatingMethodExecutor implements MethodExecutor {

806

private final MethodExecutor delegate;

807

808

@Override

809

public TypedValue execute(EvaluationContext context, Object target, Object... arguments)

810

throws AccessException {

811

812

validateArguments(arguments);

813

return delegate.execute(context, target, arguments);

814

}

815

816

private void validateArguments(Object[] arguments) throws AccessException {

817

for (Object arg : arguments) {

818

if (arg instanceof String) {

819

String str = (String) arg;

820

if (str.length() > 1000) { // Prevent extremely large strings

821

throw new AccessException("String argument too large");

822

}

823

}

824

}

825

}

826

}

827

```

828

{ .api }

829

830

### Performance Tips

831

832

1. **Use method filtering**: Filter out methods at resolution time rather than execution time

833

2. **Implement caching**: Cache method resolution results for frequently called methods

834

3. **Order resolvers by frequency**: Place most commonly used resolvers first

835

4. **Use conditional resolvers**: Only invoke expensive resolvers when necessary

836

5. **Monitor resolution metrics**: Track which methods are resolved most frequently

837

6. **Prefer compilation**: Use `CompilablePropertyAccessor` for hot paths where possible

838

839

### Error Handling Best Practices

840

841

```java

842

public class RobustMethodResolver implements MethodResolver {

843

private final MethodResolver delegate;

844

private final boolean logErrors;

845

846

public RobustMethodResolver(MethodResolver delegate, boolean logErrors) {

847

this.delegate = delegate;

848

this.logErrors = logErrors;

849

}

850

851

@Override

852

public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,

853

List<TypeDescriptor> argumentTypes) throws AccessException {

854

855

try {

856

return delegate.resolve(context, targetObject, name, argumentTypes);

857

} catch (AccessException e) {

858

if (logErrors) {

859

System.err.printf("Method resolution failed for %s.%s: %s%n",

860

targetObject.getClass().getSimpleName(), name, e.getMessage());

861

}

862

return null; // Let other resolvers try

863

} catch (Exception e) {

864

if (logErrors) {

865

System.err.printf("Unexpected error resolving %s.%s: %s%n",

866

targetObject.getClass().getSimpleName(), name, e.getMessage());

867

}

868

return null;

869

}

870

}

871

}

872

```

873

{ .api }