@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.


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.


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>

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.


11

Verwenden

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

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.


3

Sie können den Pointcut auch als definieren

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

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 {

}

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

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 *);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.