Spring AOP 核心类本身没有强制转换,因为它们仅在将方法调用委托给 AOP 代理时传递Objects。用户有责任从@Around 建议中返回正确的类型。但是,当然,有一个最终的演员表,你可以清楚地看到,如果你例如使通知返回除Object 或被拦截方法返回的实际类型以外的其他内容。然后你会看到如下异常:
Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class de.scrum_master.spring.q59783423.HoldResponse (java.lang.String is in module java.base of loader 'bootstrap'; de.scrum_master.spring.q59783423.HoldResponse is in unnamed module of loader 'app')
at de.scrum_master.spring.q59783423.HoldPaymentOrchestrationService$$EnhancerBySpringCGLIB$$13706881.execute(<generated>)
at de.scrum_master.spring.q59783423.MyApplication.doStuff(MyApplication.java:46)
at de.scrum_master.spring.q59783423.MyApplication.main(MyApplication.java:22)
在这个例子中,我让周围的建议错误地返回 String 而不是 HoldResponse。您可以看到 ClassCastException 出现在 Spring 生成的动态代理中,在本例中是 CGLIB 代理(也可以是 JDK 代理,具体取决于具体情况)。所以实际上发生了强制转换,否则不会有类强制转换异常。
当使用我在this answer 中描述的小代理转储代理的字节码时,您将看到如下内容:
public final doExecute(Lde/scrum_master/spring/q59783423/HoldRequest;)Lde/scrum_master/spring/q59783423/HoldResponse; throws de/scrum_master/spring/q59783423/PaymentServiceException
ALOAD 0
GETFIELD de/scrum_master/spring/q59783423/HoldPaymentOrchestrationService$$EnhancerBySpringCGLIB$$13706881.CGLIB$CALLBACK_0 : Lorg/springframework/cglib/proxy/MethodInterceptor;
DUP
IFNONNULL L0
POP
ALOAD 0
INVOKESTATIC de/scrum_master/spring/q59783423/HoldPaymentOrchestrationService$$EnhancerBySpringCGLIB$$13706881.CGLIB$BIND_CALLBACKS (Ljava/lang/Object;)V
ALOAD 0
GETFIELD de/scrum_master/spring/q59783423/HoldPaymentOrchestrationService$$EnhancerBySpringCGLIB$$13706881.CGLIB$CALLBACK_0 : Lorg/springframework/cglib/proxy/MethodInterceptor;
L0
DUP
IFNULL L1
ALOAD 0
GETSTATIC de/scrum_master/spring/q59783423/HoldPaymentOrchestrationService$$EnhancerBySpringCGLIB$$13706881.CGLIB$doExecute$0$Method : Ljava/lang/reflect/Method;
ICONST_1
ANEWARRAY java/lang/Object
DUP
ICONST_0
ALOAD 1
AASTORE
GETSTATIC de/scrum_master/spring/q59783423/HoldPaymentOrchestrationService$$EnhancerBySpringCGLIB$$13706881.CGLIB$doExecute$0$Proxy : Lorg/springframework/cglib/proxy/MethodProxy;
INVOKEINTERFACE org/springframework/cglib/proxy/MethodInterceptor.intercept (Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;Lorg/springframework/cglib/proxy/MethodProxy;)Ljava/lang/Object; (itf)
CHECKCAST de/scrum_master/spring/q59783423/HoldResponse
ARETURN
L1
ALOAD 0
ALOAD 1
INVOKESPECIAL de/scrum_master/spring/q59783423/HoldPaymentOrchestrationService.doExecute (Lde/scrum_master/spring/q59783423/HoldRequest;)Lde/scrum_master/spring/q59783423/HoldResponse;
ARETURN
MAXSTACK = 7
MAXLOCALS = 2
请特别注意
INVOKEINTERFACE org/springframework/cglib/proxy/MethodInterceptor.intercept (Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;Lorg/springframework/cglib/proxy/MethodProxy;)Ljava/lang/Object; (itf)
CHECKCAST de/scrum_master/spring/q59783423/HoldResponse
即,代理调用org.springframework.cglib.proxy.MethodInterceptor#intercept,然后将结果转换为被拦截方法的返回类型。这就是你的 Spring AOP 魔法。
顺便说一句,如果你想让你的周围建议更具体,你可以给它一个Object以外的返回类型。但是你需要在返回之前转换proceed() 的结果。在 Spring AOP 中,您负责声明与拦截的方法匹配的返回类型。在本机 AspectJ 中,声明特定的返回类型会自动缩小与返回类型兼容的连接点的匹配范围,这在 Spring AOP 中并非如此。