Spring AOP:有没有办法让@target 用于间接注释?
Spring AOP: is there a way to make @target work for indirect annotations?
我有注释:
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
public @interface MyAnnotation {
}
我用它注释 Spring MVC 控制器:
@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
切入点停止匹配我的控制器,不建议使用它们。
有没有办法定义一个切入点来匹配具有此类间接注释的控制器?
我用纯 AspectJ 重现了这种情况,因为我不太喜欢 Spring AOP。这就是为什么我在建议的切入点前面添加了一个额外的 execution(* *(..)) &&
以避免匹配 Spring AOP 中不可用的其他连接点,例如 call()
。如果您愿意,可以在 Spring AOP 中删除它。
好的,让我们按照您的描述创建这种情况:
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");
}
}
这是我们的纯 Java 驱动程序应用程序(没有 Spring):
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();
}
}
然后切入点将不再匹配嵌套的 meta/stereotype 注释:
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)
添加到切入点,即我们必须知道层次结构中的所有注释 class 名称。有一种方法可以使用 within()
切入点指示符的特殊语法来克服这个问题,另请参阅 :
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
看到了吗?我们只需要知道一个注解class,即MyAnnotation
,就可以覆盖两层嵌套的元注解。添加更多级别会很简单。我承认这种注解嵌套看起来很做作,我只是想向你解释一下你有哪些选择。
我有注释:
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
public @interface MyAnnotation {
}
我用它注释 Spring MVC 控制器:
@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
切入点停止匹配我的控制器,不建议使用它们。
有没有办法定义一个切入点来匹配具有此类间接注释的控制器?
我用纯 AspectJ 重现了这种情况,因为我不太喜欢 Spring AOP。这就是为什么我在建议的切入点前面添加了一个额外的 execution(* *(..)) &&
以避免匹配 Spring AOP 中不可用的其他连接点,例如 call()
。如果您愿意,可以在 Spring AOP 中删除它。
好的,让我们按照您的描述创建这种情况:
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");
}
}
这是我们的纯 Java 驱动程序应用程序(没有 Spring):
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();
}
}
然后切入点将不再匹配嵌套的 meta/stereotype 注释:
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)
添加到切入点,即我们必须知道层次结构中的所有注释 class 名称。有一种方法可以使用 within()
切入点指示符的特殊语法来克服这个问题,另请参阅
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
看到了吗?我们只需要知道一个注解class,即MyAnnotation
,就可以覆盖两层嵌套的元注解。添加更多级别会很简单。我承认这种注解嵌套看起来很做作,我只是想向你解释一下你有哪些选择。