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

evaluation-contexts.mddocs/

0

# Evaluation Contexts

1

2

This document covers SpEL's evaluation context implementations, providing the runtime environment for expression evaluation including variables, functions, and various resolvers.

3

4

## EvaluationContext Interface

5

6

### Core Interface Definition

7

8

```java

9

public interface EvaluationContext {

10

// Root object access

11

TypedValue getRootObject();

12

13

// Accessor chains

14

List<PropertyAccessor> getPropertyAccessors();

15

List<IndexAccessor> getIndexAccessors();

16

List<ConstructorResolver> getConstructorResolvers();

17

List<MethodResolver> getMethodResolvers();

18

19

// Specialized resolvers

20

BeanResolver getBeanResolver();

21

TypeLocator getTypeLocator();

22

TypeConverter getTypeConverter();

23

TypeComparator getTypeComparator();

24

OperatorOverloader getOperatorOverloader();

25

26

// Variable management

27

void setVariable(String name, Object value);

28

Object lookupVariable(String name);

29

30

// Assignment support (6.2+)

31

boolean isAssignmentEnabled();

32

TypedValue assignVariable(String name, Supplier<TypedValue> valueSupplier);

33

}

34

```

35

{ .api }

36

37

## StandardEvaluationContext

38

39

### Class Definition

40

41

```java

42

public class StandardEvaluationContext implements EvaluationContext {

43

// Constructors

44

public StandardEvaluationContext();

45

public StandardEvaluationContext(Object rootObject);

46

47

// Root object management

48

public void setRootObject(Object rootObject);

49

public void setRootObject(Object rootObject, TypeDescriptor typeDescriptor);

50

public TypedValue getRootObject();

51

52

// Accessor management

53

public void setPropertyAccessors(List<PropertyAccessor> propertyAccessors);

54

public List<PropertyAccessor> getPropertyAccessors();

55

public void addPropertyAccessor(PropertyAccessor accessor);

56

57

public void setIndexAccessors(List<IndexAccessor> indexAccessors);

58

public List<IndexAccessor> getIndexAccessors();

59

public void addIndexAccessor(IndexAccessor accessor);

60

61

// Resolver management

62

public void setConstructorResolvers(List<ConstructorResolver> constructorResolvers);

63

public List<ConstructorResolver> getConstructorResolvers();

64

public void addConstructorResolver(ConstructorResolver resolver);

65

66

public void setMethodResolvers(List<MethodResolver> methodResolvers);

67

public List<MethodResolver> getMethodResolvers();

68

public void addMethodResolver(MethodResolver resolver);

69

70

// Specialized component setters

71

public void setBeanResolver(BeanResolver beanResolver);

72

public BeanResolver getBeanResolver();

73

74

public void setTypeLocator(TypeLocator typeLocator);

75

public TypeLocator getTypeLocator();

76

77

public void setTypeConverter(TypeConverter typeConverter);

78

public TypeConverter getTypeConverter();

79

80

public void setTypeComparator(TypeComparator typeComparator);

81

public TypeComparator getTypeComparator();

82

83

public void setOperatorOverloader(OperatorOverloader operatorOverloader);

84

public OperatorOverloader getOperatorOverloader();

85

86

// Variable management

87

public void setVariables(Map<String, Object> variables);

88

public void setVariable(String name, Object value);

89

public Object lookupVariable(String name);

90

91

// Function registration

92

public void registerFunction(String name, Method method);

93

94

// Assignment support

95

public boolean isAssignmentEnabled();

96

public void setAssignmentEnabled(boolean assignmentEnabled);

97

public TypedValue assignVariable(String name, Supplier<TypedValue> valueSupplier);

98

}

99

```

100

{ .api }

101

102

### Basic Usage

103

104

```java

105

// Create with default settings

106

StandardEvaluationContext context = new StandardEvaluationContext();

107

108

// Create with root object

109

Person person = new Person("John", 30);

110

StandardEvaluationContext context = new StandardEvaluationContext(person);

111

112

// Set root object later

113

context.setRootObject(person);

114

115

// Basic expression evaluation

116

ExpressionParser parser = new SpelExpressionParser();

117

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

118

String name = exp.getValue(context, String.class); // "John"

119

```

120

{ .api }

121

122

### Variables and Functions

123

124

