您现在的位置是:亿华云 > IT科技类资讯
从零搭建开发脚手架 Spring EL表达式的简介和实战应用
亿华云2025-10-08 23:20:37【IT科技类资讯】1人已围观
简介本文转载自微信公众号「Java大厂面试官」,作者laker。转载本文请联系Java大厂面试官公众号。 简介 算术运算符 关系运算符 逻辑运算符

简介
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)
热门文章
站长推荐
在数以亿计的网站中,我们应该抓住每一个可能带来宣传的机会,域名可以带有企业的名字,一般可以使用汉语拼音或者英语单词或者是相关缩写的形式,只要用户记住了你企业的名字,就能很容易的打出你的网站域名,同样的,记住了网站域名也能很快的记住你公司的名字。
想要在JS中把正则玩得飘逸,学会这几个函数的使用必不可少
效率宝典:7个超棒的React Hooks
JavaScript实现栈结构(Stack)
顶级域名可以增加企业品牌的价值。随着经济的快速发展,域名已不再是企业在网络中的独立地位。顶级域名的服务范围、企业产品、综合形象体现等,对于企业单位来说,顶级域名的重要性不言而喻。
领导给了一堆无序杂乱的数据,我写了个Python自动化脚本
软件架构师必备的八大工程技能
首位图灵奖女得主去世,她说编程与登山一样,充满挑战