共计 5372 个字符,预计需要花费 14 分钟才能阅读完成。
前言
在介绍注释前,咱们先来讲下 spel
什么是 spel
Spring 表达式语言(简称“SpEL”)是一种功能强大的表达式语言,反对在运行时查问和操作对象图。
语言语法相似于 Unified EL,然而提供了其余性能,最驰名的是办法调用和根本的字符串模板性能。
此外它并不间接与 Spring 绑定,而是能够独立应用
spel 能够反对哪些性能
- 文字表达式
- 布尔运算符和关系运算符
- 罕用表达式
- 类表达式
- 拜访属性,数组,列表和映射
- 办法调用
- 关系运算符
- 调配
- 调用构造函数
- Bean 援用
- 数组结构
- 内联列表
- 内联 Map
- 三元运算符
- 变量
- 用户定义的性能
- 汇合投影
- 汇合抉择
- 模板表达式
上述的 spel 语法能够通过如下链接进行查阅
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-language-ref
spel 解析根本流程
形如下图
大体的步骤如下
- 创立解析器
- 解析表达式
- 结构上下文
- 求值
spel 外围接口介绍
1、org.springframework.expression.ExpressionParser
表达式解析器,其性能次要是将字符串表达式转换为 Expression 对象。反对解析模板以及规范表达式字符串
其默认实现为
org.springframework.expression.spel.standard.SpelExpressionParser
2、org.springframework.expression.EvaluationContext
spel 计算表达式值的“上下文”,这个 Context 对象能够蕴含多个对象,但只能有一个 root(根)对象。当表达式中蕴含变量时,spel 会依据 EvaluationContext 中的变量的值对表达式进行计算。能够应用 setRootObject 办法来设置根对象,应用 setVariable 办法来注册自定义变量,应用 registerFunction 来注册自定义函数。
其默认实现为
org.springframework.expression.spel.support.StandardEvaluationContext
3、org.springframework.expression.Expression
代表一个表达式,通过 getValue 办法依据上下文取得表达式值
其默认实现为
org.springframework.expression.spel.standard.SpelExpression
spel 官网文档
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions
注释
前边简要介绍一下 spel,下边咱们就通过一个小例子来演示下。
该小例子次要是通过 AOP+SPEL 来实现,例子场景是:当产品价格大于 10 时,放入本地缓存,并通过定时器打印出本地缓存的值
1、业务逻辑实现外围代码
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMockDao productMockDao;
@Override
@LocalCacheable(key = "#product.id",condition = "#product.price ge 10")
public Product save(Product product) {return productMockDao.save(product);
}
}
2、aop 切面编写
@Component
@Aspect
public class CacheAspect {@Around("@annotation(localCacheable)")
public Object around(ProceedingJoinPoint pjp, LocalCacheable localCacheable) throws Throwable{MethodSignature methodSignature = (MethodSignature)pjp.getSignature();
Method method = methodSignature.getMethod();
Object[] args = pjp.getArgs();
Object result = pjp.proceed();
String key = pjp.getTarget().getClass().getName() + "_" + method.getName() + "_" + args.length;
if(!StringUtils.isEmpty(localCacheable.key())){key = SpELParserUtils.parse(method,args,localCacheable.key(),String.class);
}
System.out.println("key:"+key);
if(!StringUtils.isEmpty(localCacheable.condition())){boolean condition = SpELParserUtils.parse(method,args,localCacheable.condition(),Boolean.class);
if(condition){LocalCache.INSTANCE.put(key,result);
}
}else{LocalCache.INSTANCE.put(key,result);
}
return result;
}
}
3、解析 spel 外围工具类
@Slf4j
public final class SpELParserUtils {
private static final String EXPRESSION_PREFIX = "#{";
private static final String EXPRESSION_SUFFIX = "}";
/**
* 表达式解析器
*/
private static ExpressionParser expressionParser = new SpelExpressionParser();
/**
* 参数名解析器,用于获取参数名
*/
private static DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
private SpELParserUtils(){}
/**
* 解析 spel 表达式
*
* @param method 办法
* @param args 参数值
* @param spelExpression 表达式
* @param clz 返回后果的类型
* @param defaultResult 默认后果
* @return 执行 spel 表达式后的后果
*/
public static <T> T parse(Method method, Object[] args, String spelExpression, Class<T> clz, T defaultResult) {String[] params = parameterNameDiscoverer.getParameterNames(method);
EvaluationContext context = new StandardEvaluationContext();
// 设置上下文变量
for (int i = 0; i < params.length; i++) {context.setVariable(params[i], args[i]);
}
T result = getResult(context,spelExpression,clz);
if(Objects.isNull(result)){return defaultResult;}
return result;
}
/**
* 解析 spel 表达式
*
* @param method 办法
* @param args 参数值
* @param spelExpression 表达式
* @param clz 返回后果的类型
* @return 执行 spel 表达式后的后果
*/
public static <T> T parse(Method method, Object[] args, String spelExpression, Class<T> clz) {String[] params = parameterNameDiscoverer.getParameterNames(method);
EvaluationContext context = new StandardEvaluationContext();
// 设置上下文变量
for (int i = 0; i < params.length; i++) {context.setVariable(params[i], args[i]);
}
return getResult(context,spelExpression,clz);
}
/**
* 解析 spel 表达式
*
* @param param 参数名
* @param paramValue 参数值
* @param spelExpression 表达式
* @param clz 返回后果的类型
* @return 执行 spel 表达式后的后果
*/
public static <T> T parse(String param, Object paramValue, String spelExpression, Class<T> clz) {EvaluationContext context = new StandardEvaluationContext();
// 设置上下文变量
context.setVariable(param, paramValue);
return getResult(context,spelExpression,clz);
}
/**
* 解析 spel 表达式
*
* @param param 参数名
* @param paramValue 参数值
* @param spelExpression 表达式
* @param clz 返回后果的类型
* @param defaultResult 默认后果
* @return 执行 spel 表达式后的后果
*/
public static <T> T parse(String param, Object paramValue,String spelExpression, Class<T> clz, T defaultResult) {EvaluationContext context = new StandardEvaluationContext();
// 设置上下文变量
context.setVariable(param, paramValue);
T result = getResult(context,spelExpression,clz);
if(Objects.isNull(result)){return defaultResult;}
return result;
}
/**
* 获取 spel 表达式后的后果
*
* @param context 解析器上下文接口
* @param spelExpression 表达式
* @param clz 返回后果的类型
* @return 执行 spel 表达式后的后果
*/
private static <T> T getResult(EvaluationContext context,String spelExpression, Class<T> clz){
try {
// 解析表达式
Expression expression = parseExpression(spelExpression);
// 获取表达式的值
return expression.getValue(context, clz);
} catch (Exception e) {log.error(e.getMessage(),e);
}
return null;
}
/**
* 解析表达式
* @param spelExpression spel 表达式
* @return
*/
private static Expression parseExpression(String spelExpression){// 如果表达式是一个 #{}表达式,须要为解析传入模板解析器上下文
if(spelExpression.startsWith(EXPRESSION_PREFIX) && spelExpression.endsWith(EXPRESSION_SUFFIX)){return expressionParser.parseExpression(spelExpression,new TemplateParserContext());
}
return expressionParser.parseExpression(spelExpression);
}
}
4、示例成果
总结
spel 在 spring 利用中随处可见,比方 @cacheable、@Value 等,咱们也能够通过 aop+spel 实现出适宜咱们业务场景的性能
demo 链接
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-aop-spel