如何将JDK和CGlib动态代理技术应用于生成?
摘要:要彻底搞懂 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 会自动适配,无需手动选择。
