做纸箱在什么网站找客户,广州办营业执照,wordpress 自己写的网页,免费注册淘宝店铺文章目录 简介Spring EL的常见应用Value处理注解xml中使用 Spring EL表达式基本表达式模板函数表达式 Spring EL定制引用Spring的bean 简介
SpringEL是Spring表达式#xff0c;功能非常强大。
我们可以能在Value、Cachable、自定义注解中的应用EL表达式#xff0c;当然这些… 文章目录 简介Spring EL的常见应用Value处理注解xml中使用 Spring EL表达式基本表达式模板函数表达式 Spring EL定制引用Spring的bean 简介
SpringEL是Spring表达式功能非常强大。
我们可以能在Value、Cachable、自定义注解中的应用EL表达式当然这些不是本文的重点。
本文的重点是如何基于SpringEL来定制业务表达式以便于简化我们工作减少自己去处理解析表达式的复杂逻辑。
一个简单的场景
例如业务希望文件名是动态的根据实际的年、月、日、季以及其他的业务数据来生成。
因为有非常多的文件类型业务希望通过表达式根据实际业务数据生成最终文件名。
如果仅仅是简单的年月日自己写个简单的表达式解析器问题也不大但是还有根据实际业务数据会有很多表达式这种倒不是说不能写但是复杂度就会比较高测试起来就非常复杂很难覆盖到所有的场景情况。
这时就可以基于SpringEL来定制开发。
Spring EL的常见应用
Value
//注入操作系统属性
Valule(#{systemProperties[os.name]})
private String os;
//注入表达式结果
Valule(#{T(java.lang.Math).random()*100})
private Double randomNumber;
//注入其他bean属性
Valule(#{myBean.name})
private String name;// 属性表达式在配置文件找app.count属性注入
Value(${app.count})
private Integer count;// EL表达式中包含$属性表达式
Value(#{T(Integer).parseInt(${config.num:10})})
private Integer num;这里要注意#和$
Spring中
${}是属性表达式引用的是properties和yaml配置文件中的属性值#{}是EL表达式默认的模板
${}属性表达式在应用启动时就会被解析在配置加载的时候进行替换 #{}EL表达式是运行时解析
${}属性表达式比#{}EL表达式先执行所以可以在EL表达式中包含${}属性表达式处理注解
最常见的就是缓存、分布式锁等注解可以根据方法的注解、结合实际调用的参数计算缓存的key和分布式锁的key。
通过支持SpringEL在设置的时候可以更加灵活。
Aspect
Component
public class KeyAspect {Around(annotation(disLock))public Object around(ProceedingJoinPoint joinPoint, DisLcok disLock) throws Throwable {EvaluationContext context new StandardEvaluationContext();MethodSignature signature (MethodSignature) joinPoint.getSignature();Object[] args joinPoint.getArgs();String[] parametersNames new DefaultParameterNameDiscoverer().getParameterNames(signature.getMethod());for (int i 0; i args.length; i) {context.setVariable(parametersNames[i], args[i]);}// 计算keyString lockKey new SpelExpressionParser().parseExpression(disLock.key()).getValue(context, String.class);// 处理缓存、锁等逻辑return joinPoint.proceed();}
}xml中使用
bean idmyBean classvip.meet.MyBeanproperty namename valueallen /property nameid value10 /
/beanbean idcustomerBean classvip.meet.CustomerBeanproperty nameitem value#{myBean} /property nameitemName value#{myBean.name} /
/beanSpring EL表达式
这里就不详解EL的各种用法了只是介绍一下注意事项和Spring EL能完成那些操作。
基本表达式
Test
public void basic() {ExpressionParser parser new SpelExpressionParser();// String操作System.out.println(parser.parseExpression(Hello World.concat(!)).getValue(String.class));// 运算符System.out.println(parser.parseExpression(102-3*4/2).getValue(Integer.class));// 访问静态变量 T表示类型,如果没有指定包,默认是java.lang下的类System.out.println(parser.parseExpression(T(Integer).MAX_VALUE).getValue(int.class));// 和上一个等价System.out.println(parser.parseExpression(T(java.lang.Integer).MAX_VALUE).getValue(int.class));// 访问静态方法System.out.println(parser.parseExpression(T(Integer).parseInt(1)).getValue(int.class));// 逻辑表达式System.out.println(parser.parseExpression(21 and (!true or !false)).getValue(boolean.class));
}模板
Test
public void templateWorld(){ExpressionParser parser new SpelExpressionParser();EvaluationContext context new StandardEvaluationContext();context.setVariable(bj, 北京);context.setVariable(cd, 成都);context.setVariable(sh, 上海);String template Hello #{#bj},你好 #{#cd},Hi #{#sh};Expression expression parser.parseExpression(template, new TemplateParserContext());System.out.println(expression.getValue(context, String.class));
}其中#{}是TemplateParserContext模板指定的表达式表达式中的#表示引用变量。
没有指定ParserContext就不能解析多个只能解析单个变量如
Test
public void templateDefault(){ExpressionParser parser new SpelExpressionParser();EvaluationContext context new StandardEvaluationContext();context.setVariable(bj, 北京);Expression expression parser.parseExpression(#bj);System.out.println(expression.getValue(context, String.class));
}函数表达式
EL中还有一个非常有用的就是可以引用函数。
public class SpringELFunctionTest {public static Integer add(Integer x, Integer y) {return x y;}Testpublic void functionTest() throws NoSuchMethodException {String exp #{ #add(4,5)};StandardEvaluationContext context new StandardEvaluationContext();Method add SpringELFunctionTest.class.getDeclaredMethod(add, Integer.class, Integer.class);context.registerFunction(add, add);ExpressionParser parser new SpelExpressionParser();Expression expression parser.parseExpression(exp, new TemplateParserContext());System.out.println(expression.getValue(context, Integer.class));}
}还可以带参数
Data
private static class Param{private Date birthday;private Integer id;private String name;
}private static class Fun{public String getParam(Param param){return hello param.toString();}
}Test
public void assignTest() {String exp #{#fun.getParam(#param)} 啊哈娘子;StandardEvaluationContext evaluationContext new StandardEvaluationContext();Fun fun new Fun();evaluationContext.setVariable(fun, fun);Param param new Param();param.setId(1);param.setBirthday(new Date());param.setName(tim);evaluationContext.setVariable(param, param);ExpressionParser parser new SpelExpressionParser();TemplateParserContext parserContext new TemplateParserContext();Expression expression parser.parseExpression(exp, parserContext);System.out.println(expression.getValue(evaluationContext, String.class));
}Spring EL定制
Spring因为${}是属性表达式所以EL的表达式默认是#{}
如果给业务用的表达式我们希望是${}该怎么做呢
例如
表达式
${name}-${year}年${month}月${day}日-${quarter}季报.xlsx计算之后得到类似阿宝基金-2024年$12月31日-4季报.xlsx
一看简单指定TemplateParserContext就可以那么下面的方式可以行吗
Test
public void templateDefault(){ExpressionParser parser new SpelExpressionParser();EvaluationContext context new StandardEvaluationContext();context.setVariable(name, 阿宝基金);context.setVariable(year, 2024);context.setVariable(month, 12);context.setVariable(day, 31);context.setVariable(quarter, 4);String template ${name}-${year}年${month}月${day}日-${quarter}季报.xlsx;TemplateParserContext parserContext new TemplateParserContext(${,});Expression expression parser.parseExpression(template, parserContext);System.out.println(expression.getValue(context, String.class));
}答案是不行表达式应该如下
String template ${#name}-${#year}年${#month}月${#day}日-${#quarter}季报.xlsx;上面的表达式看起来不够简化我就希望使用下面这个表达式怎么办
String template ${name}-${year}年${month}月${day}日-${quarter}季报.xlsx;可以利用StandardEvaluationContext的root对象。
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Map;Data
Builder
NoArgsConstructor
AllArgsConstructor
public class CustomParam {private Integer year;private Integer month;private Integer day;private Integer quarter;private String name;private MapString,Object paramMap;public static Integer staticMethod(){System.out.println(执行staticMethod);return 12;}public String instanceMethodParam(String... params){StringBuilder sb new StringBuilder();for(String p : params){sb.append(p).append();}return sb.toString();}
}Test
public void templateRoot(){ExpressionParser parser new SpelExpressionParser();HashMapString, Object map new HashMap();map.put(p1,pa);map.put(p2,pb);CustomParam param CustomParam.builder().name(阿宝基金).year(2024).month(12).day(31).quarter(4).paramMap(map).build();EvaluationContext context new StandardEvaluationContext(param);String template ${name}-${year}年${month}月${day}日-${quarter}季报.xlsx;TemplateParserContext parserContext new TemplateParserContext(${,});Expression expression parser.parseExpression(template, parserContext);// 阿宝基金-2024年12月31日-4季报.xlsxSystem.out.println(expression.getValue(context, String.class));template function-${staticMethod()}-${instanceMethodParam(Hello,Hi,World)}.xlsx;expression parser.parseExpression(template, parserContext);// function-12-HelloHiWorld.xlsxSystem.out.println(expression.getValue(context, String.class));template map-${paramMap[p1]}-${paramMap[p2]}.xlsx;expression parser.parseExpression(template, parserContext);// map-pa-pb.xlsxSystem.out.println(expression.getValue(context, String.class));
}重点在EvaluationContext context new StandardEvaluationContext(param);
StandardEvaluationContext的表达式没有#默认是找root的对象的对应属性
例如
${name}就等价于${#root.name}前面没有设置StandardEvaluationContext的rootroot为空所以:
${name}-${year}年${month}月${day}日-${quarter}季报.xlsx表达式自然有问题。设置了root对象能找到对应属性自然就没问题了。
我们可以看到不仅仅可以访问对应的属性可以访问对应的实例方法和静态方法。
设置map可以提供参数的灵活性。
引用Spring的bean
在Spring EL还可以引用Spring容器中的Bean通过Spring中所有的类和方法都可以引用非常灵活。
import lombok.Getter;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;Component
Getter
public class ApplicationContextHolder implements ApplicationContextAware {private ApplicationContext applicationContext;Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext applicationContext;}public T T getBean(ClassT clazz){return applicationContext.getBean(clazz);}
}import org.springframework.stereotype.Service;Service(valueelService)
public class ELService {public String service(){return el service;}
}import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import vip.meet.common.spring.ApplicationContextHolder;SpringBootTest
public class SpringELBeanTest {Resourceprivate ApplicationContextHolder applicationContextHolder;Testpublic void bean() {ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext();BeanFactoryResolver beanResolver new BeanFactoryResolver(applicationContextHolder.getApplicationContext());context.setBeanResolver(beanResolver);String result parser.parseExpression(elService.service()).getValue(context, String.class);System.out.println(result);}
}