自定义注释的Spring AOP切入点在内部静态类中不起作用

问题描述 投票:0回答:2

目前,我有以下切入点。

@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一起使用,我将不胜感激。

java spring aop spring-aop
2个回答
2
投票

这不是答案,但是评论太有限,无法说出我想说的话。这实际上是对the OP's own answer的反馈:

    在Spring AOP中,
  • execution(* (@com.test.MyAnnotation *).*(..))也可以更可读地写为@within(com.test.MyAnnotation),因为Spring AOP始终只知道执行连接点。在AspectJ中,您可以将&& execution(* *(..))添加到切入点。

  • 在Spring AOP中,
  • execution(@com.test.MyAnnotation * *.*(..))也可以更可读地写为@annotation(com.test.MyAnnotation),因为Spring AOP始终只知道执行连接点。在AspectJ中,您可以将&& execution(* *(..))添加到切入点。

  • [我了解到,只有将methodInMyAnnotationType放到实际拥有该方法的类上,@MyAnnotation切入点才起作用。

    当然,因为这是Java注释的一般限制。它们从不继承给子类,从接口到类或方法,从父类方法到被覆盖的子类方法都不会被继承。唯一的例外是,如果您将@Inherited用作注释类型本身的元注释,则子类将继承它(但同样不是从接口到实现类)。已记录在here中。

  • 对于this() vs target()@this() vs @target,正如您所说的,“ this”版本仅受AspectJ支持(您也可以选择在Spring应用程序中使用)。原因是“ this”仅与call()切入点中的“ target”有所不同,其中“ this”是调用方法,而“ target”是被调用方法。因为call()在Spring AOP中也不可用,所以支持相应的“ this”类型切入点是没有意义的。

  • 如果您愿意切换到AspectJ,我有一种解决方法,可以通过接口实现类的“继承”注释,也可以使特定的方法也可以实现“继承”注释,请参见this answer

我只是出于教育目的而提及所有这些,并不是为了替换您自己的解决方案,因为您似乎对标记注释和标记界面的混合感到满意。


0
投票

深入研究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不支持生成@thisUnsupportedPointcutPrimitiveException切入点原语。

[进一步研究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;
}

附加信息:

我在实验中确实尝试了以下切入点。

@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 {}
}
© www.soinside.com 2019 - 2024. All rights reserved.