指定されたアノテーション(@Monitorなど)を使用して、すべてのクラスのすべてのパブリックメソッドを監視したい(注:アノテーションはクラスレベルです)。これの可能なポイントカットは何でしょうか?注:私は@AspectJスタイルのSpringAOPを使用しています。
10 に答える
タイプポイントカットとメソッドポイントカットを組み合わせる必要があります。
これらのポイントカットは、@Monitor アノテーションでマークされたクラス内のすべてのパブリック メソッドを見つける作業を行います。
@Pointcut("within(@org.rejeev.Monitor *)")
public void beanAnnotatedWithMonitor() {}
@Pointcut("execution(public * *(..))")
public void publicMethod() {}
@Pointcut("publicMethod() && beanAnnotatedWithMonitor()")
public void publicMethodInsideAClassMarkedWithAtMonitor() {}
最初の 2 つを組み合わせた最後のポイントカットをアドバイスすれば完了です!
興味があれば、@AspectJ スタイルのチート シートをここに書き、対応するサンプル ドキュメントをここに示します。
質問で説明されているように、注釈を使用します。
注釈:@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>
そんな感じ:
@Before("execution(* com.yourpackage..*.*(..))")
public void monitor(JoinPoint jp) {
if (jp.getTarget().getClass().isAnnotationPresent(Monitor.class)) {
// perform the monitoring actions
}
}
注釈はプロキシ後に失われるため、これより前に同じクラスに関する他のアドバイスを受けてはならないことに注意してください。
次のようにアスペクトメソッドをマークするだけで十分です。
@After("@annotation(com.marcot.CommitTransaction)")
public void after() {
これに関するステップバイステップのガイドについては、これをご覧ください。
最も簡単な方法は次のようです。
@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 {
}
ポイントカットを次のように定義することもできます
public pointcut publicMethodInsideAClassMarkedWithAtMonitor() : execution(public * (@Monitor *).*(..));
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;
}
}
}