jdk动态代理和cglib动态代理
要彻底搞懂 JDK 动态代理和 CGLIB,我们先从「代理模式」的核心思想入手,再分别拆解两种动态代理的实现原理、代码示例和核心区别,最后总结适用场景。
一、前置知识:代理模式的核心
代理模式是一种设计模式,核心是通过代理类控制对目标类的访问,可以在不修改目标类代码的前提下,增加额外功能(如日志、事务、权限校验)。
- 静态代理:代理类在编译期就确定,一对一绑定目标类,灵活性差。
- 动态代理:代理类在运行期动态生成,无需手动编写代理类,是日常开发(如 Spring AOP)的核心。
JDK 动态代理和 CGLIB 是动态代理的两种主流实现方式,核心区别在于是否依赖接口。
二、JDK 动态代理
1. 核心原理
JDK 动态代理是 JDK 自带的功能(无需额外依赖),要求目标类必须实现至少一个接口。
- 底层通过
java.lang.reflect.Proxy类动态生成代理类字节码,通过InvocationHandler接口处理代理逻辑。 - 生成的代理类会实现目标类的所有接口,并通过反射调用目标方法。
2. 代码示例
步骤1:定义目标接口和目标类
// 目标接口
public interface UserService {
void addUser(String username);
void deleteUser(String username);
}
// 目标类(必须实现接口)
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("添加用户:" + username);
}
@Override
public void deleteUser(String username) {
System.out.println("删除用户:" + username);
}
}
步骤2:实现 InvocationHandler 接口(核心:代理逻辑)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
// 自定义调用处理器,封装代理逻辑(如日志)
public class JdkProxyHandler implements InvocationHandler {
// 目标对象(被代理的对象)
private Object target;
// 构造器注入目标对象
public JdkProxyHandler(Object target) {
this.target = target;
}
/**
* 核心方法:代理类调用方法时,会触发此方法
* @param proxy 生成的代理对象(一般不用)
* @param method 目标方法
* @param args 目标方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强:调用目标方法前添加日志
System.out.println("[JDK代理] 方法 " + method.getName() + " 开始执行,参数:" + args[0]);
// 调用目标类的原始方法
Object result = method.invoke(target, args);
// 后置增强:调用目标方法后添加日志
System.out.println("[JDK代理] 方法 " + method.getName() + " 执行完成");
return result;
}
}
步骤3:生成代理对象并测试
import java.lang.reflect.Proxy;
public class JdkProxyTest {
public static void main(String[] args) {
// 1. 创建目标对象
UserService target = new UserServiceImpl();
// 2. 创建调用处理器
JdkProxyHandler handler = new JdkProxyHandler(target);
// 3. 动态生成代理对象(核心方法:Proxy.newProxyInstance)
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类的类加载器
target.getClass().getInterfaces(), // 目标类实现的接口(必须)
handler // 调用处理器
);
// 4. 调用代理对象的方法(实际执行 handler.invoke 方法)
proxy.addUser("张三");
proxy.deleteUser("李四");
}
}
输出结果:
[JDK代理] 方法 addUser 开始执行,参数:张三
添加用户:张三
[JDK代理] 方法 addUser 执行完成
[JDK代理] 方法 deleteUser 开始执行,参数:李四
删除用户:李四
[JDK代理] 方法 deleteUser 执行完成
3. 关键要点
- 必须依赖接口,否则会抛出
IllegalArgumentException。 - 代理类由
Proxy类动态生成,类名格式为$Proxy0(运行期可见)。 - 底层基于反射,调用效率略低于 CGLIB。
三、CGLIB 动态代理
1. 核心原理
CGLIB(Code Generation Library)是第三方库(Spring 已内置),无需目标类实现接口,通过「继承」目标类动态生成子类作为代理类。
- 底层通过 ASM 字节码框架修改字节码,生成目标类的子类。
- 重写目标类的非 final 方法,植入代理逻辑。
2. 代码示例
前置依赖(若未使用 Spring,需手动引入):
<!-- Maven 依赖 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
步骤1:定义目标类(无需实现接口)
// 目标类(无接口)
public class OrderService {
public void createOrder(String orderNo) {
System.out.println("创建订单:" + orderNo);
}
public void cancelOrder(String orderNo) {
System.out.println("取消订单:" + orderNo);
}
}
步骤2:实现 MethodInterceptor 接口(核心:代理逻辑)
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// CGLIB 方法拦截器,封装代理逻辑
public class CglibProxyInterceptor implements MethodInterceptor {
// 目标对象
private Object target;
public CglibProxyInterceptor(Object target) {
this.target = target;
}
/**
* 核心方法:代理类调用方法时触发
* @param obj 代理对象(子类)
* @param method 目标方法
* @param args 方法参数
* @param proxy 方法代理对象(用于调用父类方法)
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 前置增强
System.out.println("[CGLIB代理] 方法 " + method.getName() + " 开始执行,参数:" + args[0]);
// 调用目标类的原始方法(通过 MethodProxy 调用,效率更高)
Object result = proxy.invokeSuper(obj, args);
// 后置增强
System.out.println("[CGLIB代理] 方法 " + method.getName() + " 执行完成");
return result;
}
}
步骤3:生成代理对象并测试
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyTest {
public static void main(String[] args) {
// 1. 创建目标对象
OrderService target = new OrderService();
// 2. 创建 CGLIB 增强器(核心类)
Enhancer enhancer = new Enhancer();
// 设置父类(目标类)
enhancer.setSuperclass(OrderService.class);
// 设置方法拦截器
enhancer.setCallback(new CglibProxyInterceptor(target));
// 3. 生成代理对象(子类)
OrderService proxy = (OrderService) enhancer.create();
// 4. 调用代理方法
proxy.createOrder("ORDER_001");
proxy.cancelOrder("ORDER_001");
}
}
输出结果:
[CGLIB代理] 方法 createOrder 开始执行,参数:ORDER_001
创建订单:ORDER_001
[CGLIB代理] 方法 createOrder 执行完成
[CGLIB代理] 方法 cancelOrder 开始执行,参数:ORDER_001
取消订单:ORDER_001
[CGLIB代理] 方法 cancelOrder 执行完成
3. 关键要点
- 无需接口,通过继承实现,因此目标类不能是 final 类,目标方法不能是 final 方法(final 方法无法被重写)。
- 基于字节码生成,调用效率比 JDK 动态代理高(JDK 1.8 后差距缩小)。
- 代理类是目标类的子类,因此无法代理私有方法(私有方法无法被继承)。
四、JDK 动态代理 vs CGLIB 核心对比
| 特性 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|
| 依赖条件 | 目标类必须实现接口 | 无需接口,依赖继承 |
| 底层实现 | Java 反射 API | ASM 字节码框架 |
| 代理类关系 | 实现目标接口(兄弟关系) | 继承目标类(父子关系) |
| 限制 | 无接口则无法使用 | 目标类/方法不能是 final |
| 效率(JDK 1.8+) | 反射调用,效率略低 | 字节码直接生成,效率略高 |
| 适用场景 | 目标类有接口(如 Spring Bean 实现接口) | 目标类无接口(如普通 POJO) |
五、Spring AOP 中的应用
Spring AOP 默认采用「JDK 动态代理」,但如果目标类没有实现接口,则自动切换为「CGLIB」。
- 可以通过配置
proxy-target-class=true强制使用 CGLIB。 - Spring 5.x 后,CGLIB 已内置,无需手动引入依赖。
总结
- JDK 动态代理:基于接口实现,JDK 自带无需依赖,通过
InvocationHandler和Proxy生成代理,适合有接口的场景。 - CGLIB 动态代理:基于继承实现,无需接口,通过
MethodInterceptor和Enhancer生成代理子类,适合无接口的场景,但目标类/方法不能是 final。 - 核心选择:有接口用 JDK 代理,无接口用 CGLIB;Spring AOP 会自动适配,无需手动选择。
百流积聚,江河是也;文若化风,可以砾石。

浙公网安备 33010602011771号