@AspectJ pointcut untuk semua metode kelas dengan anotasi tertentu

127

Saya ingin memantau semua metode publik dari semua Kelas dengan anotasi yang ditentukan (misalnya @Monitor) (catatan: Anotasi ada di level kelas). Apa yang bisa menjadi titik tolak yang mungkin untuk ini? Catatan: Saya menggunakan @AspectJ style Spring AOP.

Rejeev Divakaran
sumber
Yang di bawah ini berfungsi untuk memperpanjang. @Pointcut ("eksekusi (* (@ org.rejeev.Monitor *). * (..))") Namun sekarang saran ini dieksekusi dua kali. Ada petunjuk?
Rejeev Divakaran
Poin lain adalah bahwa anotasi @Monitor ada di antarmuka dan ada kelas yang mengimplementasikannya. Apakah kehadiran antarmuka dan kelas akan menyebabkan eksekusi ganda saran seperti itu?
Rejeev Divakaran
6
Anda harus menerima jawaban yang sangat baik di bawah ini. Ini memberinya reputasi. Ada beberapa orang yang berharga di sini di SO yang dapat menjawab pertanyaan AspectJ.
fool4jesus

Jawaban:

162

Anda harus menggabungkan jenis titik potong dengan titik potong metode.

Titik-titik ini akan melakukan pekerjaan untuk menemukan semua metode publik di dalam kelas yang ditandai dengan penjelasan @Monitor:

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

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

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

Nasihat pointcut terakhir yang menggabungkan dua yang pertama dan Anda selesai!

Jika Anda tertarik, saya telah menulis lembar contekan dengan gaya @AspectJ di sini dengan contoh dokumen yang sesuai di sini.

Espen
sumber
Terima kasih. Diskusi tentang penjelasan poin Annotation pada Cheat Sheet Anda sangat berguna.
GregHNZ
1
Bagaimana cara saya mendapatkan referensi ke kelas dalam saran seperti yang saya lakukan dengan saran pointcut normal adalah @Before ("onObjectAction () && this (obj)")
Priyadarshi Kunal
Cheat Sheet sangat membantu, meskipun sudah 5 tahun :)
Yadu Krishnan
Hanya sebuah pertanyaan di sini, jika dua metode yang berada dalam hierarki dan keduanya berada di bawah titik potong dan milik kelas yang sama, akankah ini dijalankan pada keduanya? Jika Ya, maka lihat stackoverflow.com/questions/37583539/… , karena ini tidak terjadi dalam kasus saya.
HVT7
Saya merasa eksekusi publik berlebihan karena Anda tidak dapat memiliki pointcut pada metode pribadi
amstegraf
58

Menggunakan anotasi, seperti dijelaskan dalam pertanyaan.

Anotasi: @Monitor

Anotasi pada kelas, app/PagesController.java:

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

Penjelasan metode, app/PagesController.java:

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

Kustom penjelasan, app/Monitor.java:

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

Aspek untuk penjelasan, 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());
    }
}

Aktifkan AspectJ, servlet-context.xml:

<aop:aspectj-autoproxy />

Sertakan AspectJ perpustakaan, pom.xml:

<artifactId>spring-aop</artifactId>
<artifactId>aspectjrt</artifactId>
<artifactId>aspectjweaver</artifactId>
<artifactId>cglib</artifactId>
Alex
sumber
1
Contoh yang bagus. Satu pertanyaan: mengapa Anotasi Monitorharus menjadi Musim Semi Component?
mwhs
1
The Componentpenjelasan digunakan untuk memberitahu wadah musim semi untuk menerapkan menyertakan kelas di AspectJ penenun hal. Secara default, musim semi hanya melihat Controller, Servicedan penjelasan spesifik lainnya, tapi tidak Aspect.
Alex
1
Ok terima kasih. Tetapi saya berbicara tentang @Componentanotasi pada @interfacebukan Aspect. Mengapa itu dibutuhkan?
mwhs
2
The @Componentpenjelasan membuatnya begitu Spring akan compile dengan sistem berorientasi aspek AspectJ IOC / DI. Saya tidak tahu bagaimana mengatakannya secara berbeda. docs.spring.io/spring/docs/3.2.x/spring-framework-reference/…
Alex
Apakah ini hanya melakukan metode "publik" di kelas beranotasi atau apakah itu melakukan semua metode (tidak peduli tingkat akses apa)?
Lee Meador
14

Sesuatu seperti itu:

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

Perhatikan bahwa Anda tidak boleh memiliki saran lain di kelas yang sama sebelum ini, karena anotasi akan hilang setelah proksi.

Bozho
sumber
11

Menggunakan

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

seharusnya cukup untuk menandai metode aspek Anda seperti ini:

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

lihat ini untuk panduan langkah demi langkah tentang ini.

marcocast
sumber
3

Anda juga dapat mendefinisikan titik potong sebagai

public pointcut publicMethodInsideAClassMarkedWithAtMonitor() : execution(public * (@Monitor *).*(..));
Shekhar
sumber
execution(public * @Monitor *.*(..))Bekerja sedikit lebih sederhana juga.
xmedeko
3

Cara paling sederhana adalah:

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

   return pjp.proceed();

   // perform actions after
}

Ini akan mencegat eksekusi semua metode khusus yang dijelaskan dengan '@MyHandling' di kelas 'YourService'. Untuk mencegat semua metode tanpa kecuali, cukup letakkan anotasi langsung di kelas.

Tidak masalah ruang lingkup privat / publik di sini, tetapi perlu diingat bahwa spring-aop tidak dapat menggunakan aspek untuk pemanggilan metode dalam contoh yang sama (biasanya yang privat), karena tidak menggunakan kelas proxy dalam kasus ini.

Kami menggunakan saran @Around di sini, tetapi pada dasarnya sintaksisnya sama dengan @Sebelum, @Setelah saran apa pun.

Omong-omong, anotasi @MyHandling harus dikonfigurasi seperti ini:

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

}
Donatello
sumber
yang tidak menjawab pernyataan aslinya, dengan ElementType.Type
Alex
ya, ElementType.TYPE juga akan memungkinkan untuk menempatkan anotasi langsung di kelas, yang saya kira, akan menghasilkan menangani metode apa pun di kelas ini. Apakah saya benar Apakah ini benar-benar berfungsi?
Donatello
Tidak // perform actions afterakan pernah dipanggil karena kami mengembalikan nilai di baris sebelumnya.
josephpconley
1

Anda bisa menggunakan Spring's PerformanceMonitoringInterceptor dan secara program mendaftarkan saran menggunakan prosesor 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;
    }
  }
}
Vikram
sumber
1

Dari Spring's 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
sumber