具有特定批注的类的所有方法的@AspectJ切入点


127

我想用指定的注释(例如@Monitor)监视所有类的所有公共方法(注意:注释在类级别)。可能的切入点是什么?注意:我正在使用@AspectJ样式的Spring AOP。


下面的内容可以扩展。@Pointcut(“ execution(*(@ org.rejeev.Monitor *)。*(..))”)但是,此建议现在执行了两次。有什么线索吗?
Rejeev Divakaran

另一点是@Monitor批注在接口上,并且有一个类实现了该接口。接口和类的存在是否会导致此类建议的重复执行?
Rejeev Divakaran,2010年

6
您应该接受以下出色的答案。这给了他声誉。SO上很少有人可以回答AspectJ问题。
fool4jesus

Answers:


162

您应该将类​​型切入点与方法切入点结合使用。

这些切入点将在标记为@Monitor的类中查找所有公共方法:

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

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

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

建议结合了前两个的最后一个切入点,您就完成了!

如果您有兴趣,我在这里编写了@AspectJ样式的备忘单,并在此处提供了相应的示例文档


谢谢。备忘单上有关注释切入点的讨论特别有用。
GregHNZ

1
我如何使用常规切入点建议在建议中引用类:@Before(“ onObjectAction()&& this(obj)”)
Priyadarshi Kunal 2013年

备忘单非常有用,尽管已经过去了5年:)
Yadu Krishnan

这只是一个问题,如果两个处于层次结构中并且都属于切入点并且属于同一类的方法,是否会在两个方法上都执行?如果是,那么请参阅stackoverflow.com/questions/37583539/…,因为在我的情况下这没有发生。
HVT7 '16

我觉得执行公开是多余的,因为您不能在私有方法上有切入点
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
很好的例子。一个问题:为什么注释Monitor必须是Spring Component
mwhs

1
Component注释是用来告诉Spring容器应用包括韦弗的事情在AspectJ的类。默认情况下,仅春季看着ControllerService等具体说明,但不是Aspect
亚历克斯

1
好,谢谢。但是我说@Component@interface不是上的注释Aspect。为什么需要那?
mwhs

2
@Component注解使得它如此Spring会由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

这样就足以标记您的Aspect方法:

@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不能在同一实例(通常是私有实例)中对方法调用使用aspect,因为在这种情况下它不使用代理类。

我们在这里使用@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

从Spring的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 *);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.