@AspectJ pointcut для всех методов класса с определенной аннотацией

Я хочу отслеживать все общедоступные методы всех классов с указанной аннотацией (например, @Monitor) (примечание: аннотация находится на уровне класса). Что может быть для этого возможным? Примечание. Я использую Spring AOP в стиле @AspectJ.


person Rejeev Divakaran    schedule 06.01.2010    source источник
comment
Нижеследующий работает в большей степени. @Pointcut (execution (* (@ org.rejeev.Monitor *). * (..))) Однако теперь совет выполняется дважды. Есть подсказка?   -  person Rejeev Divakaran    schedule 06.01.2010
comment
Другой момент заключается в том, что аннотация @Monitor находится на интерфейсе, и класс реализует это. Приведет ли наличие интерфейса и класса к двойному выполнению такого совета?   -  person Rejeev Divakaran    schedule 06.01.2010
comment
Вы должны принять отличный ответ, приведенный ниже. Это дает ему репутацию. На SO очень мало людей, которые могут ответить на вопросы AspectJ.   -  person fool4jesus    schedule 12.11.2013


Ответы (10)


Вы должны комбинировать тип pointcut с методом pointcut.

Эти pointcuts выполнят работу по поиску всех общедоступных методов внутри класса, отмеченного аннотацией @Monitor:

@Pointcut("within(@org.rejeev.Monitor *)")
public void beanAnnotatedWithMonitor() {}

@Pointcut("execution(public * *(..))")
public void publicMethod() {}

@Pointcut("publicMethod() && beanAnnotatedWithMonitor()")
public void publicMethodInsideAClassMarkedWithAtMonitor() {}

Посоветуйте последний pointcut, который сочетает в себе первые два, и готово!

Если вам интересно, я написал шпаргалку со стилем @AspectJ здесь с соответствующим пример документа здесь.

person Espen    schedule 26.03.2010
comment
Спасибо. Обсуждение точек аннотаций в шпаргалке особенно полезно. - person GregHNZ; 08.02.2013
comment
Как мне получить ссылку на класс в совете, как я делаю с обычными подсказками pointcut @Before (onObjectAction () && this (obj)) - person Priyadarshi Kunal; 29.07.2013
comment
Шпаргалка очень помогла, хотя прошло уже 5 лет :) - person Yadu Krishnan; 19.02.2015
comment
Просто вопрос здесь, если два метода, которые находятся в иерархии и оба подпадают под pointcut и принадлежат к одному классу, будет ли это выполняться на обоих? Если да, то просмотрите stackoverflow.com/questions/37583539/, потому что в моем случае этого не происходит. - person HVT7; 02.06.2016
comment
Я считаю, что публичное выполнение является избыточным, потому что у вас не может быть pointcut для частных методов - person amstegraf; 24.04.2020

Используя аннотации, как описано в вопросе.

Аннотация: @Monitor

Аннотация к классу, app/PagesController.java:

package app;
@Controller
@Monitor
public class PagesController {
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public @ResponseBody String home() {
        return "w00t!";
    }
}

Аннотация к методу, app/PagesController.java:

package app;
@Controller
public class PagesController {
    @Monitor
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public @ResponseBody String home() {
        return "w00t!";
    }
}

Пользовательская аннотация, app/Monitor.java:

