aop获取方法注解的方式

本文介绍了在AOP中如何获取方法及其参数的注解,包括直接使用切点表达式、通过 ProceedingJoinPoint 和 MethodSignature,以及通过handlerMappings获取注解的方式。详细解析了每种方式的实现过程,并指出不同方式的适用场景。

应该背景:计划写一个公共方法,用以截取前端请求的参数正确性,解放后端开发频繁查询代码的压力。此处用的是around切面的方式。首先,获取注解的方式有一种是直接上切点,如下:

    //定义切点Pointcut
    @Pointcut("execution(* com.biierg.pg.controller.*Controller.*(..))")
    public void excudeService() {
    }
     
    @Around("excudeService()") 
    public Object around(ProceedingJoinPoint pjp) throws Throwable {

     .............

    }

另外一种方式如下:

     //定义切点Pointcut
    @Pointcut("execution(* com.biierg.pg.controller.*Controller.*(..)) && @annotation(io.swagger.annotations.ApiImplicitParams)")
    public void excudeService() {
    }
     
    @Around("excudeService() && @annotation(apiImplicitParams)") 
    public Object around(ProceedingJoinPoint pjp,ApiImplicitParams apiImplicitParams){

     .............

    }

@annotion 还可以多添加几个,但是缺点就是只能获取到方法上面的注解,无法得到方法参数的注解,我理解为:切点只切到方法,就不会往里走,所以拿不到方法参数的注解(纯属个人猜测~_~,如果不正确,欢迎下方留言)。

 

下面进去正题,下面只讨论怎么去到参数值,至于效率问题自己斟酌即可。

1.按照上面第二种方式,可以直接拿出传递的变量,取值即可。但如果是复合注解,还需要遍历一下,比如RequestMapping,可以如下取值:

         requestMapping.value();

         requestMapping.name()

    只可获取到方法上的注解,参数注解我还不没有得到。

2、第二种方式就是通过ProceedingJoinPoint pjp 获取,如下:

         Signature sig = pjp.getSignature();
         MethodSignature msig = null;
         if (!(sig instanceof MethodSignature)) {
             throw new IllegalArgumentException("该注解只能用于方法");
         }
         msig = (MethodSignature) sig;
         Object target = pjp.getTarget();
         //getDeclaredMethod() 包括自身所有方法,private\protected\public ;
         //getMethod() 包括从基类继承,接口实现的所有public方法
         
         Method currentMethod = target.getClass().getDeclaredMethod(msig.getName(), msig.getParameterTypes());

通过上面的方式获得当前方法currentMethod,然后在往下查找注解,下面代码中可以找到提取方法:

private Map getParamMap(Method method) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
        // TODO Auto-generated method stub
        Map<String,Param> mapParams= new HashMap<String,Param>();
        ApiImplicitParams aip=method.getAnnotation(ApiImplicitParams.class);
        InvocationHandler ih=Proxy.getInvocationHandler(aip);
        Field field=ih.getClass().getDeclaredField("memberValues");
        //private final 需要打开权限
        field.setAccessible(true);
        Map memberValues=(Map) field.get(ih);
        ApiImplicitParam[] value = (ApiImplicitParam[]) memberValues.get("value"); 
        for(ApiImplicitParam param:value) {
            Param pa=new Param();
            InvocationHandler ihp= Proxy.getInvocationHandler(param);
            Field fieldP=ihp.getClass().getDeclaredField("memberValues");
            fieldP.setAccessible(true);
            Map map=(Map)fieldP.get(ihp);
            String name=(String) map.get("name");
            String type=(String) map.get("dataType");
            boolean required= (boolean) map.get("required");

            pa.setName(name);
            pa.setType(type);
            pa.setRequired(required);
            mapParams.put(pa.getName(),pa); 
        }
        return mapParams;
    }

这是方法上的注解,方法的参数注解通过method.getParameterAnnotations()获得,然后如上代码遍历即可。但是需要注意的是此时获得的是一个二维数组,也就是有几个参数就有几个一维数组在该二维数组里面。如果某参数没有注解,此时一维数组为空。

3.通过handlerMappings获得注解

   既然mvc可以拿到handler,那么我们也可以参照DispatcherServlet去拿到类以及方法,然后再去取相应的注解。首先我们要取到requeste,

        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();

 第二步,通过request拿到ApplicationContext

         ServletContext sc = request.getServletContext();  
        ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);

第三步,构造handlerMappings

       Map<String, HandlerMapping> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
        } 

第四步,通过handlerMappings 取拿注解

          for (HandlerMapping mapping : this.handlerMappings) {
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    if(!HandlerMethod.class.isInstance(handler.getHandler())) {
                        continue;
                    }
                    HandlerMethod handlerMethod = (HandlerMethod) handler.getHandler(); 
                    for(MethodParameter param:handlerMethod.getMethodParameters()) {
                        Param parameter=new Param(); 
                        Annotation[] annotatedElement = param.getParameterAnnotations();
                        //查询注解中的required=true
                        for(Annotation annotation:annotatedElement) {
                            if(RequestParam.class.isInstance(annotation)) { 
                                RequestParam requestParam = (RequestParam) annotation;
                                //parameter.setRequired(requestParam.required()); 
                                if(requestParam.required()) {
                                    paramRequiredList.add(param.getParameterName());
                                }
                            }
                        }
                        map.put(parameter.getName(),parameter);
                    }  
                    break;
                }
            }

上面代码中进行类别判断,是因为handlerMappings中存在六个不通的HandlerMapping,其实是spring容器中对应的六种不同的bean,如下:

我这里需要的是第二个RequestMappingHandlerMapping,这里每个都是一个map,里面存储这url和handler的对应关系。

方法上的注解可以通过RequestMapping anno = handlerMethod.getMethodAnnotation(RequestMapping.class)获取  ,相应值如anno.value(),anno.name()可以直接取到,anno结构如下

目前知道的三种方式已经讲述完毕,各有优略,欢迎大家补充留言,谢谢阅读!

 

 

 

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值