@AspectJ pointcut für alle Methoden einer Klasse mit spezifischer Annotation

127

Ich möchte alle öffentlichen Methoden aller Klassen mit der angegebenen Annotation (z. B. @Monitor) überwachen (Hinweis: Annotation ist auf Klassenebene). Was könnte ein möglicher Punkt dafür sein? Hinweis: Ich verwende Spring AOP im @AspectJ-Stil.

Rejeev Divakaran
quelle
Das folgende funktioniert in gewissem Umfang. @Pointcut ("Ausführung (* (@ org.rejeev.Monitor *). * (..))") Jetzt wird der Hinweis jedoch zweimal ausgeführt. Irgendeine Ahnung?
Rejeev Divakaran
Ein weiterer Punkt ist, dass sich die Annotation @Monitor auf einer Schnittstelle befindet und dort eine Klasse dies implementiert. Verursacht das Vorhandensein einer Schnittstelle und einer Klasse eine doppelte Ausführung solcher Ratschläge?
Rejeev Divakaran
6
Sie sollten die ausgezeichnete Antwort unten akzeptieren. Dies gibt ihm Ruf. Es gibt nur wenige Leute hier auf SO, die AspectJ-Fragen beantworten können.
narr4jesus

Antworten:

162

Sie sollten einen Typ-Pointcut mit einem Methoden-Pointcut kombinieren.

Diese Pointcuts erledigen die Arbeit, um alle öffentlichen Methoden in einer Klasse zu finden, die mit einer @ Monitor-Annotation gekennzeichnet ist:

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

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

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

Beraten Sie den letzten Pointcut, der die ersten beiden kombiniert, und Sie sind fertig!

Wenn Sie interessiert sind, habe ich hier einen Spickzettel im @AspectJ-Stil mit einem entsprechenden Beispieldokument hier geschrieben.

Espen
quelle
Vielen Dank. Die Diskussion von Anmerkungspunkten auf Ihrem Spickzettel ist besonders nützlich.
GregHNZ
1
Wie bekomme ich einen Verweis auf die Klasse in den Ratschlägen, wie ich es mit normalen Pointcut-Ratschlägen mache? @Before ("onObjectAction () && this (obj)")
Priyadarshi Kunal
Der Spickzettel war sehr hilfreich, obwohl es 5 Jahre her ist :)
Yadu Krishnan
Nur eine Frage hier: Wenn zwei Methoden, die sich in einer Hierarchie befinden und beide unter den Pointcut fallen und zur selben Klasse gehören, wird dies für beide ausgeführt? Wenn ja, lesen Sie stackoverflow.com/questions/37583539/… , da dies in meinem Fall nicht der Fall ist.
HVT7
Ich
halte die
58

Verwenden von Anmerkungen, wie in der Frage beschrieben.

Anmerkung: @Monitor

Anmerkung zur Klasse app/PagesController.java:

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

Anmerkung zur Methode app/PagesController.java:

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

Benutzerdefinierte Anmerkung , app/Monitor.java:

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

Aspekt für Annotation , 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 aktivieren , servlet-context.xml:

<aop:aspectj-autoproxy />

AspectJ-Bibliotheken einschließen , pom.xml:

<artifactId>spring-aop</artifactId>
<artifactId>aspectjrt</artifactId>
<artifactId>aspectjweaver</artifactId>
<artifactId>cglib</artifactId>
Alex
quelle
1
Schönes Beispiel. Eine Frage: Warum muss die Anmerkung Monitorein Frühling sein Component?
mwhs
1
Die ComponentAnmerkung wird verwendet, um den Spring-Container anzuweisen, die Klasse in die AspectJ-Weber-Sache aufzunehmen. In der Standardeinstellung Frühling sieht nur auf Controller, Serviceund andere spezifische Anmerkungen, aber nicht Aspect.
Alex
1
OK danke. Aber ich habe über die @ComponentAnmerkung auf der @interfacenicht die gesprochen Aspect. Warum wird das benötigt?
mwhs
2
Die @ComponentAnnotation macht es so, dass Spring es mit dem aspektorientierten AspectJ IoC / DI-System kompiliert. Ich weiß nicht, wie ich es anders sagen soll. docs.spring.io/spring/docs/3.2.x/spring-framework-reference/…
Alex
Führt dies nur "öffentliche" Methoden in den mit Anmerkungen versehenen Klassen aus oder werden alle Methoden ausgeführt (unabhängig von der Zugriffsebene)?
Lee Meador
14

Sowas in der Art:

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

Beachten Sie, dass Sie vor dieser Klasse keine weiteren Ratschläge zu derselben Klasse haben dürfen , da die Anmerkungen nach dem Proxy verloren gehen.

Bozho
quelle
11

Verwenden

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

Es sollte ausreichen, Ihre Aspektmethode wie folgt zu markieren:

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

Schauen Sie sich dies an, um eine schrittweise Anleitung dazu zu erhalten.

Marcocast
quelle
3

Sie können den Pointcut auch als definieren

public pointcut publicMethodInsideAClassMarkedWithAtMonitor() : execution(public * (@Monitor *).*(..));
Shekhar
quelle
Auch etwas einfacher execution(public * @Monitor *.*(..))funktioniert.
Xmedeko
3

Der einfachste Weg scheint zu sein:

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

   return pjp.proceed();

   // perform actions after
}

Die Ausführung aller Methoden, die speziell mit '@MyHandling' in der Klasse 'YourService' versehen sind, wird abgefangen. Um alle Methoden ausnahmslos abzufangen, fügen Sie die Anmerkung einfach direkt in die Klasse ein.

Unabhängig vom privaten / öffentlichen Bereich hier, aber denken Sie daran, dass spring-aop keinen Aspekt für Methodenaufrufe in derselben Instanz verwenden kann (normalerweise private), da in diesem Fall die Proxy-Klasse nicht verwendet wird.

Wir verwenden hier @ Round-Ratschläge, aber es ist im Grunde die gleiche Syntax wie bei @Before, @After oder anderen Ratschlägen.

Die @ MyHandling-Annotation muss übrigens folgendermaßen konfiguriert werden:

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

}
Donatello
quelle
das beantwortet nicht die ursprüngliche Aussage, mit ElementType.Type
Alex
Ja, ElementType.TYPE ermöglicht es auch, Anmerkungen direkt in Klassen einzufügen, was vermutlich dazu führt, dass jede Methode in dieser Klasse behandelt wird. Bin ich wahr Funktioniert es wirklich?
Donatello
Der // perform actions afterwird nie aufgerufen, da wir den Wert in der vorherigen Zeile zurückgeben.
Josephpconley
1

Sie können den PerformanceMonitoringInterceptor von Spring verwenden und den Hinweis mithilfe eines Beanpost-Prozessors programmgesteuert registrieren.

@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;
    }
  }
}
Vikram
quelle
1

Aus dem Frühling 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 *);
xmedeko
quelle