Spring知识梳理(5)
2026/7/18 16:23:00 网站建设 项目流程

Spring知识梳理(5)

作者:没有四次元口袋的蓝胖
日期:2026-07-17
标签:Java, Spring, 动态代理, JDK代理, CGLIB


一、为什么需要动态代理?

AOP 的底层就是动态代理。不修改原始类的代码,在运行时生成一个代理对象,在方法调用前后插入增强逻辑。

原始对象:UserService(只有业务逻辑) 代理对象:UserService$Proxy(业务逻辑 + 日志 + 事务 + 权限...) 调用方拿到的永远是代理对象,无感知地享受增强功能。

Spring 提供了两种代理方式:JDK 动态代理CGLIB 代理


二、JDK 动态代理(必须手写)

2.1 前提条件

目标类必须实现至少一个接口

2.2 核心类

  • java.lang.reflect.Proxy—— 生成代理对象
  • java.lang.reflect.InvocationHandler—— 定义增强逻辑

2.3 完整代码

// 1. 定义接口publicinterfaceUserService{voidsave();Stringquery(Stringname);}// 2. 目标类实现接口publicclassUserServiceImplimplementsUserService{@Overridepublicvoidsave(){System.out.println("保存用户");}@OverridepublicStringquery(Stringname){System.out.println("查询用户: "+name);return"用户: "+name;}}// 3. 编写 InvocationHandler(增强逻辑)publicclassLogInvocationHandlerimplementsInvocationHandler{privatefinalObjecttarget;// 持有原始对象的引用publicLogInvocationHandler(Objecttarget){this.target=target;}@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{// ===== 前置增强 =====System.out.println("【日志】开始执行: "+method.getName());System.out.println("【日志】参数: "+Arrays.toString(args));// ===== 调用目标方法 =====Objectresult=method.invoke(target,args);// ↑ 这里的 method.invoke(target, args) 就是反射调用原始对象的方法// ===== 后置增强 =====System.out.println("【日志】返回值: "+result);System.out.println("【日志】执行完毕: "+method.getName());returnresult;// 返回原始结果(也可以修改后返回)}}// 4. 生成代理对象publicclassProxyFactory{publicstaticObjectcreateProxy(Objecttarget){returnProxy.newProxyInstance(target.getClass().getClassLoader(),// 类加载器target.getClass().getInterfaces(),// 目标类实现的所有接口newLogInvocationHandler(target)// 增强逻辑处理器);}}// 5. 使用publicclassMain{publicstaticvoidmain(String[]args){UserServiceuserService=newUserServiceImpl();// 原始对象UserServiceproxy=(UserService)ProxyFactory.createProxy(userService);// 代理对象proxy.save();// 输出:// 【日志】开始执行: save// 【日志】参数: []// 保存用户// 【日志】返回值: null// 【日志】执行完毕: saveproxy.query("张三");// 输出:// 【日志】开始执行: query// 【日志】参数: [张三]// 查询用户: 张三// 【日志】返回值: 用户: 张三// 【日志】执行完毕: query}}

2.4 关键点总结

Proxy.newProxyInstance(classLoader, interfaces, handler) │ │ │ │ │ └── 增强逻辑写在这里 │ └── 代理对象实现这些接口 └── 用哪个类加载器加载
  • 代理对象和原始对象实现相同的接口,对调用方透明
  • invoke()中的method.invoke(target, args)是反射调用原始对象
  • 一个InvocationHandler可以代理任何实现了接口的对象

三、CGLIB 代理

3.1 前提条件

不需要接口,通过生成目标类的子类来实现代理。

3.2 核心类

  • org.springframework.cglib.proxy.Enhancer—— 生成代理对象
  • org.springframework.cglib.proxy.MethodInterceptor—— 定义增强逻辑

3.3 完整代码

// 1. 目标类(不需要接口!)publicclassOrderService{publicvoidcreateOrder(){System.out.println("创建订单");}publicStringgetOrder(intid){System.out.println("查询订单: "+id);return"订单#"+id;}}// 2. 编写 MethodInterceptor(增强逻辑)publicclassLogMethodInterceptorimplementsMethodInterceptor{@OverridepublicObjectintercept(Objectobj,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable{// ===== 前置增强 =====System.out.println("【日志】开始执行: "+method.getName());// ===== 调用目标方法 =====// 注意:这里用 invokeSuper,不是 invoke!Objectresult=proxy.invokeSuper(obj,args);// ===== 后置增强 =====System.out.println("【日志】执行完毕: "+method.getName());returnresult;}}// 3. 生成代理对象Enhancerenhancer=newEnhancer();enhancer.setSuperclass(OrderService.class);// 设置父类enhancer.setCallback(newLogMethodInterceptor());// 设置增强逻辑OrderServiceproxy=(OrderService)enhancer.create();// 4. 使用proxy.createOrder();proxy.getOrder(1);