```java

125

StandardEvaluationContext context = new StandardEvaluationContext();

126

127

// Set individual variables

128

context.setVariable("greeting", "Hello");

129

context.setVariable("maxRetries", 3);

130

131

// Set multiple variables

132

Map<String, Object> variables = new HashMap<>();

133

variables.put("prefix", "Mr.");

134

variables.put("suffix", "Jr.");

135

context.setVariables(variables);

136

137

// Register functions

138

context.registerFunction("reverse",

139

StringUtils.class.getDeclaredMethod("reverse", String.class));

140

context.registerFunction("randomInt",

141

Math.class.getDeclaredMethod("random"));

142

143

// Use variables and functions in expressions

144

ExpressionParser parser = new SpelExpressionParser();

145

146

Expression exp = parser.parseExpression("#greeting + ' World'");

147

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

148

149

exp = parser.parseExpression("#reverse('hello')");

150

String reversed = exp.getValue(context, String.class);

151

152

exp = parser.parseExpression("#randomInt() * 100");

153

Double random = exp.getValue(context, Double.class);

154

```

155

{ .api }

156

157

### Custom Resolvers

158

159

```java

160

StandardEvaluationContext context = new StandardEvaluationContext();

161

162

// Add custom property accessor

163

context.addPropertyAccessor(new MapAccessor());

164

context.addPropertyAccessor(new ReflectivePropertyAccessor());

165

166

// Add custom method resolver

167

context.addMethodResolver(new ReflectiveMethodResolver());

168

169

// Set type locator with custom packages

170

StandardTypeLocator typeLocator = new StandardTypeLocator();

171

typeLocator.registerImport("com.myapp.model");

172

typeLocator.registerImport("com.myapp.util");

173

context.setTypeLocator(typeLocator);

174

175

// Set custom type converter

176

context.setTypeConverter(new StandardTypeConverter());

177

178

// Set bean resolver (typically in Spring context)

179

context.setBeanResolver(new BeanFactoryResolver(applicationContext));

180

```

181

{ .api }

182

183

## SimpleEvaluationContext

184

185

### Class Definition and Builder

186

187

```java

188

public class SimpleEvaluationContext implements EvaluationContext {

189

// Static factory methods

190

public static Builder forReadOnlyDataBinding();

191

public static Builder forReadWriteDataBinding();

192

public static Builder forPropertyAccessors(PropertyAccessor... accessors);

193

194

// Builder class

195

public static final class Builder {

196

// Method access configuration

197

public Builder withInstanceMethods();

198

public Builder withStaticMethods();

199

200

// Collection access configuration

201

public Builder withArrayAccess();

202

public Builder withListAccess();

203

public Builder withMapAccess();

204

205

// Environment access

206

public Builder withEnvironmentAccess();

207

public Builder withSystemProperties();

208

public Builder withSystemEnvironment();

209

210

// Root object configuration

211

public Builder withTypedRootObject(Object rootObject);

212

public Builder withRootObject(Object rootObject);

213

214

// Type conversion

215

public Builder withConversionService(ConversionService conversionService);

216

public Builder withTypeConverter(TypeConverter typeConverter);

217

218

// Assignment control

219

public Builder withAssignmentDisabled();

220

221

// Build method

222

public SimpleEvaluationContext build();

223

}

224

}

225

```

226

{ .api }

227

228

### Read-Only Data Binding

229

230

```java

231

// Basic read-only context

232

SimpleEvaluationContext context = SimpleEvaluationContext

233

.forReadOnlyDataBinding()

234

.build();

235

236

// Read-only with method access

237

SimpleEvaluationContext context = SimpleEvaluationContext

238

.forReadOnlyDataBinding()

239

.withInstanceMethods()

240

.build();

241

242

// Read-only with collection access

243

SimpleEvaluationContext context = SimpleEvaluationContext

244

.forReadOnlyDataBinding()

245

.withArrayAccess()

246

.withListAccess()

247

.withMapAccess()

248

.build();

249

250

Person person = new Person("John", 30);

251

ExpressionParser parser = new SpelExpressionParser();

252

253

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

254

String name = exp.getValue(context, person, String.class); // "John"

255

256

exp = parser.parseExpression("name.length()"); // Requires withInstanceMethods()

257

Integer length = exp.getValue(context, person, Integer.class); // 4

258

```

259

{ .api }

260

261

### Read-Write Data Binding

262

263

