Spring AOP中如何拦截元注解(annotated annotations) [英] How to intercept meta annotations (annotated annotations) in Spring AOP

查看:37
本文介绍了Spring AOP中如何拦截元注解(annotated annotations)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我想找到所有用@Controller 注释的类,我会创建这个切入点:

Suppose I want to find all classes annotated with @Controller, I would create this pointcut:

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

但是那些用@RestController注解的控制器是找不到的.由于 RestController 本身是用@Controller 注释的.

But those controllers annotated with @RestController can not be found. Since RestController itself is annoatated with @Controller.

关于如何在不必创建两个切入点的情况下找到用@Controller 或 @RestController 注释的类有什么想法吗?

Any idea on how to find classes annotated either with @Controller or @RestController without having to create two Pointcuts ?

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

===== edit ==== My real intention here is as follows:

父注释:

public @interface ParentAnnotation {}

子注解(用@ParentAnnotation注解):

child annotation (annotated with @ParentAnnotation):

@ParentAnnotation 
public @interface ChildAnnotation {}

A 类:

@ParentAnnotation 
public class MyClassA {}

B 类:

@ChildAnnotation 
public class MyClassB {}

现在我想通过@ParentAnnotation 找到MyClassA 和MyClassB.找MyClassA是没有问题的,但是MyClassB是用@ParentAnnotation间接标注的,有没有通用的方法来处理这种情况?

Now I want to find both MyClassA and MyClassB through @ParentAnnotation. There's no question for finding MyClassA, but MyClassB is indirectly annotated with @ParentAnnotation, is there a generic way to deal such situation?

推荐答案

这个怎么样?

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

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

Or a little shorter, but maybe too fuzzy if there are other classes with matching names in Springs' packages (I have not checked):

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

<小时>

更新:至于你真正的问题,我在你编辑后现在明白了.这也是可能的,但在语法上有点棘手.让我将您的注释重命名为 MetaAnnotationMyAnnotation,好吗?因为它们并不是真正的父子关系,所以不涉及 OOP 意义上的继承,只是嵌套.


Update: As for your real question, I understand it now after your edit. This is also possible, but kind of tricky syntactically. Let me rename your annotations into MetaAnnotation and MyAnnotation, okay? Because they are not really parent and child of each other, there is no inheritance involved in the the OOP sense, just nesting.

注释:

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

Please make sure that the annotations have indeed runtime scope. I did not see that in your code.

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 示例类:

一类用元注解注解,一类用注解注解,一类不加注解(否定测试用例):

One class is annotated with the meta annotation, one with the annotated annotation and one with no annotation (negative test case):

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,所以我使用这个小应用程序来演示结果.

Because I am using pure Java + AspectJ without Spring, I am using this little application in order to demonstrate the result.

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())

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

Now if you want to add yet another level of nesting, add a new annotation and annotate the formerly unannotated class with it:

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() {}
}

然后将切入点再扩展一层嵌套/递归:

Then extend the pointcut by one more level of nesting/recursion:

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 中,您可以消除该部分以及 ... || 周围的大括号.... ||... 部分.

P.S.: The execution(* *(..)) part is only necessary in AspectJ in order to limit pointcut matching to method executions because AspectJ can intercept more events than Spring AOP. So in Spring AOP you can eliminate that part and the braces surrounding the ... || ... || ... part.

这篇关于Spring AOP中如何拦截元注解(annotated annotations)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