在 Spring AOP 或 AspectJ 中拦截带注释的类和方法

Use*_*291 4 java annotations aspectj spring-aop spring-boot

所以我有一个自定义注释

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Intercepted {}
Run Code Online (Sandbox Code Playgroud)

我想用它来将方面编织到方法中 (AspectJ, @annotation(Intercepted))。

这个想法是,当我@Intercepted直接对方法进行注释时,我会编织方面- 该部分有效 - 或者如果我对类进行注释,则该方面应该被编织到其所有(公共)方法中 - 该部分没有。

此外,如果我注释一个类和它的一个方法,方面应该只被编织一次,方法级别的注释覆盖类级别的注释。

本质上,我想要一个“如果有类级注释,则添加类级注释,但前提是还没有方法级注释。”

我怎么做?

kri*_*aex 9

这是一个 AspectJ 示例。Spring AOP 中的切入点语法是相同的。

辅助类:

package de.scrum_master.app;

import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Intercepted {}
Run Code Online (Sandbox Code Playgroud)
package de.scrum_master.app;

@Intercepted
public class AnnotatedClass {
  public void doSomething() {}
  public void doSomethingElse() {}
}
Run Code Online (Sandbox Code Playgroud)
package de.scrum_master.app;

public class AnnotatedMethod {
  @Intercepted
  public void doSomething() {}
  public void doSomethingElse() {}
}
Run Code Online (Sandbox Code Playgroud)
package de.scrum_master.app;

@Intercepted
public class AnnotatedMixed {
  @Intercepted
  public void doSomething() {}
  public void doSomethingElse() {}
}
Run Code Online (Sandbox Code Playgroud)

驱动程序应用程序(Java SE,无 Spring):

package de.scrum_master.app;

public class Application {
  public static void main(String[] args) {
    // Should be logged
    new AnnotatedClass().doSomething();
    // Should be logged
    new AnnotatedClass().doSomethingElse();

    // Should be logged
    new AnnotatedMethod().doSomething();
    // Should NOT be logged
    new AnnotatedMethod().doSomethingElse();

    // Should be logged, but only once
    new AnnotatedMixed().doSomething();
    // Should be logged
    new AnnotatedMixed().doSomethingElse();
  }
}
Run Code Online (Sandbox Code Playgroud)

方面:

请注意,该execution(* *(..)) &&部分在 Spring AOP 中不是必需的,因为那里只支持方法执行连接点。切入点可能就在annotatedMethod() || annotatedClass()那里。在 AspectJ 中,我必须更精确,否则会记录其他连接点类型。

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class AnnotationInterceptor {
  @Pointcut("@annotation(de.scrum_master.app.Intercepted)")
  public void annotatedMethod() {}

  @Pointcut("@within(de.scrum_master.app.Intercepted)")
  public void annotatedClass() {}

  @Before("execution(* *(..)) && (annotatedMethod() || annotatedClass())")
  public void log(JoinPoint thisJoinPoint) {
    System.out.println(thisJoinPoint);
  }
}
Run Code Online (Sandbox Code Playgroud)

控制台日志:

execution(void de.scrum_master.app.AnnotatedClass.doSomething())
execution(void de.scrum_master.app.AnnotatedClass.doSomethingElse())
execution(void de.scrum_master.app.AnnotatedMethod.doSomething())
execution(void de.scrum_master.app.AnnotatedMixed.doSomething())
execution(void de.scrum_master.app.AnnotatedMixed.doSomethingElse())
Run Code Online (Sandbox Code Playgroud)