Spring Aop中解析spel表达式,实现更灵活的功能
前言
在Spring Aop中,我们可以拿到拦截方法的参数,如果能结合spel表达式,就能实现更加灵活的功能。典型的实现有Spring的缓存注解:
1@Cacheable(value = "user", key = "#id", condition = "#id lt 10")
2public User conditionFindById(final Long id) {
3}
1@Caching(put = {
2@CachePut(value = "user", key = "#user.id"),
3@CachePut(value = "user", key = "#user.username"),
4@CachePut(value = "user", key = "#user.email")
5})
6public User save(User user) {
本文介绍如何在aop编程中解析spel表达式,提供几个通用的方法。
Spring使用自定义注解实现aop的方式这里就不赘述,只着重介绍如何解析spel。
准备
实现非常简单,Spring本身就提供了简便的api,我们只需要获取:
- 方法:
Method method - 方法参数:
Object[] arguments - spel表达式:
String spel
这些都能从aop入口方法的参数ProceedingJoinPoint中得到。
spel表达式显然就是从自定义注解中获取了,而获取方法和参数的方式如下:
获取方法:
1private Method getMethod(ProceedingJoinPoint joinPoint) {
2 MethodSignature signature = (MethodSignature) joinPoint.getSignature();
3 Method method = signature.getMethod();
4 if (method.getDeclaringClass().isInterface()) {
5 try {
6 method = joinPoint
7 .getTarget()
8 .getClass()
9 .getDeclaredMethod(joinPoint.getSignature().getName(),
10 method.getParameterTypes());
11 } catch (SecurityException | NoSuchMethodException e) {
12 throw new RuntimeException(e);
13 }
14 }
15 return method;
16 }
获取方法参数值:
1Object[] arguments = joinPoint.getArgs();
解析
然后就是解析spel表达式,首先在aop类中定义两个属性:
1private ExpressionParser parser = new SpelExpressionParser();
2
3private LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
根据spel表达式解析参数,得到结果:
1 /**
2 * 解析 spel 表达式
3 *
4 * @param method 方法
5 * @param arguments 参数
6 * @param spel 表达式
7 * @param clazz 返回结果的类型
8 * @param defaultResult 默认结果
9 * @return 执行spel表达式后的结果
10 */
11 private <T> T parseSpel(Method method, Object[] arguments, String spel, Class<T> clazz, T defaultResult) {
12 String[] params = discoverer.getParameterNames(method);
13 EvaluationContext context = new StandardEvaluationContext();
14 for (int len = 0; len < params.length; len++) {
15 context.setVariable(params[len], arguments[len]);
16 }
17 try {
18 Expression expression = parser.parseExpression(spel);
19 return expression.getValue(context, clazz);
20 } catch (Exception e) {
21 return defaultResult;
22 }
23 }
总结
上述就是整个解析spel表达式的关键流程,整体来看,aop类的结构是这样的:
1@Aspect
2public class SpelAspect {
3
4 private ExpressionParser parser = new SpelExpressionParser();
5 private LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
6
7 @Around(value = "@annotation(自定义注解)")
8 public Object test(ProceedingJoinPoint point) throws Throwable {
9 Object obj;
10 // 获取方法参数值
11 Object[] arguments = point.getArgs();
12 // 获取方法
13 Method method = getMethod(point);
14 // 从注解中获取spel字符串,省略...
15 String spel = ...
16 // 解析spel表达式
17 Boolean result = parseSpel(method, arguments, spel, Boolean.class, Boolean.FALSE);
18 // 业务操作,省略...
19 ...
20 return point.proceed();
21 }
22}
以上提供一个基本思路和几个通用的方法(#getMethod、#parseSpel),接下来就是大家发挥想象力的时间啦!