3.4 CGLIB 的本质

OrderService(原始类) ↑ │ 继承 │ OrderService$$EnhancerByCGLIB(代理类) - 重写了所有非 final 方法 - 每个方法内部先执行增强逻辑,再调用 super.method() 执行原始逻辑

四、JDK vs CGLIB 全面对比(面试必背)

维度JDK 动态代理CGLIB 代理
前提目标类必须实现接口不需要接口
底层机制java.lang.reflect.ProxyCGLIB 库,基于 ASM 字节码框架
代理方式接口代理(实现相同接口)子类继承(生成子类重写方法)
核心接口InvocationHandler.invoke()MethodInterceptor.intercept()
调用目标method.invoke(target, args)proxy.invokeSuper(obj, args)
性能JDK 8+ 性能已很好生成代理类稍慢,调用性能略快
限制只能代理接口方法不能代理 final 类 / final 方法
依赖JDK 自带,无需额外依赖需要引入 CGLIB 库
Spring 默认Spring Boot 1.x 默认Spring Boot 2.x+ 默认

代码对比速记

JDK 动态代理 CGLIB 代理 ───────────── ───────────── 需要接口 不需要接口 Proxy.newProxyInstance() Enhancer.create() InvocationHandler.invoke() MethodInterceptor.intercept() 反射调用 method.invoke(target) 继承调用 proxy.invokeSuper(obj) 只能代理接口方法 能代理所有非 final 方法 不能代理 final 类 不能代理 final 类/final 方法

五、Spring 如何选择代理方式?

目标类实现了接口? ├── 是 → 默认用 JDK 动态代理 │ (Spring Boot 2.x+ 可通过配置强制用 CGLIB) └── 否 → 必须用 CGLIB

Spring Boot 2.x+ 默认使用 CGLIB 的原因

  1. @Configuration类通常没有接口,需要 CGLIB 代理
  2. 统一使用 CGLIB 可以避免 JDK 代理的一些边界问题
  3. CGLIB 调用性能略优

配置方式

# application.ymlspring:aop:proxy-target-class:true# 强制使用 CGLIB# proxy-target-class: false # 有接口用 JDK,没接口用 CGLIB

六、为什么 CGLIB 不能代理 final?

这是面试高频追问,原理很简单:

CGLIB 的本质:生成目标类的子类,重写方法来插入增强逻辑 class OrderService$$EnhancerByCGLIB extends OrderService { @Override public void createOrder() { // 重写父类方法 // 增强逻辑 super.createOrder(); // 调用父类原始方法 // 增强逻辑 } }
  • final类 → 不能被继承 → 无法生成子类 →不能代理
  • final方法 → 不能被重写 → 无法插入增强逻辑 →不能代理
  • private方法 → 子类不可见 →不能代理

实际踩坑:如果 Service 类不小心标了final,AOP 直接失效,而且不会报错(方法会静默跳过增强),排查很痛苦。


七、思维导图速览

动态代理(AOP 的底层) ├── JDK 动态代理 │ ├── 前提:目标类实现接口 │ ├── 核心:Proxy + InvocationHandler │ ├── invoke() 中写增强逻辑 │ ├── method.invoke(target, args) 反射调用 │ └── JDK 自带,无需额外依赖 │ ├── CGLIB 代理 │ ├── 前提:不需要接口 │ ├── 核心:Enhancer + MethodInterceptor │ ├── intercept() 中写增强逻辑 │ ├── proxy.invokeSuper(obj, args) 继承调用 │ ├── 本质:生成子类,重写方法 │ ├── 限制:不能代理 final 类/方法 │ └── Spring Boot 2.x+ 默认使用 │ └── Spring 选择策略 ├── 有接口 → 默认 JDK(可配置强制 CGLIB) └── 无接口 → 必须 CGLIB

八、写在最后

面试回答模板

“Spring AOP 的底层是动态代理。如果目标类实现了接口,默认用 JDK 动态代理,通过 Proxy + InvocationHandler 实现,在 invoke() 方法中用反射调用原始对象的方法。如果没有接口,用 CGLIB 生成子类来代理,通过 MethodInterceptor 的 intercept() 方法拦截调用,用 invokeSuper 调用父类方法。CGLIB 不能代理 final 类和 final 方法,因为它本质是生成子类重写方法。Spring Boot 2.x 之后默认用 CGLIB。”

学习建议

  1. JDK 动态代理代码要能手写,面试可能让白板写
  2. CGLIB理解原理即可,重点记住"不能代理 final"及原因

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询