在 Java 开发中,动态代理是实现 AOP(面向切面编程)、事务管理、日志增强等核心功能的基石,也是面试高频考点。它能在不修改原始类代码的前提下,为目标对象动态添加额外功能,完美遵循开闭原则。
Java 中主流的动态代理实现有两种:
JDK 动态代理:Java 原生提供,基于接口实现
CGLIB 动态代理:第三方库,基于继承实现
本文将从原理、代码实战、核心区别、应用场景四个维度,彻底讲透两种动态代理。
一、代理模式基础
动态代理是代理模式的动态实现,先回顾代理模式的核心:
目标对象:实际执行业务逻辑的对象(被代理对象)
代理对象:拦截目标对象的方法调用,添加额外功能
核心作用:方法增强(日志、事务、权限校验等)、方法拦截
静态代理需要手动为每个目标类编写代理类,代码冗余且难以维护;动态代理在程序运行时,通过反射/字节码技术自动生成代理类,无需手动编写,灵活性极高。
二、JDK 动态代理(原生、基于接口)
1. 核心原理
JDK 动态代理是 Java 原生 API(java.lang.reflect 包),必须基于接口实现:
目标类必须实现至少一个接口
代理类和目标类实现同一个接口
通过
InvocationHandler接口拦截方法调用,通过反射执行目标方法运行时动态生成代理类字节码,无需手动创建
2. 核心 API
InvocationHandler:调用处理器,重写invoke()方法实现方法增强Proxy:工具类,通过newProxyInstance()方法生成代理对象
3. 代码实战
步骤1:定义业务接口(JDK 代理必须)
// 用户业务接口
public interface UserService {
// 目标方法:添加用户
void addUser(String username);
}
步骤2:实现目标类
// 目标类:实现接口
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("执行核心业务:添加用户 -> " + username);
}
}
步骤3:自定义 InvocationHandler(增强逻辑)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
// 调用处理器:实现方法增强
public class CustomInvocationHandler implements InvocationHandler {
// 目标对象(被代理对象)
private final Object target;
// 构造方法注入目标对象
public CustomInvocationHandler(Object target) {
this.target = target;
}
/**
* 拦截目标对象的所有方法调用
* @param proxy 代理对象
* @param method 被调用的目标方法
* @param args 方法参数
* @return 方法返回值
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 【前置增强】:方法执行前添加逻辑
System.out.println("===== 前置增强:日志记录开始 =====");
System.out.println("调用方法:" + method.getName());
// 执行目标方法(反射调用)
Object result = method.invoke(target, args);
// 【后置增强】:方法执行后添加逻辑
System.out.println("===== 后置增强:日志记录结束 =====");
return result;
}
}
步骤4:生成代理对象并测试
import java.lang.reflect.Proxy;
public class JdkProxyTest {
public static void main(String[] args) {
// 1. 创建目标对象
UserService target = new UserServiceImpl();
// 2. 创建调用处理器
CustomInvocationHandler handler = new CustomInvocationHandler(target);
// 3. 生成代理对象(核心方法)
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类类加载器
target.getClass().getInterfaces(), // 目标类实现的接口
handler // 调用处理器
);
// 4. 调用代理对象的方法(触发增强逻辑)
proxy.addUser("张三");
}
}
输出结果
===== 前置增强:日志记录开始 =====
调用方法:addUser
执行核心业务:添加用户 -> 张三
===== 后置增强:日志记录结束 =====
4. 优缺点
✅ 优点:
Java 原生支持,无需引入第三方依赖
无额外性能开销,稳定性高
❌ 缺点:
强制要求目标类实现接口,无接口的类无法代理
只能代理接口中定义的方法,无法代理类的私有/静态方法
三、CGLIB 动态代理(第三方、基于继承)
1. 核心原理
CGLIB(Code Generation Library)是第三方字节码生成库,无需目标类实现接口:
底层通过继承目标类生成代理子类
重写目标类的非 final 方法,实现方法拦截
通过
MethodInterceptor接口拦截方法调用运行时动态生成字节码,效率高于 JDK 动态代理(高版本)
注意:Spring 框架已内置 CGLIB,无需单独引入;普通项目需引入依赖。
2. 核心 API
MethodInterceptor:方法拦截器,重写intercept()方法实现增强Enhancer:增强器,用于生成代理子类
3. 代码实战
步骤1:引入依赖(Maven)
<!-- CGLIB 依赖 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
步骤2:定义目标类(无需实现接口)
// 普通类:无接口,CGLIB 可直接代理
public class OrderService {
// 目标方法:创建订单
public void createOrder(String orderNo) {
System.out.println("执行核心业务:创建订单 -> " + orderNo);
}
}
步骤3:自定义 MethodInterceptor(拦截增强)
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 方法拦截器:CGLIB 核心增强类
public class CustomMethodInterceptor implements MethodInterceptor {
/**
* 拦截目标类的所有方法调用
* @param obj 代理对象(子类)
* @param method 被调用的目标方法
* @param args 方法参数
* @param methodProxy 方法代理(用于快速调用父类方法)
* @return 方法返回值
* @throws Throwable 异常
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 【前置增强】
System.out.println("===== CGLIB 前置增强:权限校验 =====");
// 执行目标方法(调用父类方法,比反射更快)
Object result = methodProxy.invokeSuper(obj, args);
// 【后置增强】
System.out.println("===== CGLIB 后置增强:事务提交 =====");
return result;
}
}
步骤4:生成代理对象并测试
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyTest {
public static void main(String[] args) {
// 1. 创建目标对象
OrderService target = new OrderService();
// 2. 创建拦截器
CustomMethodInterceptor interceptor = new CustomMethodInterceptor();
// 3. 创建 Enhancer 增强器(CGLIB 核心)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass()); // 设置父类(目标类)
enhancer.setCallback(interceptor); // 设置拦截器
// 4. 生成代理对象(子类)
OrderService proxy = (OrderService) enhancer.create();
// 5. 调用代理方法
proxy.createOrder("ORDER_001");
}
}
输出结果
===== CGLIB 前置增强:权限校验 =====
执行核心业务:创建订单 -> ORDER_001
===== CGLIB 后置增强:事务提交 =====
4. 优缺点
✅ 优点:
无需目标类实现接口,适用范围更广
可代理任意普通类,性能优于 JDK 动态代理
能代理类的公有方法(非 final)
❌ 缺点:
需引入第三方依赖(Spring 已内置)
无法代理 final 类、final 方法、static 方法(继承限制)
会生成目标类的子类,可能影响类结构
四、JDK 动态代理 VS CGLIB 动态代理(核心对比)
| 维度 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|
| 实现原理 | 基于接口,实现同一接口 | 基于继承,生成子类 |
| 目标类要求 | 必须实现至少一个接口 | 无需实现接口,普通类即可 |
| 代理限制 | 只能代理接口方法 | 不能代理 final 类/方法 |
| 依赖 | Java 原生,无第三方依赖 | 需引入 CGLIB 依赖 |
| 性能 | 较低(反射调用) | 较高(字节码生成,快速调用) |
| Spring 默认选择 | Spring Boot 2.0+ 新版本默认统一使用 CGLIB,无论目标类是否实现接口 | 目标类无接口时使用 |
五、动态代理的应用场景
动态代理是 Java 框架的核心技术,实际开发中无处不在:
Spring AOP:Spring 事务、日志、权限校验底层均用动态代理
MyBatis Mapper:MyBatis 接口无需实现类,通过 JDK 动态代理生成代理对象执行 SQL
RPC 框架:Dubbo 等 RPC 框架通过动态代理屏蔽远程调用细节
全局日志/事务:不修改业务代码,统一添加日志、事务管理
六、总结
JDK 动态代理:基于接口,原生支持,适合有接口的类代理,简单稳定
CGLIB 动态代理:基于继承,无接口要求,性能更高,适合无接口的类代理
Spring Boot 2.0+ 新版本中,默认统一使用 CGLIB,不再根据目标类是否实现接口选择,仅当手动配置时可切换为 JDK 动态代理
核心思想:不修改原始代码,动态增强方法功能,是 AOP 的核心实现
关键点回顾
JDK动态代理:必须基于接口,原生实现,通过
InvocationHandler增强CGLIB动态代理:基于继承,无需接口,通过
MethodInterceptor拦截核心区别:接口 vs 继承,Spring Boot 2.0+ 新版本默认统一使用 CGLIB
核心价值:无侵入式方法增强,支撑 AOP、事务等核心功能