【问题标题】:Spring AOP Pointcut expression for custom annotation in subclass子类中自定义注解的Spring AOP切入点表达式
【发布时间】:2015-06-25 10:20:37
【问题描述】:

我正在研究需要拦截所有使用自定义注释注释的类和方法的日志记录方面。

下面是自定义注解类,可以在类和方法上进行注解:

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Loggable {
    LogLevel value();
}

我正在使用这些切入点表达式来拦截带有注解@Loggable 的方法和类,它适用于所有简单类,但不适用于扩展或实现的类。

//Works for annotation @Loggable on class level
@Pointcut("execution(* *(..)) &&  within(@com.logger.Loggable *)")
public void classAnnotationLogger() {
}

//Working for methods annotated with @Loggable
@Before(value = "@annotation(loggable)", argNames = "jp, loggable")
public void logBeforeAdvice(JoinPoint jp, Loggable loggable) {
  ..
  ..
}

下面是超类的代码

@Component
@Loggable(LogLevel.INFO)
public abstract class Processor{
  public void process(){
      readProcess();
  }

  public abstract void readProcess();
}

下面是子类的代码

@Service
@Loggable(LogLevel.INFO)
public class MyServiceProcessor extends Processor {

  @Override
  public void readProcess(){
    ...
    ...
  }
}

在应用程序中readProcess()被doi​​ng调用

Processor processor = applicationContext.getBean(MyServiceProcessor.class);
processor.readProcess();

即使我在ProcessorMyServiceProcessor 上有@Loggable,但当readProcess 被调用时,建议不会被调用。

但是会为 process() 而不是 readProcess 调用建议。

当注解@Logabble应用于任何类或方法时,我如何编写切入点表达式,它也拦截对任何子类方法的调用?

【问题讨论】:

    标签: java spring aspectj spring-aop pointcut


    【解决方案1】:

    好吧,首先

    @Pointcut("execution(* *(..)) &&  within(@com.logger.Loggable *)")
    public void classAnnotationLogger() {}
    

    只是一个切入点而不是一个建议,所以它不会做任何事情,除非你也有一个实际使用这个切入点的建议。你还没有发布这样的建议,所以我只能推测。

    其次,您没有提供任何会被触发的示例代码

    @Before(value = "@annotation(loggable)", argNames = "jp, loggable")
    public void logBeforeAdvice(JoinPoint jp, Loggable loggable) {}
    

    完全没有,即没有带注释的方法。您的示例代码仅显示带注释的类。

    至于子类上的@Loggable注解,应该没有必要,因为它的基类已经带有相同的注解,注解是@Inherited。这适用于类上的注释,但不适用于方法或接口上的注释,请参阅my other answer 以获取解释和可能的解决方法。

    你的这个例子应该可以正常工作,我看不出它不能工作的原因:

    Processor processor = applicationContext.getBean(MyServiceProcessor.class);
    processor.readProcess();
    

    但是这个对readProcess()(相当于this.readProcess())的内部调用将不起作用:

    public void process() {
        readProcess();
    }
    

    这是因为 Spring AOP 是一个基于代理的“AOP lite”框架,依赖于 JDK 动态代理(用于接口)或 CGLIB 代理(用于类)。但是对this.someMethod() 的调用不会通过任何类型的代理进行路由,因此它们不能被 Spring 方面拦截。这是记录在案的行为。如果您想克服此限制并将方面也应用于内部方法调用,请使用完整的 AspectJ,如文档中的 here

    【讨论】:

    • 您好,感谢您的关注。在我的代码中,我对切入点 classAnnotationLogger 有建议。我知道this.someMethod()不会使用标准的spring AOP。 这里的问题只是在子类方法中对重写的方法 readProcess() 进行注释不起作用,但是在类级别上进行注释,正在记录 process() 方法。无论如何我可以为具有 @Loggable 注释的覆盖方法进行日志记录吗?
    • 这里的可能性很小,因为@Inherited,正如我所说,仅适用于类,不适用于接口或方法。这是 JVM/javac 限制而不是 AspectJ 限制。也许 Spring 可以在那里发挥一些作用,但从 AspectJ 的角度来看,这是行不通的。因此,您要么手动注释子类,要么使用 APT 在编译之前添加注释,或者求助于类级别注释以及方法名称匹配、注释参数或其他差异化因素。
    • 我正在使用方面的编译时编织来解决这个问题。
    • 你能看看这个新问题吗[stackoverflow.com/questions/31290852/…我正面临着。我非常感谢分享有关方面的知识。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多