```java

264

// Basic read-write context

265

SimpleEvaluationContext context = SimpleEvaluationContext

266

.forReadWriteDataBinding()

267

.build();

268

269

// Read-write with extended features

270

SimpleEvaluationContext context = SimpleEvaluationContext

271

.forReadWriteDataBinding()

272

.withInstanceMethods()

273

.withArrayAccess()

274

.withListAccess()

275

.withMapAccess()

276

.build();

277

278

Person person = new Person("John", 30);

279

ExpressionParser parser = new SpelExpressionParser();

280

281

// Reading

282

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

283

String name = exp.getValue(context, person, String.class); // "John"

284

285

// Writing

286

exp = parser.parseExpression("name");

287

exp.setValue(context, person, "Jane");

288

// person.name is now "Jane"

289

```

290

{ .api }

291

292

### Environment Access

293

294

```java

295

// Context with environment access

296

SimpleEvaluationContext context = SimpleEvaluationContext

297

.forReadOnlyDataBinding()

298

.withEnvironmentAccess()

299

.withSystemProperties()

300

.withSystemEnvironment()

301

.build();

302

303

ExpressionParser parser = new SpelExpressionParser();

304

305

// Access system properties

306

Expression exp = parser.parseExpression("systemProperties['java.version']");

307

String javaVersion = exp.getValue(context, String.class);

308

309

// Access environment variables

310

exp = parser.parseExpression("systemEnvironment['PATH']");

311

String path = exp.getValue(context, String.class);

312

313

// Access Spring environment (when available)

314

exp = parser.parseExpression("environment.getProperty('my.app.property')");

315

String property = exp.getValue(context, String.class);

316

```

317

{ .api }

318

319

### Custom Property Accessors

320

321

```java

322

// Create context with specific property accessors

323

PropertyAccessor customAccessor = new CustomPropertyAccessor();

324

PropertyAccessor mapAccessor = new MapAccessor();

325

326

SimpleEvaluationContext context = SimpleEvaluationContext

327

.forPropertyAccessors(customAccessor, mapAccessor)

328

.withInstanceMethods()

329

.build();

330

331

// Custom accessor implementation

332

public class CustomPropertyAccessor implements PropertyAccessor {

333

@Override

334

public Class<?>[] getSpecificTargetClasses() {

335

return new Class<?>[] { MyCustomClass.class };

336

}

337

338

@Override

339

public boolean canRead(EvaluationContext context, Object target, String name) {

340

return target instanceof MyCustomClass && "customProperty".equals(name);

341

}

342

343

@Override

344

public TypedValue read(EvaluationContext context, Object target, String name) {

345

if (target instanceof MyCustomClass && "customProperty".equals(name)) {

346

return new TypedValue(((MyCustomClass) target).getCustomValue());

347

}

348

throw new AccessException("Cannot read property: " + name);

349

}

350

351

@Override

352

public boolean canWrite(EvaluationContext context, Object target, String name) {

353

return false; // Read-only

354

}

355

356

@Override

357

public void write(EvaluationContext context, Object target, String name, Object newValue) {

358

throw new AccessException("Property is read-only: " + name);

359

}

360

}

361

```

362

{ .api }

363

364

### Type Conversion Configuration

365

366

```java

367

// Using ConversionService

368

ConversionService conversionService = new DefaultConversionService();

369

SimpleEvaluationContext context = SimpleEvaluationContext

370

.forReadOnlyDataBinding()

371

.withConversionService(conversionService)

372

.build();

373

374

// Using custom TypeConverter

375

TypeConverter customConverter = new CustomTypeConverter();

376

SimpleEvaluationContext context = SimpleEvaluationContext

377

.forReadOnlyDataBinding()

378

.withTypeConverter(customConverter)

379

.build();

380

381

// Custom type converter implementation

382

public class CustomTypeConverter implements TypeConverter {

383

private final ConversionService conversionService = new DefaultConversionService();

384

385

@Override

386

public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {

387

// Custom conversion logic

388

if (sourceType.getType() == String.class && targetType.getType() == MyCustomType.class) {

389

return true;

390

}

391

return conversionService.canConvert(sourceType, targetType);

392

}

393

394

@Override

395

public Object convertValue(Object value, TypeDescriptor sourceType, TypeDescriptor targetType) {

396

// Custom conversion implementation

397

if (sourceType.getType() == String.class && targetType.getType() == MyCustomType.class) {

398

return new MyCustomType((String) value);

399

}

400

return conversionService.convert(value, sourceType, targetType);

401

}

402

}

403

```

