代码之家  ›  专栏  ›  技术社区  ›  maojf

如何在Spring AOP中拦截元注释(带注释的注释)

  •  1
  • maojf  · 技术社区  · 7 年前

    假设我想找到所有用@Controller注释的类,我将创建以下切入点:

        @Pointcut("within(@org.springframework.stereotype.Controller *)")
        public void controllerPointcut() {}
    

    但找不到带@RestController注释的控制器。 因为RestController本身是用@Controller注释的。

    你知道如何在不必创建两个切入点的情况下找到用@Controller或@RestController注释的类吗?


    ====编辑==== 我的真实意图如下:

    父批注:

    public @interface ParentAnnotation {}
    

    子批注(使用@ParentAnnotation进行批注):

    @ParentAnnotation 
    public @interface ChildAnnotation {}
    

    A类:

    @ParentAnnotation 
    public class MyClassA {}
    

    B类:

    @ChildAnnotation 
    public class MyClassB {}
    

    现在我想通过@ParentAnnotation找到MyClassA和MyClassB。 毫无疑问,可以找到MyClassA,但MyClassB是用@ParentAnnotation间接注释的,有没有通用的方法来处理这种情况?

    1 回复  |  直到 7 年前
        1
  •  5
  •   kriegaex    7 年前

    这个怎么样?

    @Pointcut(
      "within(@org.springframework.stereotype.Controller *) || " + 
      "within(@org.springframework.web.bind.annotation.RestController *)" + 
    )
    

    或者稍微短一点,但如果Springs的包中有其他具有匹配名称的类,则可能太模糊了(我没有检查):

    @Pointcut("within(@(org.springframework..*Controller) *)")
    

    更新: 至于你真正的问题,经过你的编辑,我现在明白了。这也是可能的,但在句法上有点棘手。让我将您的批注重命名为 MetaAnnotation MyAnnotation 可以因为它们实际上不是彼此的父级和子级,所以OOP意义上不涉及继承,只涉及嵌套。

    注释:

    请确保注释确实具有运行时范围。我在你的代码中没有看到这一点。

    package de.scrum_master.app;
    
    import static java.lang.annotation.ElementType.*;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    @Retention(RUNTIME)
    @Target({ TYPE })
    public @interface MetaAnnotation {}
    
    package de.scrum_master.app;
    
    import static java.lang.annotation.ElementType.*;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    @Retention(RUNTIME)
    @Target({ TYPE })
    @MetaAnnotation
    public @interface MyAnnotation {}
    

    Java示例类:

    一个类带有元注释,一个带有注释,一个没有注释(否定测试用例):

    package de.scrum_master.app;
    
    @MetaAnnotation
    public class MyClassA {
      public void doSomething() {}
    }
    
    package de.scrum_master.app;
    
    @MyAnnotation
    public class MyClassB {
      public void doSomething() {}
    }
    
    package de.scrum_master.app;
    
    public class MyClassC {
      public void doSomething() {}
    }
    

    驱动程序应用程序:

    因为我使用的是没有Spring的纯Java+AspectJ,所以我使用这个小应用程序来演示结果。

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

    方面:

    package de.scrum_master.aspect;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    @Aspect
    public class MetaAnnotationInterceptor {
      @Before(
        "execution(* *(..)) && (" +
          "within(@de.scrum_master.app.MetaAnnotation *) || " +
          "within(@(@de.scrum_master.app.MetaAnnotation *) *)" +
        ")"
      )
      public void myAdvice(JoinPoint thisJoinPoint){
        System.out.println(thisJoinPoint);
      }
    }
    

    控制台日志:

    execution(void de.scrum_master.app.MyClassA.doSomething())
    execution(void de.scrum_master.app.MyClassB.doSomething())
    

    现在,如果要添加另一层嵌套,请添加一个新注释,并用它注释以前未注释的类:

    package de.scrum_master.app;
    
    import static java.lang.annotation.ElementType.*;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    @Retention(RUNTIME)
    @Target({ TYPE })
    @MyAnnotation
    public @interface MyOtherAnnotation {}
    
    package de.scrum_master.app;
    
    @MyOtherAnnotation
    public class MyClassC {
      public void doSomething() {}
    }
    

    然后将切入点扩展一个以上的嵌套/递归级别:

    package de.scrum_master.aspect;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    @Aspect
    public class MetaAnnotationInterceptor {
      @Before(
        "execution(* *(..)) && (" +
          "within(@de.scrum_master.app.MetaAnnotation *) || " +
          "within(@(@de.scrum_master.app.MetaAnnotation *) *) || " +
          "within(@(@(@de.scrum_master.app.MetaAnnotation *) *) *)" +
        ")"
      )
      public void myAdvice(JoinPoint thisJoinPoint){
        System.out.println(thisJoinPoint);
      }
    }
    

    控制台日志更改为:

    execution(void de.scrum_master.app.MyClassA.doSomething())
    execution(void de.scrum_master.app.MyClassB.doSomething())
    execution(void de.scrum_master.app.MyClassC.doSomething())
    

    P、 S.:The execution(* *(..)) part仅在AspectJ中是必需的,以便将切入点匹配限制为方法执行,因为AspectJ可以截获比Spring AOP更多的事件。因此,在Spring AOP中,可以消除该部分和 ... || ... || ... 部分