@AspectJ pointcut per tutti i metodi di una classe con annotazioni specifiche


127

Voglio monitorare tutti i metodi pubblici di tutte le classi con l'annotazione specificata (ad esempio @Monitor) (nota: l'annotazione è a livello di classe). Quale potrebbe essere un possibile punto per questo? Nota: sto usando lo stile Spring AOP di @AspectJ.


Quello sotto funziona in modo esteso. @Pointcut ("esecuzione (* (@ org.rejeev.Monitor *). * (..))") Tuttavia ora il consiglio viene eseguito due volte. Qualche idea?
Rejeev Divakaran,

Un altro punto è che l'annotazione @Monitor si trova su un'interfaccia e lì una classe lo implementa. La presenza di un'interfaccia e di una classe provocherà una doppia esecuzione di tali consigli?
Rejeev Divakaran,

6
Dovresti accettare l'eccellente risposta di seguito. Questo gli dà reputazione. Ci sono poche persone preziose qui su SO che possono rispondere alle domande di AspectJ.
fool4jesus,

Risposte:


162

È necessario combinare un tipo pointcut con un metodo pointcut.

Questi punti serviranno a trovare tutti i metodi pubblici all'interno di una classe contrassegnata con un'annotazione @Monitor:

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

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

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

Consiglio l'ultimo punto che unisce i primi due e il gioco è fatto!

Se sei interessato, ho scritto un cheat sheet con lo stile @AspectJ qui con un documento di esempio corrispondente qui.


Grazie. La discussione dei punti di annotazione sul tuo cheat sheet è particolarmente utile.
GregHNZ,

1
Come posso ottenere un riferimento alla lezione nel consiglio come faccio con i normali consigli di pointcut è @Before ("onObjectAction () && this (obj)")
Priyadarshi Kunal

Il Cheat Sheet è stato molto utile, anche se sono passati 5 anni :)
Yadu Krishnan,

Solo una domanda qui, se due metodi che sono nella gerarchia ed entrambi rientrano nel punto e appartengono alla stessa classe, questo verrà eseguito su entrambi? In caso affermativo, consultare stackoverflow.com/questions/37583539/… , perché nel mio caso ciò non accade.
HVT7,

Sento che l'esecuzione pubblica è ridondante perché non si può avere una scorciatoia sui metodi privati
amstegraf

58

Utilizzo delle annotazioni, come descritto nella domanda.

Annotazione: @Monitor

Annotazione su di classe, app/PagesController.java:

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

Annotazione sul metodo app/PagesController.java:

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

Annotazione personalizzate, app/Monitor.java:

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

Aspect per l'annotazione, 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());
    }
}

Abilita AspectJ, servlet-context.xml:

<aop:aspectj-autoproxy />

Includere le librerie AspectJ, pom.xml:

<artifactId>spring-aop</artifactId>
<artifactId>aspectjrt</artifactId>
<artifactId>aspectjweaver</artifactId>
<artifactId>cglib</artifactId>

1
Bell'esempio Una domanda: perché l'Annotazione Monitordeve essere una primavera Component?
martedì

1
L' Componentannotazione viene utilizzata per indicare al contenitore Spring da applicare includere la classe nella cosa del tessitore AspectJ. Per impostazione predefinita, Primavera guarda solo Controller, Servicee altre annotazioni specifiche, ma non Aspect.
Alex,

1
Ok grazie. Ma stavo parlando @Componentdell'annotazione sul @interfacenon Aspect. Perché è necessario?
mwhs

2
L' @Componentannotazione lo rende così Spring lo compilerà con il sistema AspectJ IoC / DI orientato all'aspetto. Non so come dirlo diversamente. docs.spring.io/spring/docs/3.2.x/spring-framework-reference/…
Alex

Questo fa solo metodi "pubblici" nelle classi annotate o fa tutti i metodi (indipendentemente dal livello di accesso)?
Lee Meador,

14

Qualcosa del genere:

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

Nota che non devi avere altri consigli sulla stessa classe prima di questo, perché le annotazioni andranno perse dopo il proxy.


11

Uso

@Before("execution(* (@YourAnnotationAtClassLevel *).*(..))")
    public void beforeYourAnnotation(JoinPoint proceedingJoinPoint) throws Throwable {
}

4

dovrebbe essere sufficiente contrassegnare il metodo di aspetto in questo modo:

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

dare un'occhiata a questo per una guida passo passo su questo.


3

È inoltre possibile definire il punto come

public pointcut publicMethodInsideAClassMarkedWithAtMonitor() : execution(public * (@Monitor *).*(..));

Anche le execution(public * @Monitor *.*(..))opere un po 'più semplici .
xmedeko,

3

Il modo più semplice sembra essere:

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

   return pjp.proceed();

   // perform actions after
}

Intercetterà l'esecuzione di tutti i metodi specificamente annotati con '@MyHandling' nella classe 'YourService'. Per intercettare tutti i metodi senza eccezioni, basta inserire l'annotazione direttamente sulla classe.

Non importa l'ambito privato / pubblico qui, ma tieni presente che spring-aop non può usare l'aspetto per le chiamate di metodo nella stessa istanza (in genere quelle private), perché in questo caso non usa la classe proxy.

Usiamo i consigli di @Around qui, ma è sostanzialmente la stessa sintassi con @Before, @After o qualsiasi consiglio.

A proposito, l'annotazione @MyHandling deve essere configurata in questo modo:

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

}

che non risponde alla dichiarazione originale, con ElementType.Type
Alex,

sì, ElementType.TYPE consentirà anche di inserire l'annotazione direttamente sulle classi, che suppongo, risulterà per gestire qualsiasi metodo in questa classe. Sono vero? Funziona davvero?
Donatello,

Non // perform actions afterverrà mai chiamato poiché stiamo restituendo il valore nella riga prima.
josephpconley,

1

È possibile utilizzare SpringMonitoringInterceptor di Spring e registrare a livello di codice i consigli utilizzando un beanpostprocessor.

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

1

Dalla primavera 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 *);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.