Belirli bir ek açıklamaya sahip bir sınıfın tüm yöntemleri için @AspectJ pointcut


127

Belirtilen açıklama ile tüm Sınıfların tüm genel yöntemlerini izlemek istiyorum (@ Monitor diyelim) (not: Ek açıklama sınıf düzeyindedir). Bunun için olası bir nokta ne olabilir? Not: @AspectJ tarzı Yay AOP kullanıyorum.


Aşağıdakiler bir uzatma için çalışıyor. @Pointcut ("execution (* (@ org.rejeev.Monitor *). * (..))") Ancak şimdi tavsiye iki kez uygulanıyor. Bir ipucu?
— Rejeev Divakaran

Diğer bir nokta da @Monitor açıklamasının bir arayüzde olması ve bir sınıfın bunu uygulamasıdır. Bir arayüzün ve sınıfın varlığı bu tür tavsiyelerin iki kez yürütülmesine neden olur mu?
— Rejeev Divakaran

6
Aşağıdaki mükemmel cevabı kabul etmelisiniz. Bu ona itibar kazandırır. SO'da AspectJ sorularına cevap verebilecek çok az sayıda insan var.
— fool4jesus

Yanıtlar:


164

Yazım noktasını bir yöntem nokta kesimi ile birleştirmelisiniz.

Bu nokta kesimleri, @Monitor ek açıklamasıyla işaretlenmiş bir sınıf içindeki tüm genel yöntemleri bulmak için çalışacaktır:

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

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

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

İlk ikisini birleştiren son noktayı tavsiye edin ve bitirdiniz!

Eğer ilgileniyorsanız, buraya @AspectJ stiliyle bir hile sayfası yazdım ve burada karşılık gelen bir örnek belge .


Teşekkürler. Hile Sayfanızdaki Ek açıklama nokta kesimleri tartışması özellikle yararlıdır.
— GregHNZ

1
Tavsiyede sınıfa nasıl referans olabilirim, normal noktasal tavsiyelerde yaptığım gibi @Before ("onObjectAction () && this (obj)")
— Priyadarshi Kunal

Hile Sayfası 5 yıl geçmiş olmasına rağmen çok yardımcı oldu :)
— Yadu Krishnan

Burada sadece bir soru, hiyerarşide olan ve her ikisi de nokta kesiminin altına düşen ve aynı sınıfa ait olan iki yöntem, her ikisinde de yürütülecek mi? Evet ise, o zaman stackoverflow.com/questions/37583539/… adresini ziyaret edin , çünkü bu benim durumumda gerçekleşmiyor.
— HVT7

Herkese açık yürütmenin gereksiz olduğunu düşünüyorum çünkü özel yöntemlerde bir nokta kesemezsiniz
— amstegraf

58

Soruda açıklandığı gibi ek açıklamaları kullanma.

Ek Açıklama: @Monitor

Sınıftaki ek açıklama app/PagesController.java:

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

Yöntemle ilgili ek açıklama app/PagesController.java:

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

Özel açıklama, app/Monitor.java:

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

Ek açıklama yönü 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 etkinleştirme servlet-context.xml:

<aop:aspectj-autoproxy />

AspectJ kitaplıklarını dahil et pom.xml:

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

1
Güzel örnek. Bir soru: Ek Açıklama neden MonitorBahar olmak zorunda Component?
— mwhs

1
ComponentEk açıklama şey dokumacı AspectJ sınıfı dahil uygulamak için Bahar kabı anlatmak için kullanılır. Varsayılan olarak, Bahar sadece bakar Controller, Serviceancak, ve diğer özel ek açıklamaları Aspect.
— Alex

1
Tamam teşekkürler. Ama bahsediyordu @Componentüzerinde açıklama @interfacedeğil Aspect. Buna neden ihtiyaç var?
— mwhs

2
@ComponentYay AspectJ'yi IoC / DI yönü yönelimli sistemi ile derlenir nedenle açıklama yapar. Nasıl farklı söyleyeceğimi bilmiyorum. docs.spring.io/spring/docs/3.2.x/spring-framework-reference/…
— Alex

Bu, açıklamalı sınıflarda yalnızca "genel" yöntemleri mi yapıyor yoksa tüm yöntemleri mi yapıyor (hangi erişim düzeyi ne olursa olsun)?
— Lee Meador

14

Bunun gibi bir şey:

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

Bu sınıftan önce aynı sınıfla ilgili başka bir tavsiyeniz olmamasına dikkat edin , çünkü ek açıklamalar proxy uygulandıktan sonra kaybolacaktır.


11

kullanım

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

4

görünüm yönteminizi şu şekilde işaretlemeniz yeterli olmalıdır:

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

bir göz bu bu konuda adım kılavuz bir adım için.


3

Ayrıca nokta kesimini şu şekilde tanımlayabilirsiniz:

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

Biraz daha basit execution(public * @Monitor *.*(..))işler de.
— xmedeko

3

En basit yol şöyle görünüyor:

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

   return pjp.proceed();

   // perform actions after
}

'YourService' sınıfında '@MyHandling' ile özellikle açıklanmış tüm yöntemlerin yürütülmesini durdurur. İstisnasız tüm yöntemleri kesmek için, açıklamayı doğrudan sınıfa koyun.

Buradaki özel / genel kapsam ne olursa olsun, ancak spring-aop'un aynı örnekteki (tipik olarak özel olanlar) yöntem çağrıları için açı kullanamayacağını unutmayın, çünkü bu durumda proxy sınıfını kullanmaz.

Burada @Around tavsiye kullanıyoruz, ancak temelde @Before, @After veya herhangi bir tavsiye ile aynı sözdizimi.

Bu arada, @MyHandling ek açıklaması şu şekilde yapılandırılmalıdır:

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

}

ElementType ile orijinal ifadeyi yanıtlamıyor.Type
— Alex

evet, ElementType.TYPE ayrıca sınıflara doğrudan açıklama koymaya da izin verecek, ki bu sınıftaki herhangi bir yöntemi ele alacaktır. Ben doğru muyum Gerçekten çalışıyor mu?
— Donatello

Daha // perform actions afterönce satırdaki değeri döndürdüğümüz için asla aranmayacak.
— josephpconley

1

Spring'in PerformanceMonitoringInterceptor'ını kullanabilir ve bir beanpostprocessor kullanarak tavsiyeleri programlı olarak kaydedebilirsiniz.

@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

İlkbahardan itibaren 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 *);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.