此刻,我有以下切入点。
@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
*/
}
如果我在内部静态类上添加注释,它也将无法正常工作。
@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一起使用,我将不胜感激。
答案 0 :(得分:2)
深入研究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;
}
}
我了解到,methodInMyAnnotationType
切入点只有在我将@MyAnnotation
放在实际拥有该方法的类上时才能起作用。但是,如果将注释放在扩展了类A的类B上,则AOP无法截获类A的方法。
我发现一个可能的解决方案如下。
@Pointcut("execution(* *(..)) && @this(com.test.MyAnnotation)")
这意味着切入点适用于当前类和父类中的所有方法,并且当前类必须使用@MyAnnotation
进行注释。看起来很有希望。不幸的是,Spring AOP不支持生成@this
的{{1}}切入点原语。
在进一步研究UnsupportedPointcutPrimitiveException
的主题之后,我发现了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;
}
注释,或者包含该方法的类必须用@MyAnnotation
注释,或者具有该方法的对象必须是标记接口@MyAnnotation
的实例。看起来不错,可以正常工作。
我的最终课程实现看起来像这样。
MyAnnotable
附加信息:
在实验期间,我确实尝试了以下切入点。
@Service
public class ShiftModule {
@Service
public class Annotated extends ShiftModule implements MyAnnotable {}
@Resource
private ShiftModule.Annotated self;
}
我发现它不适用于带注释的内部接口,这意味着下面的代码将停止工作。 AOP方面完全没有任何作用。
@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;
}
答案 1 :(得分:2)
这不是答案,但是评论太有限,无法说出我想说的话。这实际上是对the OP's own answer的反馈:
execution(* (@com.test.MyAnnotation *).*(..))
也可以更容易地写为@within(com.test.MyAnnotation)
,因为Spring AOP始终只知道执行连接点。在AspectJ中,您可以将&& execution(* *(..))
添加到切入点。
execution(@com.test.MyAnnotation * *.*(..))
也可以更容易地写为@annotation(com.test.MyAnnotation)
,因为Spring AOP始终只知道执行连接点。在AspectJ中,您可以将&& execution(* *(..))
添加到切入点。
我了解到,
methodInMyAnnotationType
切入点只有在将@MyAnnotation
放到实际拥有该方法的类上时才能起作用。
当然,因为这是Java注释的一般限制。它们从不继承给子类,从接口到类或方法,从父类方法到被覆盖的子类方法都不会继承。唯一的例外是,如果您将@Inherited
用作注释类型本身的元注释,则子类会继承它(但同样不是从接口到实现类)。记录在here中。
对于this()
vs target()
和@this()
vs @target
,正如您所说的,AspectJ仅支持“ this”版本(您可以选择也可以在Spring应用程序中使用)。原因是在call()
切入点中,“ this”仅与“ target”有所不同,其中“ this”是调用方法,而“ target”是被调用方法。由于call()
在Spring AOP中也不可用,因此没有必要支持相应的“ this”类型切入点。
如果您愿意切换到AspectJ,我有一种变通办法,可以从接口实现类“继承”注释,也可以使特定方法“继承”注释,请参见this answer。
我只是出于教育目的而提及所有这些,并不是为了替换您自己的解决方案,因为您似乎对标记注释和标记界面的混合感到满意。