@AspectJ Точка підказки для всіх методів класу зі специфічною анотацією


127

Я хочу контролювати всі публічні методи всіх класів із заданою анотацією (скажімо @Monitor) (примітка: Анотація знаходиться на рівні класу). Що може бути можливим для цього? Примітка: я використовую Spring AOP у стилі @AspectJ.


Наведений нижче працює вкрай. @Pointcut ("виконання (* (@ org.rejeev.Monitor *). * (..))") Однак тепер порада виконується двічі. Якась підказка?
Rejeev Divakaran

Ще один момент полягає в тому, що анотація @Monitor знаходиться на інтерфейсі, і там клас реалізує це. Чи наявність інтерфейсу та класу спричинить подвійне виконання таких порад?
Rejeev Divakaran

6
Ви повинні прийняти відмінну відповідь нижче. Це дає йому репутацію. Тут мало дорогоцінних людей, які можуть відповісти на питання AspectJ.
crazy4jesus

Відповіді:


162

Ви повинні комбінувати тип пункту з методом.

Ці пункти будуть робити роботу з пошуку всіх публічних методів у класі, позначеному анотацією @Monitor:

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

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

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

Порадьте останню точку, яка поєднує перші дві, і ви закінчили!

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


Дякую. Особливо корисно обговорення точок анотацій на вашому шпаргалці.
GregHNZ

1
Як я можу отримати посилання на клас у порадах, як це роблять із звичайними порадами точок зрізання, це @Before ("onObjectAction () && this (obj)")
Priyadarshi Kunal

Чит-лист був дуже корисним, хоча минуло 5 років :)
Yadu Krishnan

Тут лише питання, якщо два методи, які знаходяться в ієрархії, і обидва потрапляють під точку і належать до одного класу, чи буде це виконано на обох? Якщо так, то дивіться stackoverflow.com/questions/37583539/… , тому що це не відбувається в моєму випадку.
HVT7

Я вважаю, що публічне виконання є зайвим, оскільки не можна мати точку
вирішення

58

Використання приміток, як описано в питанні.

Анотація: @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>

1
Гарний приклад. Одне запитання: чому Анотація Monitorповинна бути весною Component?
mwhs

1
ComponentАнотацій використовується , щоб повідомити пружинний контейнер для застосування включають клас в AspectJ ткач речі. За замовчуванням Spring тільки дивиться на Controller, Serviceі інші спеціальні інструкції, але не Aspect.
Олексій

1
Добре, дякую. Але я говорив про @Componentпримітку до @interfaceне Aspect. Для чого це потрібно?
mwhs

2
@ComponentАнотацій робить це так Спрінг буде скомпілювати його з аспектно-орієнтованої системою AspectJ IoC / DI. Я не знаю, як це сказати інакше. docs.spring.io/spring/docs/3.2.x/spring-framework-reference/…
Алекс

Чи роблять це лише "загальнодоступні" методи в анотованих класах чи це роблять усі методи (незалежно від рівня доступу)?
Лі Меадор

14

Щось схоже:

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

Зауважте, що перед цим класом ви не повинні мати жодних інших порад щодо того ж класу , оскільки анотації втрачаються після надання доступу.


11

Використовуйте

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

4

цього має бути достатньо, щоб позначити ваш аспектний метод таким чином:

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

Погляньте на це покрокове керівництво з цього приводу.


3

Ви також можете визначити точку як

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

Трохи простіші execution(public * @Monitor *.*(..))роботи теж.
xmedeko

3

Здається, найпростіший спосіб:

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

}

що не відповідає оригінальній заяві, з ElementType.Type
Олексій

так, ElementType.TYPE також дозволить розміщувати анотацію безпосередньо на класах, що, я думаю, призведе до обробки будь-якого методу цього класу. Я правда? Це справді працює?
Донателло

// perform actions afterНіколи не додзвонилися , так як ми повертаємо значення в рядку перед.
josephpconley

1

Ви можете використовувати Spring's PerformanceMonitoringInterceptor і програматично зареєструвати поради за допомогою процесора 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

З весни 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 *);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.