Gon*_*n I 2 java aop spring spring-aop
当使用带有类级别注释的 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
}
}
Run Code Online (Sandbox Code Playgroud)
安迪布朗是对的,这是设计使然。其原因是,根据所述的AspectJ手册切入点指示符如 @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();
}
}
Run Code Online (Sandbox Code Playgroud)
如果 bean 的类及其任何方法都没有注释,则不会创建代理。该建议检测两种类型的注释,但如果两者都存在,则更喜欢方法注释。
更新:您当然可以在 Spring 中使用完整的 AspectJ 并完全避免使用代理,而不是这种解决方法。