Java中JDK动态代理与CGLIB动态代理的区别详解
在Java开发中,动态代理是一种非常重要的设计模式,广泛应用于AOP(面向切面编程)、事务管理、日志记录等场景。
Java生态中主要有两种动态代理实现方式:JDK动态代理和CGLIB动态代理。本文将从原理、使用方式、性能、适用场景等多个维度详细对比这两种代理机制,并附上完整的代码示例。
一、什么是动态代理?
动态代理是指在程序运行时动态生成代理对象,而不需要在编译期就确定代理类。代理对象可以拦截对目标对象方法的调用,在方法执行前后插入自定义逻辑(如日志、权限校验、事务控制等)。
二、JDK动态代理
2.1 原理
- JDK动态代理基于 Java反射机制 实现。
- 要求被代理的类必须实现至少一个 接口(Interface)。
- 代理类在运行时通过
java.lang.reflect.Proxy类动态生成,该代理类会实现目标对象所实现的所有接口。 - 方法调用通过
InvocationHandler 的 invoke()方法进行拦截。
2.2 优点
- 纯Java实现,无需引入第三方库。
- 代理逻辑清晰,易于理解和调试。
2.3 缺点
- 只能代理实现了接口的类,无法代理没有接口的类或 final 类。
2.4 代码示例
// 1. 定义接口
public interface UserService {
void addUser(String name);
String getUser(String id);
}
// 2. 实现类
public class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("添加用户: " + name);
}
@Override
public String getUser(String id) {
System.out.println("查询用户ID: " + id);
return "User-" + id;
}
}
// 3. 自定义 InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【JDK代理】方法调用前: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("【JDK代理】方法调用后");
return result;
}
}
// 4. 测试类
import java.lang.reflect.Proxy;
public class JdkProxyTest {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
MyInvocationHandler handler = new MyInvocationHandler(userService);
// 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
handler
);
proxy.addUser("张三");
String user = proxy.getUser("1001");
System.out.println("返回结果: " + user);
}
}
输出结果:
【JDK代理】方法调用前: addUser
添加用户: 张三
【JDK代理】方法调用后
【JDK代理】方法调用前: getUser
查询用户ID: 1001
【JDK代理】方法调用后
返回结果: User-1001
三、CGLIB动态代理
3.1 原理
- CGLIB(Code Generation Library)是一个强大的高性能代码生成库。
- 它通过 继承目标类 并重写其非 final 方法来实现代理。
- 底层使用 ASM 字节码操作框架,在运行时动态生成子类(代理类)。
- 不要求目标类实现接口。
3.2 优点
- 可以代理 没有实现接口的类。
- 性能通常优于 JDK 动态代理(尤其在 JDK 8 之前)。
3.3 缺点
- 无法代理 final 类 或 final 方法(因为不能被继承/重写)。
- 需要额外引入 CGLIB 依赖(虽然 Spring 等框架已内置)。
- 由于是通过继承实现,可能引发一些设计上的问题(如构造器多次调用)。
3.4 Maven 依赖(如未使用 Spring)
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
注:Spring Boot 项目通常已包含 CGLIB,无需单独引入。
3.5 代码示例
// 1. 目标类(无接口)
public class OrderService {
public void createOrder(String orderId) {
System.out.println("创建订单: " + orderId);
}
public final void finalizeOrder(String orderId) {
System.out.println("最终确认订单(final方法): " + orderId);
}
}
// 2. 自定义 MethodInterceptor
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("【CGLIB代理】调用方法前: " + method.getName());
Object result = proxy.invokeSuper(obj, args); // 调用父类方法
System.out.println("【CGLIB代理】调用方法后");
return result;
}
}
// 3. 测试类
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderService.class);
enhancer.setCallback(new CglibMethodInterceptor());
OrderService proxy = (OrderService) enhancer.create();
proxy.createOrder("ORD-2025");
proxy.finalizeOrder("ORD-2025"); // 注意:final 方法不会被代理!
}
}
输出结果:
【CGLIB代理】调用方法前: createOrder
创建订单: ORD-2025
【CGLIB代理】调用方法后
最终确认订单(final方法): ORD-2025
⚠️ 注意:finalizeOrder 是 final 方法,CGLIB 无法代理,因此不会触发拦截逻辑。
四、JDK动态代理 vs CGLIB动态代理 对比总结
| 特性 | JDK动态代理 | CGLIB动态代理 |
|---|---|---|
| 实现原理 | 基于接口 + 反射 | 基于继承 + 字节码生成(ASM) |
| 是否需要接口 | ✅ 必须实现接口 | ❌ 不需要接口 |
| 能否代理 final 类/方法 | ❌ 不能(但接口无此问题) | ❌ 不能代理 final 类或 final 方法 |
| 性能(JDK 8+) | 较高(反射优化) | 略高或相当(早期版本 CGLIB 更快) |
| 依赖 | JDK 自带 | 需引入 cglib.jar |
| 代理对象类型 | 接口类型 | 目标类的子类 |
| 适用场景 | 目标类有接口(如 Spring 中的 Service 接口) | 目标类无接口(如某些工具类、遗留代码) |
📌 Spring 框架中的选择策略:
- 如果目标 Bean 实现了接口,Spring 默认使用 JDK 动态代理。
- 如果目标 Bean 没有实现接口,Spring 自动切换为 CGLIB 代理。
- 可通过配置强制使用 CGLIB:
@EnableAspectJAutoProxy(proxyTargetClass = true)
五、结语
理解 JDK 动态代理与 CGLIB 动态代理的区别,有助于我们在实际开发中做出合理的技术选型,尤其是在使用 Spring AOP、MyBatis 等框架时。掌握其底层原理,也能帮助我们更好地排查代理相关的问题(如代理失效、方法未被拦截等)。
希望本文能帮助你深入理解 Java 动态代理机制!欢迎关注我的 优快云 博客,获取更多高质量技术文章!

被折叠的 条评论
为什么被折叠?



