Методы сопоставления точек с аннотированными параметрами - PullRequest
7 голосов
/ 25 августа 2010

Мне нужно создать аспект с pointcut, соответствующим методу, если:

  1. помечено как MyAnnotationForMethod
  2. Один из его параметров (может иметь много) аннотируется @MyAnnotationForParam (но может иметь и другие аннотации).

Класс аспектов выглядит следующим образом

@Pointcut("execution(@MyAnnotationForMethod * *(..,@aspects.MyAnnotationForParam Object, ..)) && args(obj)")
void myPointcut(JoinPoint thisJoinPoint, Object obj) {
}

@Before("myPointcut(thisJoinPoint ,  obj)")
public void doStuffOnParam(JoinPoint thisJoinPoint, Object obj) {
    LOGGER.info("doStuffOnParam :"+obj);
}

Аннотированный метод

@MyAnnotationForMethod
public string theMethod(String a, @MyAnnotationForParam @OtherAnnotation Object obj, Object b){ 
    LOGGER.info(a+obj+b);
}

С затмением -> Предупреждения: По poincut:

Multiple markers at this line 
    - no match for this type name: MyAnnotationForMethod [Xlint:invalidAbsoluteTypeName] 
    - no match for this type name: aspects.MyAnnotationForParam On the before : advice defined in xxx.xxx.xxx.xxx.MyAspect has not been applied [Xlint:adviceDidNotMatch]

Используя последний плагин aspectJ из http://download.eclipse.org/tools/ajdt/35/update

С командной строкой maven с помощью aspectj 1.6.9

[WARNING] no match for this type name: MyAnnotationForMethod [Xlint:invalidAbsoluteTypeName]
[WARNING] no match for this type name: aspects.MyAnnotationForParam [Xlint:invalidAbsoluteTypeName]
[WARNING] advice defined in xxx.xxx.xxx.xxx.MyAspect has not been applied [Xlint:adviceDidNotMatch]

Аннотации:

package com.xxx.xxx.annotation;
// standard imports stripped
@Documented
@Target( { FIELD, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface @MyAnnotationForParam {}

и

package com.xxx.xxx.annotation;
// standard imports stripped
@Target(METHOD)
@Retention(RUNTIME)
@Documented
public @interface MyAnnotationForMethod {}

И, конечно, это не работает должным образом.

Можете ли вы сказать мне, что не так?

ТНХ.

Ответы [ 2 ]

11 голосов
/ 25 августа 2010

Обновлен:

ОК, лучшая ссылка, которую я мог найти, находится на этой странице: Аннотации, Pointcuts and Advice .

Вы можете сопоставить метод, однако вы не сможете поймать параметр (только метод и аннотацию). Так что вам нужно сделать сочетание сочетания точек и отражения. Примерно так:

@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));
}

Вот мой тестовый класс для вышеуказанного аспекта:

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");
    }

}

и вот вывод:

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

Подсказка

Возможно, вы захотите многое из этого кэшировать, нет необходимости анализировать каждый параметр каждой аннотации при каждом выполнении. Сохраните карту, для которой параметр какого метода несет аннотацию, и обработайте только эти параметры.

1 голос
/ 03 декабря 2010

Вызов ms.getParameterNames () в приведенном выше решении, похоже, не работает, когда метод реализован из интерфейса. Я возвращаю нули.

Однако, если я включу CGLIB, он будет работать.

<aop:aspectj-autoproxy proxy-target-class="true"/>
...