代码之家  ›  专栏  ›  技术社区  ›  Roman Puchkovskiy

SpringAOP:有没有办法让@target用于间接注释?

  •  0
  • Roman Puchkovskiy  · 技术社区  · 6 年前

    我有一个注解:

    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
    public @interface MyAnnotation {
    }
    

    @MyAnnotation
    public class TestController { ... }
    

    然后,我添加了一条建议,其中包含以下内容:

    @Pointcut("@target(MyAnnotation)")
    public void annotatedWithMyAnnotation() {}
    
    @Around("annotatedWithMyAnnotation()")
    public Object executeController(ProceedingJoinPoint point) throws Throwable { ... }
    

    已成功调用Advice的方法。

    现在我有一堆控制器共享相同的注释,我想使用一个原型注释来对它们进行分组。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @MyAnnotation
    ... other annotations
    public @interface StereotypeAnnotation {
    }
    

    @StereotypeAnnotation :

    @StereotypeAnnotation
    public class TestController { ... }
    

    控制器不包含 @MyAnnotation 直接的。

    问题 @target 切入点停止匹配我的控制器,不建议它们。

    有没有办法定义一个切入点来匹配具有这种间接注释的控制器?

    1 回复  |  直到 6 年前
        1
  •  2
  •   kriegaex    6 年前

    我用纯AspectJ重新创建了这种情况,因为我不太喜欢SpringAOP。这就是为什么我添加了一个额外的 execution(* *(..)) && 在通知的切入点前面,以避免匹配SpringAOP中不可用的其他连接点,例如 call() . 如果您愿意,可以在SpringAOP中删除它。

    好的,让我们按照您描述的方式创建此情况:

    package de.scrum_master.app;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
    public @interface MyAnnotation {}
    
    package de.scrum_master.app;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @MyAnnotation
    public @interface StereotypeAnnotation {}
    
    package de.scrum_master.app;
    
    @MyAnnotation
    public class TestController {
      public void doSomething() {
        System.out.println("Doing something");
      }
    }
    
    package de.scrum_master.app;
    
    @StereotypeAnnotation
    public class AnotherController {
      public void doSomething() {
        System.out.println("Doing yet another something");
      }
    }
    

    package de.scrum_master.app;
    
    public class Application {
      public static void main(String[] args) {
        new TestController().doSomething();
        new AnotherController().doSomething();
      }
    }
    

    这是一个方面:

    package de.scrum_master.aspect;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    
    @Aspect
    public class MetaAnnotationAspect {
      @Pointcut(
        "@target(de.scrum_master.app.MyAnnotation) || " +
        "@target(de.scrum_master.app.StereotypeAnnotation)"
      )
      public void solutionA() {}
    
      @Around("execution(* *(..)) && solutionA()")
      public Object executeController(ProceedingJoinPoint point) throws Throwable {
        System.out.println(point);
        return point.proceed();
      }
    }
    

    日志输出将是:

    execution(void de.scrum_master.app.TestController.doSomething())
    Doing something
    execution(void de.scrum_master.app.AnotherController.doSomething())
    Doing yet another something
    

    到目前为止,一切顺利。但是如果我们添加另一个嵌套级别呢?

    package de.scrum_master.app;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @StereotypeAnnotation
    public @interface SubStereotypeAnnotation {}
    
    package de.scrum_master.app;
    
    @SubStereotypeAnnotation
    public class YetAnotherController {
      public void doSomething() {
        System.out.println("Doing another something");
      }
    }
    
    package de.scrum_master.app;
    
    public class Application {
      public static void main(String[] args) {
        new TestController().doSomething();
        new AnotherController().doSomething();
        new YetAnotherController().doSomething();
      }
    }
    

    然后切入点将不再匹配嵌套的元/原型注释:

    execution(void de.scrum_master.app.TestController.doSomething())
    Doing something
    execution(void de.scrum_master.app.AnotherController.doSomething())
    Doing yet another something
    Doing another something
    

    我们必须明确地添加 || @target(de.scrum_master.app.StereotypeAnnotation) 到切入点,即我们必须知道层次结构中的所有注释类名。有一种方法可以通过使用 within() my other answer here

    package de.scrum_master.aspect;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    
    @Aspect
    public class MetaAnnotationAspect {
      @Pointcut(
        "within(@de.scrum_master.app.MyAnnotation *) || " +
        "within(@(@de.scrum_master.app.MyAnnotation *) *) || " +
        "within(@(@(@de.scrum_master.app.MyAnnotation *) *) *)"
      )
      public void solutionB() {}
    
      @Around("execution(* *(..)) && solutionB()")
      public Object executeController(ProceedingJoinPoint point) throws Throwable {
        System.out.println(point);
        return point.proceed();
      }
    }
    

    控制台日志更改为:

    execution(void de.scrum_master.app.TestController.doSomething())
    Doing something
    execution(void de.scrum_master.app.AnotherController.doSomething())
    Doing yet another something
    execution(void de.scrum_master.app.YetAnotherController.doSomething())
    Doing another something
    

    看见我们只需要知道一个注释类,即 MyAnnotation ,以覆盖元注释的两个嵌套级别。添加更多的级别将非常简单。我承认这种注释嵌套看起来很做作,我只是想向您解释一下您有哪些选项。