美文网首页
spring aop 自定义注解annotation中spel表

spring aop 自定义注解annotation中spel表

作者: 乌木山 | 来源:发表于2019-11-06 19:47 被阅读0次

    关键词

    spring aopannotationspel

    在使用spring aop时,一种比较方便切入方式是基于注解的spring aop。使用者只需要在需要切面的地方加上自定义注解,即可实现切面的切入。
    有些时候,当我们使用注解时,需要将方法里的参数值传递到aop中。例如spring cache的使用:

       @Cacheable(value="users", key="#id")
       public User find(Integer id) {
          return null;
       }
    

    其中的key值就是使用spel表达式,获取到的方法入参值。
    那么,对于自定义的annotation,我们又如何使用spel呢?先直接上例子:

    示例

    1.注解定义

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface OperationLog {
     //operator支持spel表达式
      public String operator();
    }
    

    2.切面定义

    @Service
    @Aspect
    public class SystemLogAop {
    
        //Spring DefaultParameterNameDiscoverer
        private DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
    
        private ExpressionParser parser = new SpelExpressionParser();
    
    
        @Around(value = "@annotation(operationLog)")
        public Object secLogProcess(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws
                Throwable {
            //获取切面方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            //获取方法的形参名称
            String[] params = discoverer.getParameterNames(method);
            //获取方法的实际参数值
            Object[] arguments = joinPoint.getArgs();
    
            //设置解析spel所需的数据上下文
            EvaluationContext context = new StandardEvaluationContext();
            for (int len = 0; len < params.length; len++) {
                context.setVariable(params[len], arguments[len]);
            }
            //解析表达式并获取spel的值
            Expression expression = parser.parseExpression(operationLog.operator());
            Object theValue = expression.getValue(context);
            
            ...
        }
    }
    

    原理

    一般来说,我们在运行时,只能拿到参数的顺序和参数值(例如 arg0:Object0, arg1:Object1),而拿不到源码级别的参数名。但是在spring中,有大量基于参数名称的使用方式,那spring是怎么做到的呢?这就是DefaultParameterNameDiscoverer的作用了。
    DefaultParameterNameDiscoverer通过两种方式来获取类的方法参数名:

    • java8
      在Java 8及之后,编译的时候可以通过-parameters 为反射生成元信息,可以获取到方法的参数名,但这个行为默认是关闭的。
    • 其他方式
      对于无法通过jdk8的方式获取参数名的情况,spring会使用ASM工具解析class文件的debug信息,然后从中解析出类方法的参数名。
      具体流程为:

    TODO
    method_info->Code_attribute->LocalVariableTable_attribute->local_variable_table->name_index->CONSTANT_Utf8_info


    参考文章

    https://www.iteye.com/blog/huan1993-2395239

    相关文章

      网友评论

          本文标题:spring aop 自定义注解annotation中spel表

          本文链接:https://www.haomeiwen.com/subject/kkmxbctx.html