Spring Expression Language (SpEL) provides a powerful expression language for querying and manipulating object graphs at runtime.
—
This document covers SpEL's configuration capabilities, including parser configuration, compiler settings, and performance tuning options.
public class SpelParserConfiguration {
// Constants
public static final int DEFAULT_MAX_EXPRESSION_LENGTH = 10000;
public static final String SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME =
"spring.expression.compiler.mode";
// Constructors
public SpelParserConfiguration();
public SpelParserConfiguration(SpelCompilerMode compilerMode, ClassLoader compilerClassLoader);
public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections);
public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections,
int maximumAutoGrowSize);
public SpelParserConfiguration(SpelCompilerMode compilerMode, ClassLoader compilerClassLoader,
boolean autoGrowNullReferences, boolean autoGrowCollections,
int maximumAutoGrowSize, int maximumExpressionLength);
// Accessors
public SpelCompilerMode getCompilerMode();
public ClassLoader getCompilerClassLoader();
public boolean isAutoGrowNullReferences();
public boolean isAutoGrowCollections();
public int getMaximumAutoGrowSize();
public int getMaximumExpressionLength();
}{ .api }
// Default configuration
SpelParserConfiguration defaultConfig = new SpelParserConfiguration();
// Configuration with compilation enabled
SpelParserConfiguration compiledConfig = new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE,
Thread.currentThread().getContextClassLoader()
);
// Configuration with auto-grow features
SpelParserConfiguration autoGrowConfig = new SpelParserConfiguration(
true, // autoGrowNullReferences
true, // autoGrowCollections
100 // maximumAutoGrowSize
);
// Complete configuration
SpelParserConfiguration fullConfig = new SpelParserConfiguration(
SpelCompilerMode.MIXED, // compilerMode
Thread.currentThread().getContextClassLoader(), // compilerClassLoader
true, // autoGrowNullReferences
true, // autoGrowCollections
1000, // maximumAutoGrowSize
50000 // maximumExpressionLength
);
// Use configuration with parser
SpelExpressionParser parser = new SpelExpressionParser(fullConfig);{ .api }
public enum SpelCompilerMode {
OFF, // No compilation, interpreted mode only (default)
IMMEDIATE, // Compile immediately after first interpretation
MIXED // Switch between interpreted and compiled modes as needed
}{ .api }
SpelParserConfiguration config = new SpelParserConfiguration(
SpelCompilerMode.OFF,
null
);
SpelExpressionParser parser = new SpelExpressionParser(config);
// All expressions will be interpreted, no compilation
Expression exp = parser.parseExpression("name.toUpperCase() + ' ' + age");
// Always uses interpretation, slower but more flexible{ .api }
SpelParserConfiguration config = new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE,
Thread.currentThread().getContextClassLoader()
);
SpelExpressionParser parser = new SpelExpressionParser(config);
SpelExpression exp = parser.parseRaw("name.toUpperCase() + ' ' + age");
// First evaluation triggers compilation
String result1 = exp.getValue(person, String.class); // Interpreted + compiled
// Subsequent evaluations use compiled code
String result2 = exp.getValue(person, String.class); // Compiled (much faster){ .api }
SpelParserConfiguration config = new SpelParserConfiguration(
SpelCompilerMode.MIXED,
Thread.currentThread().getContextClassLoader()
);
SpelExpressionParser parser = new SpelExpressionParser(config);
// Expressions can switch between interpreted and compiled modes
// based on success/failure of compilation and execution patterns
SpelExpression exp = parser.parseRaw("complexExpression()");{ .api }
// Set compiler mode via system property
System.setProperty("spring.expression.compiler.mode", "IMMEDIATE");
// Parser will use system property if no explicit configuration provided
SpelExpressionParser parser = new SpelExpressionParser();
// Equivalent to:
// SpelExpressionParser parser = new SpelExpressionParser(
// new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, classLoader)
// );{ .api }
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expression = parser.parseRaw("name.length() > 5 ? name.toUpperCase() : name");
// Check if expression can be compiled
boolean compilable = expression.compileExpression();
if (compilable) {
// Expression was successfully compiled
String result = expression.getValue(person, String.class); // Uses compiled code
} else {
// Compilation failed, will use interpreted mode
String result = expression.getValue(person, String.class); // Uses interpretation
}
// Revert to interpreted mode
expression.revertToInterpreted();
String result = expression.getValue(person, String.class); // Back to interpretation{ .api }
public class Container {
private NestedObject nested;
// getter and setter
}
public class NestedObject {
private String value;
// getter and setter
}
// Enable null reference auto-growth
SpelParserConfiguration config = new SpelParserConfiguration(
true, // autoGrowNullReferences
false // autoGrowCollections
);
SpelExpressionParser parser = new SpelExpressionParser(config);
Container container = new Container(); // nested is null
StandardEvaluationContext context = new StandardEvaluationContext(container);
// Without auto-growth, this would throw NullPointerException
// With auto-growth, it creates the nested object automatically
Expression exp = parser.parseExpression("nested.value");
exp.setValue(context, "Hello World");
// nested object was created automatically
NestedObject created = container.getNested(); // not null
String value = created.getValue(); // "Hello World"{ .api }
public class ListContainer {
private List<String> items = new ArrayList<>();
// getter and setter
}
// Enable collection auto-growth
SpelParserConfiguration config = new SpelParserConfiguration(
false, // autoGrowNullReferences
true, // autoGrowCollections
10 // maximumAutoGrowSize
);
SpelExpressionParser parser = new SpelExpressionParser(config);
ListContainer container = new ListContainer(); // empty list
StandardEvaluationContext context = new StandardEvaluationContext(container);
// Set value at index 5 in empty list
Expression exp = parser.parseExpression("items[5]");
exp.setValue(context, "Hello");
// List automatically grows to accommodate index 5
List<String> items = container.getItems();
// items.size() == 6, items.get(5) == "Hello", others are null{ .api }
public class ComplexContainer {
private List<NestedObject> objects;
// getter and setter
}
// Enable both auto-growth features
SpelParserConfiguration config = new SpelParserConfiguration(
true, // autoGrowNullReferences
true, // autoGrowCollections
50 // maximumAutoGrowSize
);
SpelExpressionParser parser = new SpelExpressionParser(config);
ComplexContainer container = new ComplexContainer(); // objects is null
StandardEvaluationContext context = new StandardEvaluationContext(container);
// This will:
// 1. Create the objects list (null reference auto-growth)
// 2. Grow the list to accommodate index 3 (collection auto-growth)
// 3. Create NestedObject at index 3 (null reference auto-growth)
Expression exp = parser.parseExpression("objects[3].value");
exp.setValue(context, "Deep Value");
NestedObject obj = container.getObjects().get(3);
String value = obj.getValue(); // "Deep Value"{ .api }
// Set custom maximum expression length
SpelParserConfiguration config = new SpelParserConfiguration(
SpelCompilerMode.OFF,
null,
false, // autoGrowNullReferences
false, // autoGrowCollections
0, // maximumAutoGrowSize
5000 // maximumExpressionLength (default is 10,000)
);
SpelExpressionParser parser = new SpelExpressionParser(config);
// Expressions longer than 5000 characters will be rejected
String longExpression = "very".repeat(2000) + "long expression";
try {
Expression exp = parser.parseExpression(longExpression);
} catch (ParseException e) {
// Expression too long
}{ .api }
public class SpelConfigurationFactory {
public static SpelParserConfiguration createProductionConfig() {
return new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE, // Aggressive compilation for performance
Thread.currentThread().getContextClassLoader(),
false, // Disable auto-growth for predictable behavior
false,
0,
10000 // Standard expression length limit
);
}
public static SpelParserConfiguration createDevelopmentConfig() {
return new SpelParserConfiguration(
SpelCompilerMode.OFF, // No compilation for easier debugging
null,
true, // Enable auto-growth for convenience
true,
100,
50000 // Larger expression limit for experimentation
);
}
public static SpelParserConfiguration createTestConfig() {
return new SpelParserConfiguration(
SpelCompilerMode.MIXED, // Mixed mode for testing both paths
Thread.currentThread().getContextClassLoader(),
true, // Auto-growth for test data setup
true,
10,
10000
);
}
}
// Usage
String environment = System.getProperty("environment", "development");
SpelParserConfiguration config = switch (environment) {
case "production" -> SpelConfigurationFactory.createProductionConfig();
case "test" -> SpelConfigurationFactory.createTestConfig();
default -> SpelConfigurationFactory.createDevelopmentConfig();
};
SpelExpressionParser parser = new SpelExpressionParser(config);{ .api }
// Custom class loader for isolation
ClassLoader customClassLoader = new URLClassLoader(
new URL[]{new URL("file:///path/to/custom/classes/")},
Thread.currentThread().getContextClassLoader()
);
SpelParserConfiguration config = new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE,
customClassLoader // Use custom class loader for compilation
);
SpelExpressionParser parser = new SpelExpressionParser(config);
// Compiled expressions will use the custom class loader
Expression exp = parser.parseExpression("T(com.custom.MyClass).staticMethod()");{ .api }
// High-performance configuration for production systems
SpelParserConfiguration performanceConfig = new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE, // Immediate compilation
Thread.currentThread().getContextClassLoader(), // Appropriate class loader
false, // Disable auto-growth (predictable)
false, // Disable auto-growth
0, // No auto-growth
10000 // Standard limit
);
// Use with cached expressions
Map<String, Expression> expressionCache = new ConcurrentHashMap<>();
SpelExpressionParser parser = new SpelExpressionParser(performanceConfig);
public Expression getCachedExpression(String expressionString) {
return expressionCache.computeIfAbsent(expressionString, parser::parseExpression);
}{ .api }
// Security-focused configuration
SpelParserConfiguration secureConfig = new SpelParserConfiguration(
SpelCompilerMode.OFF, // No compilation to avoid runtime class generation
null,
false, // No auto-growth to prevent unexpected object creation
false,
0,
1000 // Strict expression length limit
);
// Combine with SimpleEvaluationContext for additional security
SpelExpressionParser parser = new SpelExpressionParser(secureConfig);
SimpleEvaluationContext context = SimpleEvaluationContext
.forReadOnlyDataBinding()
.build();{ .api }
// Development configuration with helpful features
SpelParserConfiguration devConfig = new SpelParserConfiguration(
SpelCompilerMode.MIXED, // Test both compilation modes
Thread.currentThread().getContextClassLoader(),
true, // Auto-growth for easier prototyping
true,
1000, // Generous auto-growth limit
100000 // Large expression limit for experimentation
);
SpelExpressionParser parser = new SpelExpressionParser(devConfig);
// Useful for development: detailed error reporting
public Object safeEvaluate(String expressionString, Object root) {
try {
Expression exp = parser.parseExpression(expressionString);
return exp.getValue(root);
} catch (ParseException e) {
System.err.println("Parse error in expression: " + expressionString);
System.err.println("Error: " + e.toDetailedString());
return null;
} catch (EvaluationException e) {
System.err.println("Evaluation error in expression: " + expressionString);
System.err.println("Error: " + e.toDetailedString());
return null;
}
}{ .api }
public class ConfigurationValidator {
public static void validateConfiguration(SpelParserConfiguration config) {
SpelCompilerMode mode = config.getCompilerMode();
ClassLoader classLoader = config.getCompilerClassLoader();
if ((mode == SpelCompilerMode.IMMEDIATE || mode == SpelCompilerMode.MIXED)
&& classLoader == null) {
throw new IllegalArgumentException(
"ClassLoader must be provided for compilation modes");
}
if (config.getMaximumAutoGrowSize() < 0) {
throw new IllegalArgumentException(
"Maximum auto-grow size must be non-negative");
}
if (config.getMaximumExpressionLength() <= 0) {
throw new IllegalArgumentException(
"Maximum expression length must be positive");
}
}
public static SpelExpressionParser createValidatedParser(SpelParserConfiguration config) {
validateConfiguration(config);
return new SpelExpressionParser(config);
}
}{ .api }
// Legacy configuration (pre-6.0)
SpelParserConfiguration legacyConfig = new SpelParserConfiguration(
true, // autoGrowNullReferences
true // autoGrowCollections
);
// Modern equivalent with explicit settings
SpelParserConfiguration modernConfig = new SpelParserConfiguration(
SpelCompilerMode.OFF, // Explicit compiler mode
Thread.currentThread().getContextClassLoader(), // Explicit class loader
true, // autoGrowNullReferences
true, // autoGrowCollections
Integer.MAX_VALUE, // maximumAutoGrowSize
SpelParserConfiguration.DEFAULT_MAX_EXPRESSION_LENGTH
);{ .api }
public class CompatibilityChecker {
public static boolean isCompilationSupported() {
try {
SpelParserConfiguration config = new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE,
Thread.currentThread().getContextClassLoader()
);
SpelExpressionParser parser = new SpelExpressionParser(config);
SpelExpression exp = parser.parseRaw("'test'");
return exp.compileExpression();
} catch (Exception e) {
return false;
}
}
public static SpelParserConfiguration createCompatibleConfig() {
if (isCompilationSupported()) {
return new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE,
Thread.currentThread().getContextClassLoader()
);
} else {
return new SpelParserConfiguration();
}
}
}{ .api }
Install with Tessl CLI
npx tessl i tessl/maven-org-springframework--spring-expression