[英]Spring AOP Pointcut expression for custom annotation in subclass
[英]Spring AOP pointcut for custom annotation not working inner static class
目前,我有以下切入点。
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
@Aspect
@Component
public static class MyAnnotationAspect {
@Pointcut("execution(* (@com.test.MyAnnotation *).*(..))")
public void methodInMyAnnotationType() {}
@Around("methodInMyAnnotationType()")
public Object annotate(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("AOP WORKING");
return pjp.proceed();
}
}
}
当我在根级类上添加@MyAnnotation
时,它工作正常,如下所示。
@MyAnnotation
@Service
public class ShiftModule {
@Resource
private ShiftModule self;
/* Executing anything using self.method() triggers the Aspect
* for @MyAnnotation perfectly
*/
}
如果我在内部 static class 上添加注释,它也不起作用。
@Service
public class ShiftModule {
@Service
@MyAnnotation
public class AnnotatedShiftModule extends ShiftModule {}
@Resource
private AnnotatedShiftModule self;
/* Executing anything using self.method() does NOT trigger the
* Aspect for @MyAnnotation or even framework's annotations
* like @Async
*/
}
如果我在界面上使用这种技术,它就可以工作。
@Repository
public interface OrderRepo extends JpaRepository<Order,Long> {
@Repository("annotatedOrderRepo")
@MyAnnotation
public interface AnnotatedOrderRepo extends OrderRepo {}
}
如果您能告诉我如何使它与类和 Spring bean 一起工作,我将不胜感激。
在深入研究了 AOP 的主题之后,我终于找到了一个可行的解决方案。
最初,我使用以下切入点。
@Aspect
@Component
public static class MyAnnotationAspect {
/**
* Matches the execution of any methods in a type annotated with @MyAnnotation.
*/
@Pointcut("execution(* (@com.test.MyAnnotation *).*(..))")
public void methodInMyAnnotationType() {}
/**
* Matches the execution of any methods annotated with @MyAnnotation.
*/
@Pointcut("execution(@com.test.MyAnnotation * *.*(..))")
public void methodAnnotatedWithMyAnnotation() {}
@Around("methodInMyAnnotationType() || methodAnnotatedWithMyAnnotation()")
public Object aop(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("AOP IS WORKING");
return pjp.proceed;
}
}
我了解到,只有在我将@MyAnnotation
放在实际拥有该方法的 class 上时, methodInMyAnnotationType
切入点才有效。 但是,如果我将注释放在扩展 class A 的 class B 上,则 AOP 无法拦截来自 class A 的方法。
我发现的一种潜在解决方案如下。
@Pointcut("execution(* *(..)) && @this(com.test.MyAnnotation)")
这意味着切入点适用于当前 class 和父 class 的所有方法,并且当前 class 必须使用@MyAnnotation
注释。 看起来很有希望。 不幸的是,Spring AOP 不支持产生UnsupportedPointcutPrimitiveException
的@this
切入点原语。
在深入研究了this
主题之后,我发现了target
原语的存在,并提出了以下解决方案。
@Pointcut("execution(@com.test.MyAnnotation * *.*(..))")
public void annotatedMethod() {}
@Pointcut("execution(* (@com.test.MyAnnotation *).*(..))")
public void annotatedClass() {}
@Pointcut("execution(* *(..)) && target(com.test.MyAnnotable)")
public void implementedInterface() {}
@Around("annotatedMethod() || annotatedClass() || implementedInterface()")
public Object aop(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("AOP IS WORKING");
return pjp.proceed;
}
这意味着切入点适用于当前 class 和父 class 的所有方法。 此外,该方法必须使用@MyAnnotation
注释或包含该方法的class 使用@MyAnnotation
注释或具有此方法的object 必须是标记接口MyAnnotable
的实例。 它看起来不错并且有效。
我最终的 class 实现看起来像这样。
@Service
public class ShiftModule {
@Service
public class Annotated extends ShiftModule implements MyAnnotable {}
@Resource
private ShiftModule.Annotated self;
}
附加信息:
在我的实验过程中,我确实尝试了以下切入点。
@Pointcut("@annotation(com.test.MyAnnotation)")
public void annotatedMethod() {}
@Pointcut("@within(com.test.MyAnnotation)")
public void annotatedClass() {}
@Pointcut("target(com.test.MyAnnotable)")
public void implementedInterface() {}
@Around("execution(* *(..)) && (annotatedMethod() || annotatedClass() || implementedInterface()")
public Object aop(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("AOP IS WORKING");
return pjp.proceed;
}
我发现它不适用于带注释的内部界面,这意味着下面的代码将停止工作。 AOP 方面根本没有任何影响。
@Repository
public interface OrderRepo extends JpaRepository<Order,Long> {
@Repository("annotatedOrderRepo")
@MyAnnotation
public interface Annotated extends OrderRepo {}
}
这不是答案,但评论太有限,无法说出我想说的话。 这实际上是对 OP 自己的回答的反馈:
execution(* (@com.test.MyAnnotation *).*(..))
也可以在 Spring AOP 中写成@within(com.test.MyAnnotation)
,因为 Spring AOP 只知道执行连接点。 在 AspectJ 中,您可以将&& execution(* *(..))
添加到切入点。
execution(@com.test.MyAnnotation * *.*(..))
也可以写成 Spring AOP 中的@annotation(com.test.MyAnnotation)
更具可读性,因为 Spring AOP 无论如何都只知道执行连接点。 在 AspectJ 中,您可以将&& execution(* *(..))
添加到切入点。
我了解到,只有在我将
@MyAnnotation
放在实际拥有该方法的 class 上时,methodInMyAnnotationType
切入点才有效。
当然,因为这是 Java 注解的一般限制。 它们永远不会被继承到子类,从接口到类或方法,或者从父 class 方法到覆盖的子类方法。 唯一的例外是,如果您使用@Inherited
作为注释类型本身的元注释,那么它会被子类继承(但同样不是从接口到实现类)。 这在此处记录。
至于this()
vs target()
和@this()
vs @target
,正如您所说,“this”版本仅受 AspectJ 支持(您也可以选择在 Spring 应用程序中使用它)。 原因是“this”仅与call()
切入点中的“target”有所不同,其中“this”是调用方法,“target”是被调用方法。 因为call()
在 Spring AOP 中也不可用,所以支持相应的“this”类型切入点是没有意义的。
如果您愿意切换到 AspectJ,我有一个解决方法,可以让实现类从接口“继承”注释,也可以让特定方法“继承”注释,请参阅这个答案。
我只是出于教育目的提及所有这些,而不是为了替换您自己的解决方案,因为您似乎对标记注释和标记界面的组合感到满意。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.