简体   繁体   中英

spring creates proxy for wrong classes when using aop class level annotation

When using spring AOP with class level annotations, spring context.getBean seems to always create and return a proxy or interceptor for every class, wether they have the annotation or not.

This behavior is only for class level annotation. For method level annotations, or execution pointcuts, if there is no need for interception, getBean returns a POJO.

Is this a bug? As designed? Or am i doing something wrong?

@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
      }
    }

Andy Brown is right, it is by design. The reason is that according to the AspectJ manual pointcut designators such as @args , @this , @target , @within , @withincode , and @annotation (or the subset of those available in Spring AOP) are used to match based on the presence of an annotation at runtime . This is why in the Spring debug log you see that proxies are created for all components which might need aspect functionality.

If you want to avoid that, you can refactor your aspect into something like this at the cost of an uglier pointcut and even uglier reflection in the advice code:

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();
  }
}

If neither the bean's class nor any of its methods bears the annotation, no proxy will be created. The advice detects both types of annotation, but prefers a method annotation if both are present.

Update: Instead of this workaround you could of course use full AspectJ from within Spring and avoid proxies altogether.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM