【问题标题】:spring creates proxy for wrong classes when using aop class level annotationspring 在使用 aop 类级别注释时为错误的类创建代理
【发布时间】:2019-03-30 05:57:49
【问题描述】:

当使用带有类级别注释的 spring AOP 时,spring context.getBean 似乎总是为每个类创建并返回一个代理或拦截器,无论它们是否具有注释。

此行为仅适用于类级别注释。对于方法级别的注解,或者执行切入点,如果不需要拦截,getBean返回一个POJO。

这是一个错误吗?按照设计?还是我做错了什么?

@Component
@Aspect
public class AspectA {

    @Around("@target(myAnnotation)")
    public Object process(ProceedingJoinPoint jointPoint, MyAnnotation myAnnotation) throws Throwable {
        System.out.println(
                "AspectA: myAnnotation target:" + jointPoint.getTarget().getClass().getSimpleName());
        System.out.println(" condition:" + myAnnotation.condition());
        System.out.println(" key:" + myAnnotation.key());
        System.out.println(" value:" + myAnnotation.value());
        return jointPoint.proceed();
    }
}




@Component("myBean2")
//@MyAnnotation(value="valtest-classLevel2", key="keytest-classLevel2", condition="contest-classLevel2")
 public class MyBean2 {
     public Integer testAspectCallInt(int i){
    System.out.println("MyBean2.testAspectCallInt(i=" + i + ")");
    return i+1000;
    }
}








@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MyAnnotation {
  String value()      default "";
  String key()         default "";
  String condition() default "";
}


@ComponentScan()
@EnableAspectJAutoProxy
public class Test {
      public static void main(String[] args) {
          ApplicationContext ctx =  new AnnotationConfigApplicationContext(Test.class);

          MyBean2 bean   = (MyBean2)ctx.getBean("myBean2");
          System.out.println(bean.getClass());  // prints CGLIB proxy, even when annotation is commented out on class

          bean.testAspectCallInt(12); // calling method
      }
    }

【问题讨论】:

  • 我确定这是设计使然。您是否希望访问代理背后的“真实”bean?如果是这样,请致电AopUtils.getTargetClass(proxyBean);
  • @AndyBrown 不,它会导致一些错误,例如 CGLIB 在尝试创建不相关的单例类的代理时失败,该类没有注解,但有一个私有构造函数。

标签: java spring aop spring-aop


【解决方案1】:

Andy Brown 是对的,这是设计使然。原因是根据AspectJ manual 切入点指示符,例如@args@this@target@within@withincode@annotation(或Spring AOP 中可用的那些子集)是用于根据 运行时 中存在的注释进行匹配。这就是为什么在 Spring 调试日志中您会看到为所有可能需要方面功能的组件创建了代理。

如果你想避免这种情况,你可以将你的切面重构为这样的东西,代价是在建议代码中的切入点更丑,甚至更丑:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;

@Component
@Aspect
public class AspectA {
  @Around("execution(* (@MyAnnotation *).*(..)) || execution(@MyAnnotation * *(..))")
  public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
    MyAnnotation myAnnotation = null;
    for (Annotation annotation : ((MethodSignature) joinPoint.getSignature()).getMethod().getDeclaredAnnotations()) {
      if (annotation instanceof MyAnnotation) {
        myAnnotation = (MyAnnotation) annotation;
        break;
      }
    }
    if (myAnnotation == null) {
      myAnnotation = joinPoint.getTarget().getClass().getAnnotationsByType(MyAnnotation.class)[0];
    }
    System.out.println("AspectA: myAnnotation target:" + joinPoint.getTarget().getClass().getSimpleName());
    System.out.println(" condition:" + myAnnotation.condition());
    System.out.println(" key:" + myAnnotation.key());
    System.out.println(" value:" + myAnnotation.value());
    return joinPoint.proceed();
  }
}

如果 bean 的类和它的任何方法都没有注解,则不会创建代理。建议检测这两种类型的注释,但如果两者都存在,则更喜欢方法注释。

更新:您当然可以在 Spring 中使用完整的 AspectJ 并完全避免代理,而不是这种解决方法。

【讨论】:

  • 谢谢。但是在使用 @Around("@annotation(MyAnnotation)") 时,对于方法级别的注解,只有当注解实际出现在某个类方法上时,getBean 才会返回代理,否则返回原始 POJO。奇怪的是,一旦您定义了任何类级别的注释建议,所有类的 getBean,无论是否具有注释,都将开始返回代理。
  • 嗯,Spring AOP 使用启发式,即所谓的 AspectJ “快速匹配”特性,这里是为了避免切入点匹配的运行时开销。有时快速匹配会返回“可能”,这被解释为“是”,因此您可能会在那里得到一些误报。检查例如this similar issue 和 AspectJ 邮件列表线程的相关链接。顺便说一句,我实际上通过它进行了调试,发现快速匹配是您的问题的原因。
  • 你的回答救了我!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多