@AspectJ pointcut dla wszystkich metod klasy z określoną adnotacją

127

Chcę monitorować wszystkie metody publiczne wszystkich klas z określoną adnotacją (powiedzmy @Monitor) (uwaga: adnotacja jest na poziomie klasy). Co mogłoby być w tym przypadku możliwe? Uwaga: używam Spring AOP w stylu @AspectJ.

Rejeev Divakaran
źródło
Poniższy działa w dużym stopniu. @Pointcut ("wykonanie (* (@ org.rejeev.Monitor *). * (..))") Jednak teraz porada jest wykonywana dwukrotnie. Jakaś wskazówka?
Rejeev Divakaran
Inną kwestią jest to, że adnotacja @Monitor znajduje się w interfejsie i klasa to implementuje. Czy obecność interfejsu i klasy spowoduje podwójne wykonanie takiej porady?
Rejeev Divakaran
6
Powinieneś zaakceptować doskonałą odpowiedź poniżej. To daje mu reputację. W SO jest bardzo niewiele osób, które mogą odpowiedzieć na pytania AspectJ.
fool4jesus

Odpowiedzi:

162

Należy połączyć punkt przekroju typu z punktem przekroju metody.

Poniższe wskazówki wykonają pracę, aby znaleźć wszystkie metody publiczne wewnątrz klasy oznaczonej adnotacją @Monitor:

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

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

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

Poradź ostatni punkt cięcia, który łączy dwa pierwsze i gotowe!

Jeśli jesteś zainteresowany, napisałem tutaj ściągawkę w stylu @AspectJ z odpowiednim przykładowym dokumentem tutaj.

Espen
źródło
Dzięki. Szczególnie przydatna jest dyskusja o punktach adnotacji na ściągawce.
GregHNZ,
1
Jak uzyskać odniesienie do klasy w poradach, tak jak w przypadku zwykłych porad dotyczących punktów cięcia, to @Before ("onObjectAction () && this (obj)")
Priyadarshi Kunal
Ściągawka była bardzo pomocna, mimo że minęło już 5 lat :)
Yadu Krishnan,
Tylko pytanie, czy jeśli dwie metody, które są w hierarchii i obie należą do punktu przecięcia i należą do tej samej klasy, czy to się wykona na obu? Jeśli tak, zobacz stackoverflow.com/questions/37583539/… , ponieważ tak się nie dzieje w moim przypadku.
HVT7
Uważam, że publiczne egzekucje jest zbędne, ponieważ nie można mieć punktacji na prywatne metody
amstegraf
58

Korzystanie z adnotacji zgodnie z opisem w pytaniu.

Adnotacja: @Monitor

Adnotacja na temat zajęć app/PagesController.java:

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

Adnotacja o metodzie app/PagesController.java:

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

Niestandardowe adnotacji app/Monitor.java:

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

Aspekt do adnotacji 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());
    }
}

Włącz AspectJ, servlet-context.xml:

<aop:aspectj-autoproxy />

Obejmują bibliotek AspectJ, pom.xml:

<artifactId>spring-aop</artifactId>
<artifactId>aspectjrt</artifactId>
<artifactId>aspectjweaver</artifactId>
<artifactId>cglib</artifactId>
Alex
źródło
1
Niezły przykład. Jedno pytanie: dlaczego adnotacja Monitormusi być wiosną Component?
mwhs
1
ComponentAdnotacji służy powiedzieć pojemnik Wiosna zastosowania obejmują klasę w AspectJ tkacz rzeczy. Domyślnie Wiosna tylko patrzy Controller, Servicei inne adnotacje konkretne, ale nie Aspect.
Alex
1
Ok dzięki. Ale ja mówiłem o @Componentadnotacji na @interfacenie Aspect. Dlaczego jest to potrzebne?
mwhs
2
@ComponentAdnotacja czyni go tak Wiosna będzie skompilować go z systemem aspekt zorientowane AspectJ IoC / DI. Nie wiem, jak inaczej to powiedzieć. docs.spring.io/spring/docs/3.2.x/spring-framework-reference/…
Alex
Czy dotyczy to tylko metod „publicznych” w klasach z adnotacjami, czy też wszystkich metod (bez względu na poziom dostępu)?
Lee Meador
14

Coś w tym stylu:

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

Pamiętaj, że nie możesz mieć żadnych innych porad dotyczących tej samej klasy przed tą, ponieważ adnotacje zostaną utracone po przesłaniu proxy.

Bozho
źródło
11

Posługiwać się

@Before("execution(* (@YourAnnotationAtClassLevel *).*(..))")
    public void beforeYourAnnotation(JoinPoint proceedingJoinPoint) throws Throwable {
}
Davide Consonni
źródło
4

powinno wystarczyć oznaczenie metody aspektu w ten sposób:

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

spójrz na to, aby zapoznać się z przewodnikiem krok po kroku.

marcocast
źródło
3

Można również zdefiniować punkt przekroju jako

public pointcut publicMethodInsideAClassMarkedWithAtMonitor() : execution(public * (@Monitor *).*(..));
Shekhar
źródło
Trochę prostsze też execution(public * @Monitor *.*(..))działa.
xmedeko
3

Najprostszym sposobem wydaje się być:

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

   return pjp.proceed();

   // perform actions after
}

Przechwytuje wykonanie wszystkich metod z adnotacją „@MyHandling” w klasie „YourService”. Aby przechwycić wszystkie metody bez wyjątku, wystarczy umieścić adnotację bezpośrednio w klasie.

Bez względu na zakres prywatny / publiczny tutaj, ale pamiętaj, że spring-aop nie może używać aspektu dla wywołań metod w tej samej instancji (zwykle prywatnych), ponieważ w tym przypadku nie używa klasy proxy.

Używamy tutaj porady @Around, ale w zasadzie jest to ta sama składnia z @Before, @After lub jakąkolwiek radą.

Nawiasem mówiąc, adnotacja @MyHandling musi być skonfigurowana w następujący sposób:

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

}
Donatello
źródło
to nie odpowiada oryginalnemu stwierdzeniu, z ElementType.Type
Alex
tak, ElementType.TYPE pozwoli również na umieszczanie adnotacji bezpośrednio w klasach, co, jak przypuszczam, spowoduje obsługę dowolnej metody w tej klasie. Czy to prawda? Czy to naprawdę działa?
Donatello
// perform actions afterNigdy nie będzie sprawdzony ponieważ mamy do zwrotu wartości w wierszu poprzednim.
josephpconley
1

Możesz użyć Springa PerformanceMonitoringInterceptor i programowo zarejestrować poradę za pomocą 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;
    }
  }
}
Vikram
źródło
1

Od wiosny 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
źródło