Методы сопоставления Pointcut с аннотированными параметрами

Мне нужно создать аспект с 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);
}

С eclipse -> предупреждения: На 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 с использованием аспекта 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 {}

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

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

Спасибо.


person Patrick Sauts    schedule 25.08.2010    source источник
comment
можете ли вы показать код двух аннотаций, включая их объявления пакетов.   -  person Bozho    schedule 25.08.2010
comment
есть ли в аннотациях сохранение = RUNTIME?   -  person Sean Patrick Floyd    schedule 25.08.2010
comment
связанные: stackoverflow.com/questions/2766844/   -  person Sean Patrick Floyd    schedule 25.08.2010


Ответы (2)


Обновлено:

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

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

@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

Намекать

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

person Sean Patrick Floyd    schedule 25.08.2010
comment
Шон, Любая идея, почему вам нужен полный путь к классам для вашей собственной определенной аннотации. Это то, чем я занимаюсь последние 2 часа. Спасибо - person Bill Comer; 29.06.2011
comment
@Bill проблема в том, что аспект обычно работает с скомпилированными классами, а не с источниками. И импорт не хранится в классах. Таким образом, на самом деле нет способа угадать полное имя по имени класса, найденному в аннотации pointcut. - person Sean Patrick Floyd; 08.07.2011

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

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

<aop:aspectj-autoproxy proxy-target-class="true"/>
person Darth Ninja    schedule 03.12.2010
comment
Имена параметров @Nittin доступны, только если вы компилируете свои классы. с флагом -g - person Sean Patrick Floyd; 08.07.2011
comment
@SeanPatrickFloyd на самом деле, используя прокси-классы целевых классов, также позволяет получать аннотации параметров. Пока я не добавил этот параметр, у меня всегда был пустой массив. - person uthark; 14.02.2012