package app;
@Component
@Target(value = {ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Monitor {
}

Аспект аннотации, app/MonitorAspect.java:

package app;
@Component
@Aspect
public class MonitorAspect {
    @Before(value = "@within(app.Monitor) || @annotation(app.Monitor)")
    public void before(JoinPoint joinPoint) throws Throwable {
        LogFactory.getLog(MonitorAspect.class).info("monitor.before, class: " + joinPoint.getSignature().getDeclaringType().getSimpleName() + ", method: " + joinPoint.getSignature().getName());
    }

    @After(value = "@within(app.Monitor) || @annotation(app.Monitor)")
    public void after(JoinPoint joinPoint) throws Throwable {
        LogFactory.getLog(MonitorAspect.class).info("monitor.after, class: " + joinPoint.getSignature().getDeclaringType().getSimpleName() + ", method: " + joinPoint.getSignature().getName());
    }
}

Включите AspectJ, servlet-context.xml:

<aop:aspectj-autoproxy />

Включите библиотеки AspectJ, pom.xml:

<artifactId>spring-aop</artifactId>
<artifactId>aspectjrt</artifactId>
<artifactId>aspectjweaver</artifactId>
<artifactId>cglib</artifactId>
person Alex    schedule 15.05.2012
comment
Хороший пример. Один вопрос: почему аннотация Monitor должна быть Spring Component? - person mwhs; 22.11.2013
comment
Аннотация Component используется, чтобы указать контейнеру Spring применить включение класса в объект AspectJ weaver. По умолчанию Spring просматривает только Controller, Service и другие конкретные аннотации, но не Aspect. - person Alex; 23.11.2013
comment
Хорошо спасибо. Но я говорил об аннотации @Component к @interface, а не к Aspect. Зачем это нужно? - person mwhs; 23.11.2013
comment
Аннотация @Component позволяет Spring компилировать его с аспектно-ориентированной системой AspectJ IoC / DI. Я не знаю, как это сказать по-другому. docs.spring.io/spring/ документы / 3.2.x / spring-framework-reference / - person Alex; 25.11.2013
comment
Это касается только общедоступных методов в аннотированных классах или всех методов (независимо от уровня доступа)? - person Lee Meador; 20.04.2015
comment
Я почти уверен, что вы также можете перехватывать частные методы. Что действительно происходит, так это то, что AspectJ переписывает класс. - person Alex; 23.04.2015

Что-то такое:

@Before("execution(* com.yourpackage..*.*(..))")
public void monitor(JoinPoint jp) {
    if (jp.getTarget().getClass().isAnnotationPresent(Monitor.class)) {
       // perform the monitoring actions
    }
}

Обратите внимание, что у вас не должно быть никаких других советов по тому же классу перед этим, потому что аннотации будут потеряны после проксирования.

person Bozho    schedule 06.01.2010

Использовать

@Before("execution(* (@YourAnnotationAtClassLevel *).*(..))")
    public void beforeYourAnnotation(JoinPoint proceedingJoinPoint) throws Throwable {
}
person Davide Consonni    schedule 25.06.2017

этого должно быть достаточно, чтобы пометить ваш метод аспекта следующим образом:

@After("@annotation(com.marcot.CommitTransaction)")
    public void after() {

взгляните на this для пошаговое руководство по этому поводу.

person marcocast    schedule 01.07.2013

Вы также можете определить pointcut как

public pointcut publicMethodInsideAClassMarkedWithAtMonitor() : execution(public * (@Monitor *).*(..));
person Shekhar    schedule 11.01.2011
comment
Немного попроще execution(public * @Monitor *.*(..)) тоже работает. - person xmedeko; 19.12.2014

Кажется, самый простой способ:

@Around("execution(@MyHandling * com.exemple.YourService.*(..))")
public Object aroundServiceMethodAdvice(final ProceedingJoinPoint pjp)
   throws Throwable {
   // perform actions before

   return pjp.proceed();

   // perform actions after
}

Он перехватит выполнение всех методов, специально помеченных @MyHandling в классе YourService. Чтобы перехватить все методы без исключения, просто поместите аннотацию прямо в класс.

Независимо от частной / общедоступной области, но имейте в виду, что spring-aop не может использовать аспект для вызовов методов в одном и том же экземпляре (обычно частные), потому что в этом случае он не использует прокси-класс.

Здесь мы используем совет @Around, но в основном это тот же синтаксис, что и @Before, @After или любой другой совет.

Кстати, аннотацию @MyHandling нужно настроить так:

@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.METHOD, ElementType.TYPE })
public @interface MyHandling {

}
person Donatello    schedule 24.06.2011
comment
который не отвечает на исходный оператор, с ElementType.Type - person Alex; 15.05.2012
comment
да, ElementType.TYPE также позволит помещать аннотации непосредственно в классы, что, как я полагаю, приведет к обработке любого метода в этом классе. Я правда? Это действительно работает? - person Donatello; 24.05.2012
comment
// perform actions after никогда не будет вызван, поскольку мы возвращали значение в строке раньше. - person josephpconley; 21.04.2017

Из Spring's AnnotationTransactionAspect:

/**
 * Matches the execution of any public method in a type with the Transactional
 * annotation, or any subtype of a type with the Transactional annotation.
 */
private pointcut executionOfAnyPublicMethodInAtTransactionalType() :
    execution(public * ((@Transactional *)+).*(..)) && within(@Transactional *);
person xmedeko    schedule 19.12.2014

Вы можете использовать Spring PerformanceMonitoringInterceptor и программно зарегистрировать совет с помощью постпроцессора beanpost.

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Monitorable
{

}


public class PerformanceMonitorBeanPostProcessor extends ProxyConfig implements BeanPostProcessor, BeanClassLoaderAware, Ordered,
    InitializingBean
{

  private Class<? extends Annotation> annotationType = Monitorable.class;

  private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();

  private Advisor advisor;

  public void setBeanClassLoader(ClassLoader classLoader)
  {
    this.beanClassLoader = classLoader;
  }

  public int getOrder()
  {
    return LOWEST_PRECEDENCE;
  }

  public void afterPropertiesSet()
  {
    Pointcut pointcut = new AnnotationMatchingPointcut(this.annotationType, true);
    Advice advice = getInterceptor();
    this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
  }

  private Advice getInterceptor()
  {
    return new PerformanceMonitoringInterceptor();
  }

  public Object postProcessBeforeInitialization(Object bean, String beanName)
  {
    return bean;
  }

  public Object postProcessAfterInitialization(Object bean, String beanName)
  {
    if(bean instanceof AopInfrastructureBean)
    {
      return bean;
    }
    Class<?> targetClass = AopUtils.getTargetClass(bean);
    if(AopUtils.canApply(this.advisor, targetClass))
    {
      if(bean instanceof Advised)
      {
        ((Advised)bean).addAdvisor(this.advisor);
        return bean;
      }
      else
      {
        ProxyFactory proxyFactory = new ProxyFactory(bean);
        proxyFactory.copyFrom(this);
        proxyFactory.addAdvisor(this.advisor);
        return proxyFactory.getProxy(this.beanClassLoader);
      }
    }
    else
    {
      return bean;
    }
  }
}
person Vikram    schedule 23.04.2013

Я поделюсь с вами кодом, который может быть полезен, он предназначен для создания аннотации, которую можно использовать либо в классе, либо в методе.

@Target({TYPE, METHOD})
@Retention(RUNTIME)
@Documented
public @interface AnnotationLogger {
    /**
     * It is the parameter is to show arguments in the method or the class.
     */
    boolean showArguments() default false;
}


@Aspect
@Component
public class AnnotationLoggerAspect {
    
    @Autowired 
    private Logger logger;  
    
    private static final String METHOD_NAME   = "METHOD NAME: {} ";
    private static final String ARGUMENTS     = "ARGS: {} ";
    
    @Before(value = "@within(com.org.example.annotations.AnnotationLogger) || @annotation(com.org.example.annotations.AnnotationLogger)")
    public void logAdviceExecutionBefore(JoinPoint joinPoint){  
        CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();
        AnnotationLogger annotationLogger = getAnnotationLogger(joinPoint);
        if(annotationLogger!= null) {
            StringBuilder annotationLoggerFormat = new StringBuilder();
            List<Object> annotationLoggerArguments = new ArrayList<>();
            annotationLoggerFormat.append(METHOD_NAME);
            annotationLoggerArguments.add(codeSignature.getName());
            
            if (annotationLogger.showArguments()) {
                annotationLoggerFormat.append(ARGUMENTS);
                List<?> argumentList = Arrays.asList(joinPoint.getArgs());
                annotationLoggerArguments.add(argumentList.toString());
            }
            logger.error(annotationLoggerFormat.toString(), annotationLoggerArguments.toArray());
        }
    }
    
    private AnnotationLogger getAnnotationLogger(JoinPoint joinPoint) {
        AnnotationLogger annotationLogger = null;
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = joinPoint.getTarget().getClass().
                    getMethod(signature.getMethod().getName(), signature.getMethod().getParameterTypes());
            
            if (method.isAnnotationPresent(AnnotationLogger.class)){
                annotationLogger = method.getAnnotation(AnnotationLoggerAspect.class);
            }else if (joinPoint.getTarget().getClass().isAnnotationPresent(AnnotationLoggerAspect.class)){
                annotationLogger = joinPoint.getTarget().getClass().getAnnotation(AnnotationLoggerAspect.class);
            }
            return annotationLogger;
        }catch(Exception e) {
            return annotationLogger;
        }
    }
}
person J. Abel    schedule 09.09.2020