Pointcut matching methods with annotated parameters

后端 未结 2 519
醉酒成梦
醉酒成梦 2020-12-05 21:26

I need to create an aspect with a pointcut matching a method if:

  1. it is annoted with MyAnnotationForMethod
  2. One of its parameters (can have many) is ann
相关标签:
2条回答
  • 2020-12-05 21:41

    The ms.getParameterNames() call in the above solution doesnt seem to work when the method is implemented from an interface. I get back nulls.

    However, if I enable CGLIB, then it works.

    <aop:aspectj-autoproxy proxy-target-class="true"/>
    
    0 讨论(0)
  • 2020-12-05 21:43

    Updated:

    OK, the best reference I could find is on this page: Annotations, Pointcuts and Advice.

    You can match the method, however you won't be able to catch the parameter (just the method and the annotation). So what you will have to do is a combination of pointcut matching and reflection. Something like this:

    @Pointcut(
        "execution(@com.xxx.xxx.annotation.MyAnnotationForMethod * *(.., @com.xxx.xxx.annotation.MyAnnotationForParam (*), ..))")
    public void annotatedMethod(){}
    
    @Before("annotatedMethod()")
    public void doStuffOnParam(final JoinPoint jp){
        final Signature signature = jp.getSignature();
        if(signature instanceof MethodSignature){
            final MethodSignature ms = (MethodSignature) signature;
    
            final Method method = ms.getMethod();
            final String[] parameterNames = ms.getParameterNames();
            final Class<?>[] parameterTypes = ms.getParameterTypes();
            final Annotation[][] parameterAnnotations =
                method.getParameterAnnotations();
            for(int i = 0; i < parameterAnnotations.length; i++){
                final Annotation[] annotations = parameterAnnotations[i];
                final MyAnnotationForParam paramAnnotation =
                    getAnnotationByType(annotations, MyAnnotationForParam.class);
                if(paramAnnotation != null){
                    this.processParameter(ms.toShortString(),
                        parameterNames[i],
                        parameterTypes[i],
                        paramAnnotation);
                }
    
            }
        }
    }
    
    /**
     * In an array of annotations, find the annotation of the specified type, if any.
     * @return the annotation if available, or null
     */
    @SuppressWarnings("unchecked")
    private static <T extends Annotation> T getAnnotationByType(final Annotation[] annotations,
        final Class<T> clazz){
    
        T result = null;
        for(final Annotation annotation : annotations){
            if(clazz.isAssignableFrom(annotation.getClass())){
                result = (T) annotation;
                break;
            }
        }
        return result;
    }
    
    /**
     * Do some processing based on what we found.
     * @param signature method signature
     * @param paramName parameter name
     * @param paramType parameter type
     * @param paramAnnotation annotation we found
     */
    private void processParameter(final String signature,
        final String paramName,
        final Class<?> paramType,
        final MyAnnotationForParam paramAnnotation){
    
        System.out.println(MessageFormat.format(
            "Found parameter ''{0}'' \n  of type ''{1}'' \n  with annotation ''{2}'' \n  in method ''{3}''",
            paramName,
            paramType,
            paramAnnotation,
            signature));
    }
    

    Here is my test class for the above aspect:

    public class TestClass{
    
        @MyAnnotationForMethod
        public void simpleTestMethod(@MyAnnotationForParam final String param1){
            System.out.println("Method body (simple)");
        };
    
        @MyAnnotationForMethod
        public void complexTestMethod(final String param1,
            @MyAnnotationForParam final Float param2,
            @MyAnnotationForParam final Boolean param3){
            System.out.println("Method body (complex)");
        };
    
        public static void main(final String[] args){
            System.out.println("Starting up");
            final TestClass testObject = new TestClass();
            testObject.simpleTestMethod("Hey");
            testObject.complexTestMethod("Hey", 123.4f, false);
            System.out.println("Finished");
        }
    
    }
    

    and here is the output:

    Starting up
    Found parameter 'param1' 
      of type 'class java.lang.String' 
      with annotation '@com.xxx.xxx.annotation.MyAnnotationForParam()' 
      in method 'TestClass.simpleTestMethod(..)'
    Method body (simple)
    Found parameter 'param2' 
      of type 'class java.lang.Float' 
      with annotation '@com.xxx.xxx.annotation.MyAnnotationForParam()' 
      in method 'TestClass.complexTestMethod(..)'
    Found parameter 'param3' 
      of type 'class java.lang.Boolean' 
      with annotation '@com.xxx.xxx.annotation.MyAnnotationForParam()' 
      in method 'TestClass.complexTestMethod(..)'
    Method body (complex)
    Finished
    

    Hint

    You will probably want to cache a lot of this, there is no need to parse every parameter of every annotation in every execution. Keep a map of which parameter of which method carries the annotation and process only those parameters.

    0 讨论(0)
提交回复
热议问题