Master Dynamic Expression Engines in Spring Boot 3: 7 Real‑World Examples
This article introduces dynamic expression parsing for enterprise Java applications, compares seven Spring Boot‑compatible expression engines (SpEL, MVEL, Aviator, JEP, OGNL, JEXL, QLExpress) across performance, security and syntax, and provides complete code samples, execution results and pros‑cons for each engine.
1. Introduction
In enterprise‑level Java development, dynamic expression parsing has become essential for implementing flexible business logic such as rule engines, dynamic configuration, formula calculation and permission control. Spring Boot 3.4.2 supports multiple expression engines, each with its own performance characteristics, syntax complexity and suitable scenarios.
2. Practical Cases
2.1 SpEL (Spring Expression Language)
SpEL is tightly integrated with Spring, offering concise syntax for property access, method invocation, arithmetic operations and collection handling. It is widely used in configuration management and conditional injection.
@RestController
public class SpELController {
@GetMapping("/spel")
public String evaluate() {
ExpressionParser parser = new SpelExpressionParser();
// Basic arithmetic
Expression exp1 = parser.parseExpression("2 * 3 + 5");
int result1 = exp1.getValue(Integer.class);
// String operation
Expression exp2 = parser.parseExpression("'SPEL'.toLowerCase()");
String result2 = exp2.getValue(String.class);
// Collection filtering
Expression exp3 = parser.parseExpression("{1,2,3,4}.?[#this > 2]");
Object result3 = exp3.getValue();
return String.format("""
SpEL Result:<br/>
Basic: 2*3+5 = %d<br/>
String: 'SPEL'.toLowerCase() = %s<br/>
Filter: {1,2,3,4}.?[#this > 2] = %s<br/>
""", result1, result2, result3);
}
}Result:
Advantages
Flexible syntax that can query and manipulate object graphs.
Decouples business logic from implementation, improving maintainability.
Supports runtime evaluation, adapting to varying conditions.
Unified syntax reduces learning cost and offers rich features.
Disadvantages
Steeper learning curve for beginners.
Runtime evaluation may introduce performance overhead.
Potential security risks if malicious expressions are executed.
2.2 MVEL
MVEL is a lightweight Java‑based expression language with simple syntax, supporting arithmetic, logical and string operations. It is suitable for complex rule engines and dynamic configuration.
<dependency>
<groupId>org.mvel</groupId>
<artifactId>mvel2</artifactId>
<version>2.5.2.Final</version>
</dependency> @RestController
public class MVELController {
@GetMapping("/mvel")
public String evaluate() {
// Compile expression for better performance
Serializable exp1 = MVEL.compileExpression("a * b + c");
Map<String, Object> vars = new HashMap<>();
vars.put("a", 5);
vars.put("b", 3);
vars.put("c", 2);
int result1 = (int) MVEL.executeExpression(exp1, vars, Integer.class);
// Conditional logic
String exp2 = "if (x > y) { x * 2 } else { y * 3 }";
vars.put("x", 8);
vars.put("y", 10);
int result2 = (int) MVEL.eval(exp2, vars, Integer.class);
// Collection operation
List<Integer> list = Arrays.asList(1,2,3,4,5);
Map<String, Object> ctx = new HashMap<>();
ctx.put("list", list);
String exp = "sum = 0; for (n : list) { if (n % 2 == 0) sum += n }; sum";
Integer result3 = (Integer) MVEL.eval(exp, ctx);
return String.format("""
MVEL Result:<br/>
Basic: a*b+c = %d<br/>
Conditional: if (x>y) x*2 else y*3 = %d<br/>
Collection sum of evens = %d
""", result1, result2, result3);
}
}Result:
Advantages
Simple syntax, easy integration, relatively high execution efficiency.
Supports type inference, collection operations and some functional features.
Disadvantages
Incompatible with Java Stream API, causing usage friction.
Limited support for complex logic, debugging and error messages are weak.
2.3 Aviator
Aviator is a high‑performance, lightweight Java expression engine that compiles expressions to bytecode, making it suitable for financial and big‑data calculations.
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>5.4.3</version>
</dependency> @RestController
public class AviatorController {
@GetMapping("/aviator")
public String evaluate() {
// Basic arithmetic
Double result1 = (Double) AviatorEvaluator.execute("2 ** 10 + math.sqrt(100)");
// Variable usage
Map<String, Object> env = new HashMap<>();
env.put("name", "Aviator");
env.put("score", 95.5);
String result2 = (String) AviatorEvaluator.execute("name + ' score: ' + score", env);
// Large number calculation
String result3 = AviatorEvaluator.execute("999999999999999999 * 888888888888888888").toString();
// Custom function
AviatorEvaluator.addFunction(new CustomFunction());
Double result4 = (Double) AviatorEvaluator.execute("square_root(144)");
return String.format("""
Aviator Result:<br/>
Basic: 2^10 + sqrt(100) = %s<br/>
Variable: %s<br/>
Large number: %s<br/>
Custom function: square_root(144) = %s
""", result1, result2, result3, result4);
}
static class CustomFunction extends com.googlecode.aviator.runtime.function.AbstractFunction {
private static final long serialVersionUID = 1L;
@Override
public String getName() { return "square_root"; }
@Override
public com.googlecode.aviator.runtime.type.AviatorObject call(Map<String, Object> env, com.googlecode.aviator.runtime.type.AviatorObject arg1) {
Long num = (Long) arg1.getValue(env);
return new com.googlecode.aviator.runtime.type.AviatorDouble(Math.sqrt(num));
}
}
}Result:
Advantages
Very high performance by compiling to JVM bytecode.
Simple API, rich syntax, easy to use.
Strong extensibility – custom functions and operators are supported.
Provides runtime safety mechanisms.
Disadvantages
Limited syntax – no full language constructs such as if‑else, loops or assignment statements.
Insufficient date‑type support; dates must be expressed as formatted strings.
Only decimal and hexadecimal literals are allowed (no octal).
2.4 JEP (Java Expression Parser)
JEP focuses on mathematical expression evaluation, allowing custom variables, constants and functions.
<dependency>
<groupId>org.scijava</groupId>
<artifactId>jep</artifactId>
<version>2.4.2</version>
</dependency> @RestController
public class JEPController {
@GetMapping("/jep")
public String evaluate() {
JEP jep = new JEP();
jep.addStandardFunctions();
jep.addStandardConstants();
// Basic math
jep.parseExpression("sin(pi/2) + log(100)");
double result1 = jep.getValue();
// Multi‑variable
jep.addVariable("x", 3);
jep.addVariable("y", 4);
jep.parseExpression("x^2 + y^2");
double result2 = jep.getValue();
return String.format("""
JEP Result:<br/>
Trig & log: %.1f<br/>
Multi‑var: %.1f
""", result1, result2);
}
}Result:
2.5 OGNL
OGNL (Object‑Graph Navigation Language) provides powerful object graph navigation, method invocation and collection projection, commonly used in frameworks like Struts2.
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.4.7</version>
</dependency> @RestController
public class OGNLController {
public static class User {
public String name;
public Address address;
public User(String name, Address address) { this.name = name; this.address = address; }
}
public static class Address {
public String city;
public String street;
public Address(String city, String street) { this.city = city; this.street = street; }
}
@GetMapping("/ognl")
public String evaluate() throws OgnlException {
User user = new User("Tom", new Address("Beijing", "Chang'an Street"));
String city = (String) Ognl.getValue("address.city", user);
String upperName = (String) Ognl.getValue("name.toUpperCase()", user);
List<User> users = List.of(
new User("Alice", new Address("Shanghai", "Nanjing Road")),
new User("Bob", new Address("Guangzhou", "Zhongshan Road"))
);
OgnlContext ctx = (OgnlContext) Ognl.createDefaultContext(users);
ctx.put("users", users);
String expr = "#users.{name}";
Object names = Ognl.getValue(expr, ctx, ctx.getRoot());
return String.format("""
OGNL Result:<br/>
City: %s<br/>
Upper name: %s<br/>
Names: %s
""", city, upperName, names);
}
}Result:
Advantages
Concise and flexible syntax for complex object manipulation.
Supports method calls, static calls, collection operations and lambda‑style projections.
Integrates well with Spring and other frameworks.
Disadvantages
Parsing and execution consume resources, potentially affecting performance.
Security risks such as injection attacks.
Complex expressions can reduce code maintainability.
2.6 JEXL
JEXL implements a JSTL‑style expression language, allowing scripts similar to JavaScript/ECMAScript and is useful for letting end‑users write custom logic within controlled sandbox environments.
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jexl3</artifactId>
<version>3.5.0</version>
</dependency> @RestController
public class JEXLController {
@GetMapping("/jexl")
public String evaluate() {
JexlEngine jexl = new JexlBuilder().create();
// Simple expression
JexlExpression exp1 = jexl.createExpression("'Hello ' + name");
JexlContext ctx1 = new MapContext();
ctx1.set("name", "JEXL");
String result1 = exp1.evaluate(ctx1).toString();
// Conditional
JexlExpression exp2 = jexl.createExpression("age >= 18 ? 'Adult' : 'Minor'");
JexlContext ctx2 = new MapContext();
ctx2.set("age", 20);
String result2 = exp2.evaluate(ctx2).toString();
// Loop sum
JexlScript script = jexl.createScript("""
total = 0;
for (n : numbers) { total += n }
return total
""");
JexlContext ctx3 = new MapContext();
ctx3.set("numbers", new int[]{1,2,3,4,5});
Object result3 = script.execute(ctx3);
return String.format("""
JEXL Result:<br/>
Concatenation: %s<br/>
Conditional: %s<br/>
Sum: %s
""", result1, result2, result3);
}
}Result:
Advantages
Simple, intuitive syntax lowers the learning barrier.
Good Java integration via reflection.
Highly flexible – supports custom functions, variables and operators.
Provides a sandbox to restrict system resource access.
Cross‑platform.
Disadvantages
Lacks built‑in rule management features; external tools are needed.
Execution efficiency is lower compared with compiled engines.
2.7 QLExpress
QLExpress is a powerful dynamic script engine that can handle complex business rules. (Detailed description omitted for brevity; see the linked article for full coverage.)
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