404

{ .api }

405

406

## Context Comparison

407

408

### StandardEvaluationContext vs SimpleEvaluationContext

409

410

| Feature | StandardEvaluationContext | SimpleEvaluationContext |

411

|---------|---------------------------|--------------------------|

412

| **Performance** | Slower due to reflection overhead | Faster, optimized for data binding |

413

| **Security** | Full SpEL features (potential security risks) | Restricted features (more secure) |

414

| **Flexibility** | Highly customizable | Limited but sufficient for most use cases |

415

| **Method Invocation** | All methods by default | Opt-in via builder |

416

| **Type References** | T(Class) syntax supported | Not supported |

417

| **Constructor Calls** | new Constructor() supported | Not supported |

418

| **Bean References** | @bean syntax supported | Not supported |

419

| **Assignment** | Supported by default | Opt-in via builder |

420

| **Thread Safety** | Not thread-safe | Thread-safe when immutable |

421

422

### Choosing the Right Context

423

424

```java

425

// Use SimpleEvaluationContext for:

426

// - Data binding scenarios

427

// - Security-sensitive environments

428

// - Performance-critical applications

429

// - Web applications with user input

430

431

SimpleEvaluationContext secureContext = SimpleEvaluationContext

432

.forReadOnlyDataBinding()

433

.withInstanceMethods()

434

.build();

435

436

// Use StandardEvaluationContext for:

437

// - Full SpEL feature requirements

438

// - Complex expression scenarios

439

// - Integration with Spring frameworks

440

// - Development and testing environments

441

442

StandardEvaluationContext fullContext = new StandardEvaluationContext();

443

```

444

{ .api }

445

446

## Advanced Context Usage

447

448

### Context Inheritance and Chaining

449

450

```java

451

// Base context with common configuration

452

StandardEvaluationContext baseContext = new StandardEvaluationContext();

453

baseContext.setVariable("appName", "MyApplication");

454

baseContext.setVariable("version", "1.0.0");

455

baseContext.registerFunction("formatDate",

456

DateUtils.class.getDeclaredMethod("format", Date.class, String.class));

457

458

// Specialized context inheriting from base

459

StandardEvaluationContext userContext = new StandardEvaluationContext();

460

userContext.setVariables(baseContext.lookupVariable("appName")); // Copy variables

461

userContext.setVariable("username", "john.doe");

462

userContext.setRootObject(currentUser);

463

464

// Context for specific operations

465

StandardEvaluationContext operationContext = new StandardEvaluationContext(userContext.getRootObject());

466

operationContext.setVariable("operation", "UPDATE");

467

operationContext.setVariable("timestamp", new Date());

468

```

469

{ .api }

470

471

### Dynamic Context Configuration

472

473

```java

474

public class DynamicContextFactory {

475

476

public EvaluationContext createContext(String profile, Object rootObject) {

477

switch (profile.toLowerCase()) {

478

case "secure":

479

return SimpleEvaluationContext

480

.forReadOnlyDataBinding()

481

.withRootObject(rootObject)

482

.build();

483

484

case "standard":

485

return SimpleEvaluationContext

486

.forReadWriteDataBinding()

487

.withInstanceMethods()

488

.withArrayAccess()

489

.withListAccess()

490

.withMapAccess()

491

.withRootObject(rootObject)

492

.build();

493

494

case "full":

495

StandardEvaluationContext context = new StandardEvaluationContext(rootObject);

496

context.addPropertyAccessor(new ReflectivePropertyAccessor());

497

context.addMethodResolver(new ReflectiveMethodResolver());

498

return context;

499

500

default:

501

throw new IllegalArgumentException("Unknown profile: " + profile);

502

}

503

}

504

}

505

```

506

{ .api }

507

508

### Context Pooling for Performance

509

510

