dubbo默认使用的动态代理方式

本文探讨了Dubbo中动态代理的实现方式,对比了JDK、Javassist和CGLib的特点,分析了Dubbo为何选择Javassist而非CGLib进行动态代理,以及如何在Dubbo中配置不同的动态代理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Dubbo中,没有使用CGLib进行代理,而是使用JDK和Javassist来进行动态代理!我们知道,动态代理是无法用反射做的,只能靠动态生成字节码,这就需要使用字节码工具包,比如asm和Javassist等,在Spring3.2.2之前版本的源码中,我们可以看到是有单独spring-asm的模块的,但在Spring3.2.2版本开始,就没有spring-asm模块了,不是不使用了,而是spring-asm已经整合到spring-core中了,可见asm在Spring中的地位(CGLib使用的就是asm),至于Dubbo为什么不使用CGLib,当我们选择动态代理实现时,无非考虑的是如下因素:

  1. 使用的难易程度。
  2. 功能,比如JDK的代理只能通过接口实现,而CGLib则没这个限制。
  3. 生成的字节码的效率,包括创建的效率和运行的效率。
  4. 生成的字节码大小,为什么说这也比较重要?这和生成的Class和其实例的大小相关。

怎么选择动态代理方式?

<dubbo:provider proxy="jdk" />或<dubbo:consumer proxy="jdk" />  默认情况下使用Javassist来进行动态代理。

