【问题标题】:Spring AOP @Around advice return typeSpring AOP @Around 建议返回类型
【发布时间】:2022-08-20 16:09:43
【问题描述】:

我正在学习 Spring AOP,我知道在 @Around 建议中我们使用 Object 返回类型,因为目标方法的返回值可以是任何类型。 但我的问题是返回值何时被向下转换为实际返回类型? 代理是否在将其发送到主要方法(调用目标方法的位置)之前对其进行了向下转换?

在主要 -

String result = account.getAccountHolderName();

在方面类 -

@Around(\"execution(* getAccountHolderName())\")
public Object myAroundAdvice(ProceedingJoinPoint joinPoint)  
{
   Object result = joinPoint.proceed();

   return result;
}

我想知道从建议作为对象返回后结果何时向下转换为 String 类

  • 我对此表示怀疑..你能edit你的问题并将示例代码添加为minimal reproducible example吗?还有,你试过了吗?发生了什么?
  • @Robert An MVCE 和“发生了什么”不适合“现有的建议代理基础设施如何工作?”的问题。
  • @chrylis-cautiouslyoptimistic- 为什么不呢? MCVE 以查看 OP 正在做什么。 \"会发生什么?\" 已经可以回答这个问题:\"Proxy 在将其发送到 main 方法之前是否对其进行了向下转换?\"
  • @Robert它显示字符串没有任何错误,所以这意味着代理将它从对象向下转换为字符串?
  • 我要么使用调试器并检查,要么打印类名。我的猜测是它保留了原始对象类型。不涉及铸造。例如,您可以执行 List<X> list = new ArrayList<X>();list 将始终是 ArrayList,即使编译器可能只允许您在其上调用 List 方法。如果切入点确实将所有内容都变成了Object,那么它会丢失信息,并且会破坏您的代码:当涉及到切入点时,您的代码的行为会有所不同,即使该切入点没有做任何事情。那会很奇怪。

标签: java spring types spring-aop


【解决方案1】:

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 中并非如此。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多