当前位置: 首页 > 知识库问答 >
问题:

spring在使用aop类级注释时为错误的类创建代理

云镜
2023-03-14

当使用带有类级注释的Spring AOP时,Springcontext.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
  }
}

共有1个答案

梁丘书
2023-03-14

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

如果你想避免这种情况,你可以将你的方面重构成这样,代价是在建议代码中使用更丑陋的切入点,甚至更丑陋的反射:

java prettyprint-override">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,完全避免代理。

 类似资料: