在spring中,通过aopproxyutils.ultimatetargetclass()可获取被aop代理bean的真实目标类,该方法能穿透cglib/jdk多层代理,返回最内层原始类;若手动解包,需判断代理类型并处理advised接口,但推荐使用工具类。

在 Spring 中,当 Bean 被 AOP(如事务、日志等)代理后,通过 bean.getClass() 得到的通常是 CGLIB 生成的代理类或 JDK 动态代理类,而非原始的目标类。若需获取被代理的真实目标类(即 Target Class),不能直接用 getClass(),而要借助 Spring 提供的工具类。
使用 AopProxyUtils 获取原始目标类
AopProxyUtils 是 Spring 内置的工具类,专门用于处理代理对象,提供 ultimateTargetClass(Object) 方法,可穿透多层代理(CGLIB/JDK),返回最内层的目标类。
- 适用于 Spring 管理的 Bean(含 @Transactional、@Cacheable 等注解的类)
- 自动识别 CGLIB 代理(
Enhancer)、JDK 动态代理(Proxy)及 Spring 自定义代理 - 如果传入对象未被代理,直接返回其
getClass()
示例:
import org.springframework.aop.support.AopProxyUtils;
Object bean = applicationContext.getBean("userService");
Class> targetClass = AopProxyUtils.ultimateTargetClass(bean);
System.out.println(targetClass.getName()); // 输出 UserServiceImpl,而非 com.sun.proxy.$Proxy1 或 xxx$$EnhancerBySpringCGLIB$$xxx
```
手动判断并解包代理对象(不推荐,仅作理解)
若不想依赖 Spring 工具类,也可自行判断代理类型并提取目标类,但逻辑较繁琐且易遗漏边界情况:
- JDK 代理:调用
Proxy.getInvocationHandler(obj),再尝试从 handler 中获取 target(如Advised接口或自定义 handler) - CGLIB 代理:检查类名是否含
"$$EnhancerBySpringCGLIB$$",再通过obj.getClass().getSuperclass()获取父类(注意:仅当未重写getSuperclass()且目标类非 final 时有效) - 更可靠方式是强转为
Advised接口(Spring AOP 代理对象通常实现该接口):
```java
if (bean instanceof Advised) {
Object target = ((Advised) bean).getTargetSource().getTarget();
return target != null ? target.getClass() : null;
}
```
注意 Advised 接口的适用前提
只有 Spring AOP 创建的代理对象才一定实现 Advised 接口。若使用 AspectJ 编译期织入(@AspectJ with LTW)或第三方框架(如 Byte Buddy)代理,则该方式可能失效。
- 确认代理由 Spring AOP 生成(如启用
@EnableAspectJAutoProxy或 XML 配置 aop:config) -
getTargetSource().getTarget()可能返回null(如使用EmptyTargetSource) - 生产环境建议优先使用
AopProxyUtils.ultimateTargetClass(),它内部已兼容多种代理场景
获取 Class 后的典型用途
拿到原始目标类 Class 后,常用于:
- 反射调用私有方法或字段(避免代理类限制)
- 基于类名/包路径做权限或路由判断(如网关鉴权)
- 生成文档、监控埋点或序列化策略(需真实类型而非代理类型)
- 与 MyBatis、Hibernate 等框架集成时,确保类型匹配(如
MapperScannerConfigurer扫描原始类)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