```java

511

public class EvaluationContextPool {

512

private final Queue<StandardEvaluationContext> pool = new ConcurrentLinkedQueue<>();

513

private final int maxSize;

514

515

public EvaluationContextPool(int maxSize) {

516

this.maxSize = maxSize;

517

}

518

519

public StandardEvaluationContext borrowContext() {

520

StandardEvaluationContext context = pool.poll();

521

if (context == null) {

522

context = new StandardEvaluationContext();

523

configureContext(context);

524

}

525

return context;

526

}

527

528

public void returnContext(StandardEvaluationContext context) {

529

if (pool.size() < maxSize) {

530

clearContext(context);

531

pool.offer(context);

532

}

533

}

534

535

private void configureContext(StandardEvaluationContext context) {

536

// Standard configuration

537

context.addPropertyAccessor(new ReflectivePropertyAccessor());

538

context.addMethodResolver(new ReflectiveMethodResolver());

539

}

540

541

private void clearContext(StandardEvaluationContext context) {

542

// Clear variables and root object for reuse

543

context.setRootObject(null);

544

context.setVariable("temp", null); // Clear known temporary variables

545

}

546

}

547

```

548

{ .api }

549

550

## Best Practices

551

552

### Security Best Practices

553

554

```java

555

// 1. Use SimpleEvaluationContext for user-provided expressions

556

SimpleEvaluationContext secureContext = SimpleEvaluationContext

557

.forReadOnlyDataBinding()

558

.build(); // Minimal permissions

559

560

// 2. Validate expressions before evaluation

561

public boolean isSafeExpression(String expression) {

562

// Check for dangerous patterns

563

return !expression.contains("T(") && // No type references

564

!expression.contains("new ") && // No constructor calls

565

!expression.contains("@") && // No bean references

566

!expression.contains("#") && // No variables (if not needed)

567

expression.length() < 200; // Reasonable length limit

568

}

569

570

// 3. Use whitelisted property accessors

571

public class WhitelistPropertyAccessor implements PropertyAccessor {

572

private final Set<String> allowedProperties = Set.of("name", "id", "status");

573

574

@Override

575

public boolean canRead(EvaluationContext context, Object target, String name) {

576

return allowedProperties.contains(name) &&

577

target instanceof SafeDataObject;

578

}

579

580

// ... other methods

581

}

582

```

583

{ .api }

584

585

### Performance Best Practices

586

587

```java

588

// 1. Reuse contexts when possible (SimpleEvaluationContext)

589

public class ContextManager {

590

private final SimpleEvaluationContext sharedReadOnlyContext =

591

SimpleEvaluationContext.forReadOnlyDataBinding().build();

592

593

public Object evaluateReadOnly(Expression expression, Object root) {

594

return expression.getValue(sharedReadOnlyContext, root);

595

}

596

}

597

598

// 2. Cache context configurations

599

public class CachedContextFactory {

600

private final Map<String, EvaluationContext> contextCache = new ConcurrentHashMap<>();

601

602

public EvaluationContext getContext(String type) {

603

return contextCache.computeIfAbsent(type, this::createContext);

604

}

605

606

private EvaluationContext createContext(String type) {

607

// Create context based on type

608

return SimpleEvaluationContext.forReadOnlyDataBinding().build();

609

}

610

}

611

612

// 3. Minimize context setup overhead

613

public EvaluationContext createOptimizedContext(Object root) {

614

return SimpleEvaluationContext

615

.forReadOnlyDataBinding()

616

.withRootObject(root) // Set root directly

617

.build(); // No additional configuration

618

}

619

```

620

{ .api }

621

622

### Thread Safety Considerations

623

624

```java

625

// SimpleEvaluationContext is thread-safe when immutable

626

public class ThreadSafeExpressionEvaluator {

627

private final SimpleEvaluationContext sharedContext =

628

SimpleEvaluationContext.forReadOnlyDataBinding().build();

629

private final ExpressionParser parser = new SpelExpressionParser();

630

631

public Object evaluate(String expression, Object root) {

632

// Safe to share context across threads for read-only operations

633

Expression exp = parser.parseExpression(expression);

634

return exp.getValue(sharedContext, root);

635

}

636

}

637

638

// StandardEvaluationContext requires thread-local instances

639

public class ThreadLocalContextEvaluator {

640

private final ThreadLocal<StandardEvaluationContext> contextHolder =

641

ThreadLocal.withInitial(this::createContext);

642

643

private StandardEvaluationContext createContext() {

644

StandardEvaluationContext context = new StandardEvaluationContext();

645

// Configure context...

646

return context;

647

}

648

649

public Object evaluate(String expression, Object root) {

650

StandardEvaluationContext context = contextHolder.get();

651

context.setRootObject(root);

652

653

ExpressionParser parser = new SpelExpressionParser();

654

Expression exp = parser.parseExpression(expression);

655

return exp.getValue(context);

656

}

657

}

658

```

659

{ .api }