您现在的位置是:亿华云 > IT科技类资讯

从零搭建开发脚手架 Spring EL表达式的简介和实战应用

亿华云2025-10-08 23:20:37【IT科技类资讯】1人已围观

简介本文转载自微信公众号「Java大厂面试官」,作者laker。转载本文请联系Java大厂面试官公众号。 简介 算术运算符 关系运算符 逻辑运算符

本文转载自微信公众号「Java大厂面试官」,从零作者laker。搭建达式的简转载本文请联系Java大厂面试官公众号。脚手架S介和 简介 算术运算符 关系运算符 逻辑运算符 三目运算符 正则运算符 访问List和Map 以编程方式解析表达式 ExpressionParser EvaluationContext 高级应用 Bean引用 #this和#root 表达式模板 实战 1.注册常用的实战用户、Request、应用Response、从零工具类到上下文 2.访问Spring容器中的搭建达式的简任意Bean并调用其方法 3.自定义注解+获取方法入参

简介

Sping EL(Spring Expression Language 简称 SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象,脚手架S介和它可以与 XML 或基于注解的实战 Spring 配置一起使用。语言语法类似于统一 EL,应用但提供了额外的从零功能,方法调用和字符串模板功能。

虽然还有其他几种可用的搭建达式的简 Java 表达式语言,OGNL、脚手架S介和MVEL 和 JBoss EL等,实战但创建 Spring 表达式语言是应用为了向 Spring 社区提供一种受良好支持的表达式语言,SpEL基于与技术无关的 API,允许在需要时集成其他表达式语言实现。

SpEL支持以下运算符

类型操作符算术运算符 +, -, *, /, %, ^, div, mod 关系运算符 <, >, ==, !=, <=, >=, lt, gt, eq, ne, le, ge 逻辑运算符 and, or, not, &&, ||, ! 三目运算符 ?: 正则运算符 matches

以注解的方式举例如下:

SpEL表达式以#符号开头,并用大括号括起来:#{ expression}。可以以类似的高防服务器方式引用属性,以$符号开头,并用大括号括起来:${ property.name}。属性占位符不能包含 SpEL 表达式,但表达式可以包含属性引用.

#{ ${ someProperty} + 2} someProperty 的值为 2,计算结果为 4。 

算术运算符

支持所有基本算术运算符。

@Value("#{ 19 + 1}") // 20 private double add;  @Value("#{ String1  + string2}") // "String1 string2" private String addString;  @Value("#{ 20 - 1}") // 19 private double subtract; @Value("#{ 10 * 2}") // 20 private double multiply; @Value("#{ 36 / 2}") // 19 private double divide; @Value("#{ 36 div 2}") // 18, the same as for / operator private double divideAlphabetic;  @Value("#{ 37 % 10}") // 7 private double modulo; @Value("#{ 37 mod 10}") // 7, the same as for % operator private double moduloAlphabetic;  @Value("#{ 2 ^ 9}") // 512 private double powerOf; @Value("#{ (2 + 2) * 2 + 9}") // 17 private double brackets; 

关系运算符

@Value("#{ 1 == 1}") // true private boolean equal; @Value("#{ 1 eq 1}") // true private boolean equalAlphabetic; @Value("#{ 1 != 1}") // false private boolean notEqual; @Value("#{ 1 ne 1}") // false private boolean notEqualAlphabetic; @Value("#{ 1 < 1}") // false private boolean lessThan; @Value("#{ 1 lt 1}") // false private boolean lessThanAlphabetic; @Value("#{ 1 <= 1}") // true private boolean lessThanOrEqual; @Value("#{ 1 le 1}") // true private boolean lessThanOrEqualAlphabetic; @Value("#{ 1 > 1}") // false private boolean greaterThan; @Value("#{ 1 gt 1}") // false private boolean greaterThanAlphabetic; @Value("#{ 1 >= 1}") // true private boolean greaterThanOrEqual; @Value("#{ 1 ge 1}") // true private boolean greaterThanOrEqualAlphabetic; 

逻辑运算符

@Value("#{ 250 > 200 && 200 < 4000}") // true private boolean and;  @Value("#{ 250 > 200 and 200 < 4000}") // true private boolean andAlphabetic; @Value("#{ 400 > 300 || 150 < 100}") // true private boolean or; @Value("#{ 400 > 300 or 150 < 100}") // true private boolean orAlphabetic; @Value("#{ !true}") // false private boolean not; @Value("#{ not true}") // false private boolean notAlphabetic; 

三目运算符

@Value("#{ 2 > 1 ? a : b}") // "a" private String ternary; @Value("#{ someBean.someProperty != null ? someBean.someProperty : default}") private String ternary; 

正则运算符

@Value("#{ 100 matches \\d+ }") // true private boolean validNumericStringResult; @Value("#{ 100fghdjf matches \\d+ }") // false private boolean invalidNumericStringResult; @Value("#{ valid alphabetic string matches [a-zA-Z\\s]+ }") // true private boolean validAlphabeticStringResult; @Value("#{ invalid alphabetic string #$1 matches [a-zA-Z\\s]+ }") // false private boolean invalidAlphabeticStringResult; @Value("#{ someBean.someValue matches \d+}") // true if someValue contains only digits private boolean validNumericValue; 

访问List和Map

@Component("workersHolder") public class WorkersHolder {      private List<String> workers = new LinkedList<>();     private Map<String, Integer> salaryByWorkers = new HashMap<>();     public WorkersHolder() {          workers.add("John");         workers.add("Susie");         workers.add("Alex");         workers.add("George");         salaryByWorkers.put("John", 35000);         salaryByWorkers.put("Susie", 47000);         salaryByWorkers.put("Alex", 12000);         salaryByWorkers.put("George", 14000);     }     //Getters and setters } @Value("#{ workersHolder.salaryByWorkers[John]}") // 35000 private Integer johnSalary; @Value("#{ workersHolder.salaryByWorkers[George]}") // 14000 private Integer georgeSalary; @Value("#{ workersHolder.salaryByWorkers[Susie]}") // 47000 private Integer susieSalary; @Value("#{ workersHolder.workers[0]}") // John private String firstWorker; @Value("#{ workersHolder.workers[3]}") // George private String lastWorker; @Value("#{ workersHolder.workers.size()}") // 4 private Integer numberOfWorkers; 

以编程方式解析表达式

ExpressionParser

ExpressionParser负责解析表达式字符串,可以使用它来调用方法、访问属性或调用构造函数。 Expression expression = expressionParser.parseExpression("Any string.length()"); Integer result = (Integer) expression.getValue(); Expression expression = expressionParser.parseExpression("new String(Any string).length()"); Expression expression = expressionParser.parseExpression("Any string.replace(\" \", \"\").length()"); Integer result = expression.getValue(Integer.class); Car car = new Car(); car.setMake("Good manufacturer"); car.setModel("Model 3"); car.setYearOfProduction(2014); ExpressionParser expressionParser = new SpelExpressionParser(); Expression expression = expressionParser.parseExpression("model"); EvaluationContext context = new StandardEvaluationContext(car); String result = (String) expression.getValue(context); Expression expression = expressionParser.parseExpression("yearOfProduction > 2005"); boolean result = expression.getValue(car, Boolean.class); Expression expression = expressionParser.parseExpression("model"); String result = (String) expression.getValue(car);  使用ExpressionParser设置值 StandardEvaluationContext context = new StandardEvaluationContext(carPark); ExpressionParser expressionParser = new SpelExpressionParser(); expressionParser.parseExpression("cars[0].model").setValue(context, "Other model");  ExpressionParser解析器配置 SpelParserConfiguration config = new SpelParserConfiguration(true, true); StandardEvaluationContext context = new StandardEvaluationContext(carPark); ExpressionParser expressionParser = new SpelExpressionParser(config); expressionParser.parseExpression("cars[0]").setValue(context, car); Car result = carPark.getCars().get(0);  允许它在指定索引为空时自动创建元素(*autoGrowNullReferences,*构造函数的第一个参数) 自动增长数组或列表以容纳超出其初始大小的元素(autoGrowCollections,第二个参数)

EvaluationContext

当计算表达式解析properties, methods, fields,并帮助执行类型转换, 使用接口EvaluationContext 这是一个开箱即用的实现, StandardEvaluationContext,使用反射来操纵对象, 缓存java.lang.reflect的Method,Field,和Constructor实例 提高性能。

class Simple {      public List<Boolean> booleanList = new ArrayList<Boolean>(); } Simple simple = new Simple(); simple.booleanList.add(true); StandardEvaluationContext simpleContext = new StandardEvaluationContext(simple); // false is passed in here as a string. SpEL and the conversion service will // correctly recognize that it needs to be a Boolean and convert it parser.parseExpression("booleanList[0]").setValue(simpleContext, "false"); // b will be false Boolean b = simple.booleanList.get(0); 

高级应用

Bean引用

如果解析上下文已经配置,那么bean解析器能够 从表达式使用(@)符号查找bean类。

ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.setBeanResolver(new MyBeanResolver()); // This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation Object bean = parser.parseExpression("@foo").getValue(context); 

如果需要获取Bean工厂本身而不是它构造的Bean,可以使用&Bean名称。

Object bean = parser.parseExpression("&foo").getValue(context); 

#this和#root

#this和#root代表了表达式上下文的对象,#root就是网站模板当前的表达式上下文对象,#this则根据当前求值环境的不同而变化。下面的例子中,#this即每次循环的值。

// create an array of integers List<Integer> primes = new ArrayList<Integer>(); primes.addAll(Arrays.asList(2,3,5,7,11,13,17)); // create parser and set variable primes as the array of integers ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.setVariable("primes",primes); // all prime numbers > 10 from the list (using selection ?{ ...}) // evaluates to [11, 13, 17] List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression("#primes.?[#this>10]").getValue(context); 

表达式模板

表达式模板使用#{ }定义,它允许我们混合多种结果。下面就是一个例子,首先Spring会先对模板中的表达式求值,在这里是返回一个随机值,然后将结果和外部的表达式组合起来。最终的结果就向下面这样了。

String randomPhrase = parser.parseExpression(         "random number is #{ T(java.lang.Math).random()}",         new TemplateParserContext()).getValue(String.class); // 结果是 "random number is 0.7038186818312008" 

实战

以上都是官网的理论值,现总结下项目实战中常用的技巧。

1.注册常用的用户、Request、Response、工具类到上下文

注册常用的用户、Request、服务器托管Response、工具类到上下文,以便于在表达式中引用业务无关的对象。

ExpressionParser parser = new SpelExpressionParser();// 这个是线程安全的 定义为全局变量。   String expression = "#{ user.id + request.getQuringString()}"; Expression exp = parser.parseExpression(expression); EvaluationContext context = new StandardEvaluationContext(); context.setVariable("user", user); context.setVariable("request", request); context.setVariable("dateUtils", dateUtils); String value = (String) exp.getValue(context); 

2.访问Spring容器中的任意Bean并调用其方法

要访问 bean 对象,那么EvaluationContext中需要包含 bean 对象才行,可以借助BeanResolver来实现,如context.setBeanResolver(new BeanFactoryResolver(applicationContext)),访问 bean 的前缀修饰为@符号。

我们需要获取ApplicationContext,可以继承ApplicationContextAware,或者使用@Autowired获取。

StandardEvaluationContext context = new StandardEvaluationContext(); context.setBeanResolver(new BeanFactoryResolver(applicationContext));  // 获取bean对象 LakerService lakerService = parser.parseExpression("@lakerService").getValue(context, LakerService.class); System.out.println("lakerService : " + lakerService); // 访问bean方法 String result = parser.parseExpression("@lakerService.print(lakernote)").getValue(context, String.class); System.out.println("return result : " + result); 

3.自定义注解+获取方法入参

1.定义自定义注解

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Laker {      String value(); } 

2.针对自定义注解定义切面拦截

@Aspect @Component @Slf4j public class LakerAspect {      private SpelExpressionParser parserSpel = new SpelExpressionParser();     private DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();     @Pointcut("@annotation(com.laker.map.moudle.spel.Laker)")     private void elPoint() {      }     @Around("elPoint()")     public void cache(ProceedingJoinPoint pjp) {          Method method = ((MethodSignature) pjp.getSignature()).getMethod();         Laker laker = method.getAnnotation(Laker.class);         String value = getValue(laker.value(), pjp);         log.info(value);         try {              pjp.proceed();         } catch (Throwable e) {              log.error("", e);         }     }     public String getValue(String key, ProceedingJoinPoint pjp) {          Expression expression = parserSpel.parseExpression(key);         EvaluationContext context = new StandardEvaluationContext();         User user = new User();         user.id = 123L;         context.setVariable("user", user);// 模拟设置用户信息         MethodSignature methodSignature = (MethodSignature) pjp.getSignature();         Object[] args = pjp.getArgs();         String[] paramNames = parameterNameDiscoverer.getParameterNames(methodSignature.getMethod());         for (int i = 0; i < args.length; i++) {              context.setVariable(paramNames[i], args[i]);         }         return expression.getValue(context).toString();     }     class User {          public Long id;     } } 

3.在业务类上使用自定义注解

@Service public class LakerService {      @Laker("#user.id + #msg") //要符合SpEL表达式格式     public void print(String msg) {          System.out.println(msg);     } } 

参考:

https://docs.spring.io/spring-framework/docs/current/reference/html/ https://www.baeldung.com/spring-expression-language

很赞哦!(98975)