Spring Expression Language
阅读数:159 评论数:0
跳转到新版页面分类
python/Java
正文
一、SPEL
SpEL(Spring Expression Language),即Spring表达式语言, 语法类似于 EL 表达式 ,但提供了显式方法调用和基本字符串模板函数等特性 ,能够在运行时构建复杂的表达式。
SpEL 虽然作为 Spring 家族中表达式求值的基础,但却可以被独立使用。
在单独使用 SpEL 时,需要创建一个 ExpressionParser 解析器,并提供一个 EvaluationContext 求值上下文 。 但在 Spring 框架中,仅需要配置一个 SpEL 表达式( Spring bean 定义)即可。SpEL 解析器、求值上下文、根对象和其他预定义变量等基础设施都会被隐含创建
二、EvaluationContext
EvaluationContext 接口定义了获取属性、方法、域字段解析器及类型转换器。
StandardEvaluationContext 使用的是反射机制来操作对象 。 为了提高性能,他会在其内部缓存 java.lang.reflect 的 Method、Field 和 Constructor 实例。
StandardEvaluationContext 类可以通过构造函数来传递求值根对象,也可以通过方法 setRootObject() 来指定求值根对象。 可以使用 setVariable()
来设置变量;使用 registerFunction(String name, Method method)
来注册自定义函数。
在StandardEvaluationContext中,我们还可以注册自定的构造函数转换器ContructorResolvers、方法转换器MethodResolvers及属性转换器PropertyAccessors来定制表达式的解析方式。
默认情况下,SpEL使用的是ConversionService来执行转换服务,它会根据源类型与目录类型来选择合适的转换器。Spring内置了很多转换器。
public class GenericConvertExample {
public List<Integer> nums = new ArrayList();
public static void main(String[] args) {
GenericConvertExample example = new GenericConvertExample();
example.nums.add(1);
//创建表达式上下文
StandardEvaluationContext context = new StandardEvaluationContext(example);
//创建表达式解析器
ExpressionParser parser = new SpelExpressionParser();
String expression = "nums[0]";
//自动将 2 转换为 Integer 类型
parser.parseExpression(expression).setValue(context, 2);
System.out.println("nums:" + example.nums);
//抛出 ConverterNotFoundException
try {
parser.parseExpression(expression).setValue(context, true);
} catch (EvaluationException e) {
e.printStackTrace();s
} catch (ParseException e) {
e.printStackTrace();
}
}
}
(1)在上下文中设置变量,然后在表达式中引用这些变量
context.setVariable("greeting", "Hello, World!");
(2)注册函数
如果你有一些自定义逻辑,你可以将方法注册为函数,然后在表达式中调用:
public class StringUtils {
public static String concatenate(String one, String two) {
return one + two;
}
}
// 注册函数
context.registerFunction("concat", StringUtils.class.getDeclaredMethod("concatenate", String.class, String.class));
// 然后在表达式中使用
String result = parser.parseExpression("#concat('Hello', ' World')").getValue(context, String.class);
(3)设置根对象
你可以设置一个根对象,表达式将默认使用这个根对象的属性和方法:
class Simple {
public String message = "Hello, World!";
}
Simple simple = new Simple();
context.setRootObject(simple);
(4)使用类型转换器
StandardEvaluationContext
可以配置自定义的类型转换器来处理表达式结果的类型转换:
ConversionService conversionService = new DefaultConversionService();
context.setTypeConverter(new StandardTypeConverter(conversionService));
(5)评估表达式
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("('Hello' + ' World').toUpperCase()");
String result = expression.getValue(context, String.class);
System.out.println(result); // 输出: HELLO WORLD
(6)使用属性访问器
如果你需要自定义属性的解析,你可以添加自定义的属性访问器:
context.addPropertyAccessor(new CustomPropertyAccessor());
(7)安全性
从 Spring 4.1 开始,StandardEvaluationContext
有一个用于配置安全性的方法,可以限制哪些类型可以被访问和操作:
context.setBeanResolver(new MySecureBeanResolver());
三、SpelCompiler
默认情况下, SpEL 表达式只有在求值时才会进行表达式计算,所以表达式可以在运行时进行动态修改。但如果一个表达式被重复调用的次数很多,那么就必须使用SpelCompiler 编译器来保证性能。
SpelCompiler 编译器会将表达式编译成字节码,只有在运行时表达式发生变化时,才会被重新编译。
//Spel 解析配置器
SpelParserConfiguration configuration=new SpelParserConfiguration
(SpelCompilerMode.IMMEDIATE,CompilerExample.class.getClassLoader());
//解析器
SpelExpressionParser parser=new SpelExpressionParser(configuration);
//上下文
EvaluationContext context=new StandardEvaluationContext(new Account("Deniro"));
//表达式
String expression="getName()";
//解析表达式
SpelExpression spelExpression=parser.parseRaw(expression);
System.out.println(spelExpression.getValue(context));
Spel解析配置器有三种编译模式
OFF | 不启用编译模式(默认) |
MIXED | 在混合模式下,随着时间的推移,表达式会从解释模式自动切换到编译模式。即前面使用解释模式,当调用次数达到某个阈值后,改为使用编译模式。 |
IMMEDIATE | 启用编译模式。实际内部在调用第一次之后,才会真正使用编译模式。 |
四、使用
1、依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
2、简单示例
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("6+2");
Integer result = (Integer) expression.getValue();
System.out.println("result:" + result);
3、获取某个类实例的属性值
可以在表达式中引用根对象的属性,在解析表达式时,会在内部使用反射会从根对象中获取属性的值。
public class Account {
private String name;
public Account(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
我们通过表达式来获取 Account 实例中的 name 值:
Account account=new Account("Deniro");
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context=new StandardEvaluationContext(account);
Expression expression = parser.parseExpression("name");
String result = expression.getValue(context,String.class);
System.out.println("result:" + result);