@AspectJ pointcut cho tất cả các phương thức của một lớp với chú thích cụ thể


127

Tôi muốn theo dõi tất cả các phương thức công khai của tất cả các Lớp với chú thích được chỉ định (giả sử @Monitor) (lưu ý: Chú thích ở cấp lớp). Điều gì có thể là một điểm cắt có thể cho điều này? Lưu ý: Tôi đang sử dụng @AspectJ kiểu Spring AOP.


Một trong những dưới đây làm việc để mở rộng. @Pointcut ("thực thi (* (@ org.rejeev.Monitor *). * (..))") Tuy nhiên, bây giờ lời khuyên đang được thực hiện hai lần. Có manh mối nào không?
Rejeev Divakaran

Một điểm khác là chú thích @Monitor nằm trên một giao diện và có một lớp thực hiện điều đó. Liệu sự hiện diện của một giao diện và lớp sẽ gây ra việc thực hiện gấp đôi lời khuyên như vậy?
Rejeev Divakaran

6
Bạn nên chấp nhận câu trả lời tuyệt vời dưới đây. Điều này mang lại cho anh danh tiếng. Có rất ít người ở đây trên SO có thể trả lời các câu hỏi của AspectJ.
lừa4jesus

Câu trả lời:


162

Bạn nên kết hợp một kiểu cắt điểm với một phương pháp cắt điểm.

Các phím tắt này sẽ thực hiện công việc tìm tất cả các phương thức công khai trong một lớp được đánh dấu bằng chú thích @Monitor:

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

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

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

Tư vấn cho điểm cuối cùng kết hợp hai phần đầu và bạn đã hoàn tất!

Nếu bạn quan tâm, tôi đã viết một bảng cheat với kiểu @AspectJ ở đây với một tài liệu ví dụ tương ứng ở đây.


Cảm ơn. Thảo luận về các phím tắt chú thích trên Cheat Sheet của bạn đặc biệt hữu ích.
GregHNZ

1
Làm cách nào để tôi tham khảo lớp trong lời khuyên theo cách tôi làm với lời khuyên cắt điểm thông thường là @B Before ("onObjectAction () && this (obj)")
Priyadarshi Kunal 29/07/13

Cheat Sheet rất hữu ích, mặc dù đã 5 năm rồi :)
Yadu Krishnan

Chỉ là một câu hỏi ở đây, nếu hai phương thức được phân cấp và cả hai đều nằm dưới điểm cắt và thuộc cùng một lớp, liệu phương thức này có thực hiện trên cả hai không? Nếu Có, thì hãy xem stackoverflow.com/questions/37583539/ , vì điều này không xảy ra trong trường hợp của tôi.
HVT7

Tôi cảm thấy việc thực thi công khai là dư thừa vì bạn không thể có quan điểm về các phương thức riêng tư
amstegraf

58

Sử dụng các chú thích, như được mô tả trong câu hỏi.

Chú thích: @Monitor

Chú thích trên lớp , app/PagesController.java:

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

Chú thích về phương pháp , app/PagesController.java:

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

Chú thích tùy chỉnh , app/Monitor.java:

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

Các khía cạnh cho chú thích , 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());
    }
}

Kích hoạt AspectJ , servlet-context.xml:

<aop:aspectj-autoproxy />

Bao gồm các thư viện AspectJ , pom.xml:

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

1
Ví dụ tốt đẹp. Một câu hỏi: tại sao chú thích Monitorphải là một mùa xuân Component?
mwhs

1
Các Componentchú thích được sử dụng để nói container mùa xuân để áp dụng bao gồm các lớp trong AspectJ thợ dệt điều. Theo mặc định, mùa xuân chỉ nhìn Controller, Servicevà chú thích cụ thể khác, nhưng không phải Aspect.
Alex

1
Được rồi cảm ơn. Nhưng tôi đã nói về @Componentchú thích trên @interfacekhông Aspect. Tại sao cần thiết?
mwhs

2
Các @Componentchú thích làm cho nó nên mùa xuân sẽ biên dịch nó với hệ thống hướng khía cạnh AspectJ IoC / DI. Tôi không biết nói như thế nào. docs.spring.io/spring/docs/3.2.x/spring-framework-reference/iêu
Alex

Điều này chỉ làm các phương thức "công khai" trong các lớp có chú thích hay nó thực hiện tất cả các phương thức (bất kể mức độ truy cập nào)?
Lee Meador

14

Một cái gì đó như thế:

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

Lưu ý rằng bạn không được có bất kỳ lời khuyên nào khác trên cùng một lớp trước cái này, bởi vì các chú thích sẽ bị mất sau khi ủy quyền.


11

Sử dụng

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

4

nó là đủ để đánh dấu phương pháp khía cạnh của bạn như thế này:

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

có một cái nhìn tại này cho một hướng dẫn từng bước về vấn đề này.


3

Bạn cũng có thể định nghĩa điểm cắt là

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

Công việc nhỏ đơn giản hơn execution(public * @Monitor *.*(..)), quá.
xmedeko

3

Cách đơn giản nhất dường như là:

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

   return pjp.proceed();

   // perform actions after
}

Nó sẽ chặn thực thi tất cả các phương thức được chú thích cụ thể với lớp '@MyHandling' trong lớp 'YourService'. Để chặn tất cả các phương thức mà không có ngoại lệ, chỉ cần đặt chú thích trực tiếp vào lớp.

Bất kể phạm vi riêng tư / công khai ở đây, nhưng hãy nhớ rằng spring-aop không thể sử dụng khía cạnh cho các cuộc gọi phương thức trong cùng một trường hợp (thường là riêng tư), bởi vì nó không sử dụng lớp proxy trong trường hợp này.

Chúng tôi sử dụng lời khuyên @Around ở đây, nhưng về cơ bản, đó là cùng một cú pháp với @B Before, @After hoặc bất kỳ lời khuyên nào.

Nhân tiện, chú thích @MyHandling phải được cấu hình như thế này:

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

}

đó không phải là trả lời câu lệnh gốc, với ElementType.Type
Alex

có, ElementType.TYPE cũng sẽ cho phép đặt chú thích trực tiếp vào các lớp, mà tôi cho rằng, sẽ dẫn đến việc xử lý bất kỳ phương thức nào trong lớp này. Tôi có đúng không Có thực sự làm việc?
Donatello

Các // perform actions aftersẽ không bao giờ được gọi là vì chúng ta đang trở về giá trị trong dòng trước đó.
josephpconley

1

Bạn có thể sử dụng PerformanceMonitoringInterceptor của Spring và đăng ký lời khuyên bằng cách sử dụng bộ xử lý beanpost.

@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

Từ mùa xuân 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 *);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.