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

时间:2018-01-31 11:31:51

标签: spring annotations aspectj spring-aop

假设我想找到所有使用@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 个答案:

答案 0 :(得分:1)

这个怎么样?

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

或者更短一点,但如果在Springs'中有其他类具有匹配名称,则可能太模糊了。包(我还没检查过):

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

更新:关于您的真实问题,我现在在您编辑后了解它。这也是可能的,但在句法上有点棘手。让我将您的注释重命名为MetaAnnotationMyAnnotation,好吗?因为它们不是彼此的父母和孩子,所以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。:execution(* *(..))部分仅在AspectJ中是必需的,以便限制与方法执行的切入点匹配,因为AspectJ可以拦截比Spring AOP更多的事件。因此,在Spring AOP中,您可以消除该部分以及... || ... || ...部分周围的大括号。