<think>我们正在讨论Java动态代理的实现方式。根据引用内容,Java动态代理主要有两种实现方式:JDK原生动态代理CGLib动态代理。下面我将详细解释这两种方式,并比较它们的异同。 ### 1. JDK原生动态代理 JDK动态代理是Java标准库提供的一种代理机制,基于**反射**实现。它要求被代理的类必须实现至少一个接口。 #### 实现步骤: 1. **定义接口**:声明需要代理的方法。 2. **实现接口**:创建目标类,实现接口中的方法。 3. **实现InvocationHandler**:编写调用处理器,在`invoke`方法中添加增强逻辑。 4. **生成代理对象**:通过`Proxy.newProxyInstance`方法动态创建代理实例。 #### 示例代码: ```java // 接口 public interface UserService { void addUser(String name); } // 目标类 public class UserServiceImpl implements UserService { public void addUser(String name) { System.out.println("添加用户: " + name); } } // 调用处理器 public class LogHandler implements InvocationHandler { private Object target; public LogHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法调用前日志"); Object result = method.invoke(target, args); System.out.println("方法调用后日志"); return result; } } // 创建代理 public static void main(String[] args) { UserService target = new UserServiceImpl(); InvocationHandler handler = new LogHandler(target); UserService proxy = (UserService) Proxy.newProxyInstance( UserService.class.getClassLoader(), new Class[]{UserService.class}, handler ); proxy.addUser("张三"); // 调用代理方法 } ``` #### 特点: - 只能代理接口 - 无需引入第三方库 - 执行效率相对较低(反射调用) ### 2. CGLib动态代理 CGLib(Code Generation Library)是一个基于**字节码操作**(通过ASM框架)的代理库。它通过生成目标类的子类来实现代理,因此不需要目标类实现接口。 #### 实现步骤: 1. **创建MethodInterceptor**:实现`MethodInterceptor`接口,在`intercept`方法中添加增强逻辑。 2. **生成代理对象**:使用`Enhancer`类创建代理实例。 #### 示例代码: ```java import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CglibProxy implements MethodInterceptor { private Object target; public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("方法调用前日志"); Object result = proxy.invokeSuper(obj, args); System.out.println("方法调用后日志"); return result; } } // 使用代理 public static void main(String[] args) { UserService target = new UserService(); // 注意:这里UserService是类,不是接口 CglibProxy cglibProxy = new CglibProxy(); UserService proxy = (UserService) cglibProxy.getInstance(target); proxy.addUser("李四"); } ``` #### 特点: - 可以代理普通类(无需实现接口) - 需要引入CGLib依赖 - 执行效率较高(通过FastClass机制直接调用方法) - 无法代理final类或final方法 ### 3. 两种代理方式对比 | 特性 | JDK动态代理 | CGLib动态代理 | |---------------------|--------------------------------|-------------------------------| | 代理对象要求 | 必须实现接口 | 任意Java类 | | 底层技术 | 反射 | 字节码操作(ASM) | | 执行速度 | 较慢(反射调用) | 较快(直接调用) | | 生成代理类速度 | 较快 | 较慢(需要生成字节码) | | 方法拦截 | 通过`InvocationHandler` | 通过`MethodInterceptor` | | 依赖 | JDK自带 | 需引入CGLib库 | | 对final类的支持 | 无此问题(代理接口) | 无法代理final类 | > 💡 **选择建议**: > - 若目标对象实现了接口,优先使用JDK动态代理(减少依赖) > - 若目标对象没有接口,必须使用CGLib > - Spring框架默认对接口使用JDK代理,对类使用CGLib[^3] ### 4. 应用场景 - **AOP编程**:方法增强(日志、事务等)[^1][^3] - **RPC框架**:远程方法调用的本地代理 - **测试框架**:Mock对象创建 - **延迟加载**:Hibernate的延迟加载机制 ### 总结 Java动态代理有两种主流实现: 1. **JDK动态代理**:基于接口和反射,适合代理接口实现类 2. **CGLib动态代理**:基于字节码生成,适合代理普通类 两者各有优劣,实际开发中可根据目标对象类型和性能要求选择合适方案。在Spring等框架中,这两种代理方式被智能组合使用以实现全面的AOP功能[^2][^3]。 --- §§相关问题§§ 1. 动态代理在Spring框架中是如何应用的? 2. 如何解决JDK动态代理只能代理接口的问题? 3. CGLIB实现动态代理的原理与JDK代理有何本质区别?</think>### Java 动态代理的实现方式详解 Java 动态代理主要有两种实现方式,它们在底层原理、使用场景和性能特点上存在显著差异: #### 一、JDK 原生动态代理(基于接口) **实现原理**: 使用 Java 反射机制,在运行时动态生成代理类(`$ProxyN`格式),要求**被代理对象必须实现至少一个接口**。 **核心组件**: 1. `java.lang.reflect.Proxy`:代理类工厂 2. `java.lang.reflect.InvocationHandler`:方法调用处理器 **实现步骤**: ```java // 1. 定义接口 public interface OrderService { void createOrder(String orderId); } // 2. 创建调用处理器 public class LogHandler implements InvocationHandler { private final Object target; public LogHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("[LOG] 调用方法: " + method.getName()); return method.invoke(target, args); // 实际方法调用 } } // 3. 生成代理对象 OrderService realService = new OrderServiceImpl(); OrderService proxy = (OrderService) Proxy.newProxyInstance( OrderService.class.getClassLoader(), new Class[]{OrderService.class}, // 代理接口 new LogHandler(realService) // 处理器 ); // 4. 使用代理 proxy.createOrder("ORD-2023-001"); ``` **特点**: - ✅ JDK 原生支持,无需额外依赖 - ✅ 接口方法自动代理 - ❌ 只能代理接口,不能代理类 - ⚠️ 反射调用有性能开销 #### 二、CGLIB 动态代理(基于继承) **实现原理**: 通过 ASM 字节码操作框架,在运行时**生成目标类的子类**作为代理类,通过方法重写实现代理,不要求实现接口。 **核心组件**: 1. `net.sf.cglib.proxy.Enhancer`:字节码增强器 2. `net.sf.cglib.proxy.MethodInterceptor`:方法拦截器 **实现步骤**: ```java // 1. 创建方法拦截器 public class UserServiceInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("[CGLIB] 方法调用: " + method.getName()); return proxy.invokeSuper(obj, args); // 调用父类方法 } } // 2. 生成代理对象 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserServiceImpl.class); // 设置父类 enhancer.setCallback(new UserServiceInterceptor()); // 设置拦截器 UserService proxy = (UserService) enhancer.create(); // 创建代理实例 // 3. 使用代理 proxy.addUser("李四"); ``` **特点**: - ✅ 可代理普通类(无需接口) - ✅ 执行效率更高(避免反射调用) - ❌ 需要引入第三方库(cglib) - ❌ 无法代理 final 类/方法 - ⚠️ 生成字节码有初始性能开销 --- ### 两种方式对比分析 | 特性 | JDK 动态代理 | CGLIB 动态代理 | |---------------------|----------------------------------|---------------------------------| | **代理要求** | 必须实现接口 | 可代理任意类 | | **底层技术** | Java 反射机制 | ASM 字节码操作 | | **执行速度** | 较慢(反射调用) | 较快(直接方法调用) | | **初始化开销** | 较小 | 较大(需生成字节码) | | **方法拦截方式** | InvocationHandler | MethodInterceptor | | **final 方法支持** | 无此问题 | 无法代理 final 方法 | | **依赖** | JDK 原生支持 | 需引入 cglib 库 | > 💡 **最佳实践选择**: > - 当目标对象**已实现接口**时优先使用 JDK 代理(如 Spring 默认策略) > - 当需要代理**普通类**或追求更高性能时使用 CGLIB > - 在 Spring AOP 中可通过 `@EnableAspectJAutoProxy(proxyTargetClass=true)` 强制使用 CGLIB[^3] --- ### 应用场景示例 1. **日志记录**(两种代理均可实现) ```java public Object invoke(...) { log.info(">> 调用 {}.{}()", target.getClass().getSimpleName(), method.getName()); Object result = method.invoke(target, args); log.info("<< 方法执行完成 (耗时: {}ms)", System.currentTimeMillis()-start); return result; } ``` 2. **事务管理**(Spring 声明式事务底层实现) ```java public Object intercept(...) { Transaction tx = beginTransaction(); try { Object result = proxy.invokeSuper(obj, args); tx.commit(); // 成功提交 return result; } catch (Exception e) { tx.rollback(); // 异常回滚 throw e; } } ``` 3. **RPC 调用**(Dubbo 等框架的客户端代理) ```java public Object invoke(...) { // 将方法调用信息序列化 RpcRequest request = buildRequest(method, args); // 通过网络发送请求 return rpcClient.send(request); } ``` --- ### 性能优化建议 1. **缓存代理类**:避免重复生成代理类字节码(CGLIB 的 `Enhancer` 可缓存) 2. **减少拦截方法**:在处理器中过滤无需代理的方法 3. **使用 FastClass**(CGLIB):通过索引直接调用方法,避免反射查找 4. **JIT 优化**:JDK 代理经过多次调用后会被 JIT 编译优化 > 📌 **注意**:在 Java 16+ 中,由于模块系统限制,需添加 JVM 参数 `--add-opens java.base/java.lang=ALL-UNNAMED` 才能使用 CGLIB[^2] 两种动态代理技术各有优势,实际开发中常结合使用(如 Spring AOP 的智能代理选择机制),开发者应根据具体场景选择最合适的方案[^1][^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值