特定の注釈を持つクラスのすべてのメソッドの@AspectJポイントカット


127

指定した注釈(@Monitorなど)を使用して、すべてのクラスのすべてのパブリックメソッドを監視したい(注:注釈はクラスレベルです)。このためのポイントカットは何でしょうか?注:私は@AspectJスタイルのSpring AOPを使用しています。


以下のものは拡張して機能します。@Pointcut( "execution(*(@ org.rejeev.Monitor *)。*(..))")ただし、現在アドバイスは2回実行されています。どんな手掛かり?
Rejeev Divakaran、2010年

もう1つのポイントは、@ Monitorアノテーションがインターフェース上にあり、クラスがそれを実装していることです。インターフェースとクラスの存在はそのようなアドバイスの二重の実行を引き起こしますか?
Rejeev Divakaran、2010年

6
以下の優れた答えを受け入れる必要があります。これは彼に評判を与えます。SOには、AspectJの質問に答えられる貴重な人はほとんどいません。
fool4jesus 2013年

回答:


162

タイプポイントカットとメソッドポイントカットを組み合わせる必要があります。

これらのポイントカットは、@ Monitorアノテーションが付けられたクラス内のすべてのパブリックメソッドを見つける作業を行います。

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

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

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

最初の2つを組み合わせた最後のポイントカットにアドバイスすれば完了です。

興味があれば、ここに@AspectJスタイルのチートシートを書き、対応するサンプルドキュメントをここに示します


ありがとう。チートシートの注釈ポイントカットの説明は特に役立ちます。
GregHNZ、2013

1
通常のポイントカットアドバイスで行うように、アドバイスでクラスへの参照を取得するには、@ Before( "onObjectAction()&& this(obj)")
Priyadarshi Kunal


ここで質問です。階層にあり、両方がポイントカットに該当し、同じクラスに属する2つのメソッドがある場合、これは両方で実行されますか?はいの場合は、stackoverflow.com / questions / 37583539 /…を参照してください。これは、私の場合は発生しないためです。
HVT7 2016年

あなたはプライベートメソッドをポイントカットすることができないので、実行パブリックは冗長だと思います
amstegraf

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
いい例です。1つの質問:なぜ注釈Monitorは春でなければならないのComponentですか?
mwhs 2013年

1
このComponentアノテーションは、AspectJウィーバーの事柄にクラスを含めるようにSpringコンテナーに指示するために使用されます。デフォルトでは、Springは、、およびその他の特定のアノテーションのみを確認しControllerServiceは確認しませんAspect
アレックス

1
わかりました。しかし、私はの@Component注釈について話して@interfaceいましたAspect。なぜそれが必要なのですか?
mwhs 2013年

2
@Component春のAspectJのIoC / DIアスペクト指向システムでコンパイルされますので、注釈は、それを作ります。別の言い方はわかりません。docs.spring.io/spring/docs/3.2.x/spring-framework-reference/...
アレックス

これは、注釈付きクラスの「パブリック」メソッドのみを実行しますか、それとも(アクセスレベルに関係なく)すべてのメソッドを実行しますか?
Lee Meador、

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 2014

3

最も簡単な方法は次のようです:

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

   return pjp.proceed();

   // perform actions after
}

「YourService」クラスの「@MyHandling」で特に注釈が付けられたすべてのメソッドの実行をインターセプトします。すべてのメソッドを例外なくインターセプトするには、注釈を直接クラスに配置します。

ここではプライベートスコープまたはパブリックスコープは関係ありませんが、この場合プロキシクラスを使用しないため、spring-aopは同じインスタンス(通常はプライベートコール)でメソッド呼び出しにアスペクトを使用できないことに注意してください。

ここでは@Aroundアドバイスを使用しますが、基本的には@ Before、@ Afterまたはその他のアドバイスと同じ構文です。

ちなみに、@ MyHandlingアノテーションは次のように設定する必要があります。

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

}

ElementType.Type
Alex

はい。ElementType.TYPEを使用すると、クラスに直接アノテーションを付けることもできます。これにより、このクラスのすべてのメソッドが処理されると思います。私は本当ですか?それは本当に機能していますか?
Donatello

// perform actions after私たちは前の行の値を返していることから呼ばれることはありません飽きないだろう。
josephpconley

1

Springの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 *);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.