bigywater.tech
Spring · 28

AopContext 与 AopUtils:拿到原始对象

怎么从代理对象拿回原始对象?代理类上找不到注解怎么办?代理和目标 equals 吗?多个切面叠加是几层代理?这篇在 Spring Framework 7.0.9 上逐项验证。

4 个
两个切面加 @Async 作用在同一个 Bean 上时,同一个代理里的 advisor 个数(只有 1 层代理)
null
CGLIB 代理类上直接 getAnnotation 目标类注解的结果(findAnnotation 能找到)
false
代理 equals 目标(JDK 与 CGLIB 都是)

拿回原始对象

实测 · 本机执行输出
  JDK 代理:getClass() = $Proxy2;AopUtils.getTargetClass = JdkTarget;AopProxyUtils.ultimateTargetClass = JdkTarget;ClassUtils.getUserClass = $Proxy2
    ((Advised) proxy).getTargetSource().getTarget() = JdkTarget;proxy == target ? false;isAopProxy(target) = false
  CGLIB 代理:getClass() = CglibTarget·CGLIB;AopUtils.getTargetClass = CglibTarget;AopProxyUtils.ultimateTargetClass = CglibTarget;ClassUtils.getUserClass = CglibTarget
    ((Advised) proxy).getTargetSource().getTarget() = CglibTarget;proxy == target ? false;isAopProxy(target) = false
  两层代理:最外层 → getTarget() 得到 $Proxy2(还是个代理:true);AopProxyUtils.getSingletonTarget(最外层) = $Proxy2;ultimateTargetClass = JdkTarget;AopUtils.getTargetClass = $Proxy2
    getSingletonTarget 连续两次 → JdkTarget

getTargetClass、ultimateTargetClass、getTarget() 各有适用范围,多层代理要用 ultimate 系列。

找注解

实测 · 本机执行输出
  CGLIB 代理 CglibTarget·CGLIB:pc.getAnnotation(Tag.class) = null;pc.isAnnotationPresent(Tag) = false
    AnnotationUtils.findAnnotation(pc, Tag.class) = @Tag(on-class);AnnotatedElementUtils.findMergedAnnotation = @Tag(on-class);getAnnotation(AopUtils.getTargetClass(p)) = @demo.Main.Tag("on-class")
  JDK 代理 $Proxy2:jc.getAnnotation(Tag.class) = null;findAnnotation(jc) = null;findAnnotation(AopUtils.getTargetClass(j)) = @Tag(on-class)

代理类上直接 getAnnotation 会漏掉类注解,要用 findAnnotation 或目标类。

身份比较

实测 · 本机执行输出
  CGLIB 代理:p1.equals(p1) = true;p1.equals(target) = false;target.equals(p1) = false;p1.hashCode() == target.hashCode() ? false;p1.toString() 以 CglibTarget 开头?true
  JDK 代理:j1.equals(j1) = true;j1.equals(target) = false;j1.hashCode() == target.hashCode() ? false;HashSet 里放 target 再用 j1 查 contains = false

代理和目标不相等,混放在集合里会查不到。

一层还是多层

实测 · 本机执行输出
  同时有两个切面 A1、A2 和 @Async 作用在 CglibTarget 上:Bean 是代理?true;Advised.getAdvisors().length = 4;advisors = [AnnotationAsyncExecutionInterceptor, ExposeInvocationInterceptor, AspectJAroundAdvice, AspectJAroundAdvice]
    getTarget() = CglibTarget(是代理:false);说明多个切面和 @Async 被合并进了同一个代理对象的拦截链,不是一层套一层

多个切面与 @Async 合并进同一个代理的拦截器链。

分步演示

代理与原始对象
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。用不带 Boot 的 Spring Framework 7.0.9 + AspectJ 注解;前三节的代理是手工用 ProxyFactory 创建的(行为与容器创建的代理一致,第四节是容器创建的);nm() 把代理类名里的序号归一化成 ·CGLIB
    • 没有验证:scoped proxy 的 getTarget();AopTestUtils(spring-test)里的 getUltimateTargetObject;Hibernate 代理与 Spring 代理叠加;@Inherited 注解在 CGLIB 代理上的行为;ProxyFactory 的 frozen 与 exposeProxy;序列化代理
    京ICP备2026067502号-1京公网安备11010502063487号