Bid Farewell to If-Else: 5 Recommended Java Expression Engines

Foreword

When designing forms or workflow engines, we often encounter scenarios requiring expressions or rules to drive business logic. In this article, we review five commonly used Java expression engines that can replace cumbersome if-else blocks, making your code cleaner and more maintainable.

These expression engines are well-known and widely used. Let’s dive in for a quick refresher.


1. Spring Expression Language (SpEL)

Official Documentation: Spring Expression Language Documentation
Official Examples: Spring Framework GitHub

Spring Expression Language (SpEL) is a powerful expression language built into the Spring Framework for runtime object querying and manipulation. Key features include:

  • Dynamic Querying and Manipulation: Query bean properties, invoke methods, perform arithmetic, and logical operations.
  • Spring Integration: Used in Spring Security, Spring Data, and Spring Integration.
  • Flexible Syntax: Supports expressions within #{...}, e.g., #{bean.property} or #{list[0]}.
  • Context Awareness: Access Spring beans and application context within expressions.
  • Type Conversion: Built-in service for automatic or explicit type conversion.
  • Security: Configurable to prevent injection attacks.

Example Usage:

1
2
3
4
#{myBean.propertyName}
#{condition ? trueValue : falseValue}
#{myList[0]}
#{2 + 3}

Utility Class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SpringExpressionUtil {
private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

public static <T> T evaluateExpression(Map<String, Object> rootObject, String expression, Class<T> returnType) {
StandardEvaluationContext context = new StandardEvaluationContext(rootObject);
rootObject.forEach(context::setVariable);
return EXPRESSION_PARSER.parseExpression(expression).getValue(context, returnType);
}

public static void main(String[] args) {
Map<String, Object> map = Map.of("name", "John", "greeting", "Hello");
System.out.println(evaluateExpression(map, "#root.get('name')", String.class));
}
}

2. OGNL (Object-Graph Navigation Language)

Official Documentation: OGNL Language Guide
Official Examples: OGNL GitHub

OGNL is a powerful expression language for accessing and modifying Java object properties. It is widely used in frameworks like Apache Struts2.

Key Features:

  • Object Graph Navigation: Easily access nested properties (e.g., customer.address.street).
  • Collection Operations: Filter, project, and traverse collections (e.g., customers.{name}).
  • Method Calls: Supports calling methods and creating objects.
  • Dynamic Context: Variables and expressions adapt to their runtime context.

Utility Class:

1
2
3
4
5
6
7
8
9
10
11
public class OgnlExpressionUtil {
public static <T> T evaluateExpression(Map<String, Object> rootObject, String expression, Class<T> returnType) {
Object value = OgnlCache.getValue(expression, rootObject);
return returnType.isInstance(value) ? returnType.cast(value) : null;
}

public static void main(String[] args) {
Map<String, Object> map = Map.of("name", "John", "greeting", "Hello");
System.out.println(evaluateExpression(map, "#root.name", String.class));
}
}

3. Aviator

Official Documentation: Aviator Documentation
Official Examples: AviatorScript GitHub

Aviator is a lightweight and high-performance expression engine designed for scenarios requiring dynamic computations at runtime.

Key Features:

  • High Performance: Optimized for financial and real-time computing.
  • Rich Expression Support: Includes arithmetic, logical, and string operations.
  • JIT Compilation: Converts expressions to Java bytecode for faster execution.
  • Extensibility: Define custom functions for specialized logic.
  • Sandbox Mode: Restrict execution permissions for security.

Utility Class:

1
2
3
4
5
6
7
8
9
10
11
public final class AviatorExpressionUtil {
public static <T> T evaluateExpression(Map<String, Object> env, String expression, Class<T> returnType) {
Object value = AviatorEvaluator.execute(expression, env);
return returnType.isInstance(value) ? returnType.cast(value) : null;
}

public static void main(String[] args) {
Map<String, Object> env = Map.of("name", "John", "greeting", "Hello");
System.out.println(evaluateExpression(env, "name", String.class));
}
}

4. MVEL (MVFLEX Expression Language)

Official Documentation: MVEL Documentation
Official Examples: MVEL GitHub

MVEL2 offers a powerful and flexible way to parse and execute Java expressions. It is particularly effective for scenarios requiring lightweight scripting or embedded template generation.

Key Features:

  • Simple Syntax: Concise Java-like syntax for expressions and logic.
  • Dynamic and Static Typing: Combines flexibility and safety.
  • Template Engine: Supports generating text output via templates.
  • Efficient Execution: Optimized execution engine for performance-sensitive tasks.

5. Hutool Expression Engine Facade

Official Documentation: Hutool ExpressionUtil

Hutool provides a unified API for multiple expression engines, including Aviator, Apache Jexl3, MVEL, JfireEL, Rhino, and SpEL. It also supports custom engines via SPI.

Utility Class:

1
2
3
4
5
6
7
8
9
10
11
public class HutoolExpressionUtil {
public static <T> T evaluateExpression(Map<String, Object> variables, String expression, Class<T> returnType) {
Object value = ExpressionUtil.eval(expression, variables);
return returnType.isInstance(value) ? returnType.cast(value) : null;
}

public static void main(String[] args) {
Map<String, Object> vars = Map.of("name", "John", "greeting", "Hello");
System.out.println(evaluateExpression(vars, "name", String.class));
}
}

Conclusion

These five Java expression engines offer robust alternatives to traditional if-else logic. Depending on your use case, such as performance sensitivity, ease of integration, or dynamic capabilities, you can choose the most suitable engine for your needs. Each engine simplifies complex logic, improves maintainability, and enhances readability.