在Java中,动态代理是一种运行时动态生成代理对象的技术,属于代理模式的进阶实现。它无需像静态代理那样手动编写代理类,而是通过框架在运行时自动生成代理类字节码,从而简化代理逻辑的实现,提升代码灵活性。
一、代理模式的核心思想
代理模式的核心是:通过代理对象间接访问目标对象,在不修改目标对象代码的前提下,为目标对象的方法添加额外功能(如日志、事务、权限校验等)。
- 目标对象:被代理的原始对象(如业务逻辑类)。
- 代理对象:持有目标对象的引用,对外提供与目标对象相同的方法,但在方法执行前后可添加增强逻辑。
二、静态代理的局限
静态代理是代理模式的基础实现,需要手动为每个目标类编写代理类(代理类与目标类实现相同接口)。但它存在明显局限:
- 代理类与目标类强耦合,若接口新增方法,所有代理类都需同步修改,维护成本高。
- 当目标类数量多且接口方法复杂时,会产生大量重复的代理代码。
例如,若有100个业务类,静态代理需要编写100个对应的代理类,显然不现实。动态代理正是为解决这一问题而生。
三、Java动态代理的两种实现方式
Java中动态代理的主流实现有两种:JDK动态代理 (基于接口)和CGLIB动态代理(基于继承)。
1. JDK动态代理(基于接口)
JDK动态代理是Java原生支持的代理方式,核心依赖java.lang.reflect包下的两个类:Proxy(生成代理对象)和InvocationHandler(定义代理逻辑)。
核心要求:目标类必须实现至少一个接口(代理对象会实现相同接口)。
实现步骤:
以"为用户服务类添加日志增强"为例:
-
Step 1:定义接口(目标类需实现)
接口中声明目标方法(代理对象会实现这些方法)。
java// 用户服务接口 public interface UserService { void addUser(String name); } -
Step 2:实现目标类
目标类是实际业务逻辑的载体,需实现上述接口。
java// 目标类(被代理的类) public class UserServiceImpl implements UserService { @Override public void addUser(String name) { System.out.println("执行核心业务:添加用户 " + name); } } -
Step 3:定义代理逻辑(实现InvocationHandler)
InvocationHandler是一个函数式接口,其中的invoke方法是代理逻辑的核心:当调用代理对象的方法时,会自动触发invoke方法执行。javaimport java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; // 代理逻辑处理器 public class LogInvocationHandler implements InvocationHandler { // 持有目标对象的引用 private Object target; public LogInvocationHandler(Object target) { this.target = target; } /** * 代理逻辑核心方法 * @param proxy 代理对象(一般不用) * @param method 目标方法(被调用的方法) * @param args 目标方法的参数 * @return 目标方法的返回值 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 增强逻辑:方法执行前 System.out.println("【日志】方法 " + method.getName() + " 开始执行,参数:" + args[0]); // 调用目标对象的方法(核心业务) Object result = method.invoke(target, args); // 增强逻辑:方法执行后 System.out.println("【日志】方法 " + method.getName() + " 执行结束"); return result; } } -
Step 4:生成代理对象(通过Proxy类)
使用
Proxy.newProxyInstance()方法动态生成代理对象,需要3个参数:- 类加载器(与目标类相同);
- 目标类实现的接口数组;
- 上述
InvocationHandler实例(代理逻辑)。
javaimport java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { // 1. 创建目标对象 UserService target = new UserServiceImpl(); // 2. 创建代理逻辑处理器(传入目标对象) LogInvocationHandler handler = new LogInvocationHandler(target); // 3. 动态生成代理对象(代理对象实现UserService接口) UserService proxy = (UserService) Proxy.newProxyInstance( target.getClass().getClassLoader(), // 类加载器 target.getClass().getInterfaces(), // 目标类实现的接口 handler // 代理逻辑 ); // 4. 调用代理对象的方法(会触发invoke方法) proxy.addUser("张三"); } }
执行结果:
【日志】方法 addUser 开始执行,参数:张三
执行核心业务:添加用户 张三
【日志】方法 addUser 执行结束
2. CGLIB动态代理(基于继承)
CGLIB(Code Generation Library)是一个第三方字节码生成库,它通过继承目标类 生成代理对象(无需目标类实现接口)。核心依赖net.sf.cglib包下的MethodInterceptor接口(定义代理逻辑)和Enhancer类(生成代理对象)。
核心要求 :目标类不能是final(否则无法继承),目标方法不能是final(否则无法重写增强)。
实现步骤:
同样以"为用户服务类添加日志增强"为例(目标类无需实现接口):
-
Step 1:引入CGLIB依赖
若使用Maven,需在
pom.xml中添加:xml<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency> -
Step 2:定义目标类(无需实现接口)
java// 目标类(被代理的类,无需实现接口) public class UserService { public void addUser(String name) { System.out.println("执行核心业务:添加用户 " + name); } } -
Step 3:定义代理逻辑(实现MethodInterceptor)
MethodInterceptor接口中的intercept方法是代理逻辑核心:调用代理对象的方法时,会触发该方法。javaimport net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; // CGLIB代理逻辑处理器 public class LogMethodInterceptor implements MethodInterceptor { /** * 代理逻辑核心方法 * @param obj 代理对象 * @param method 目标方法 * @param args 目标方法参数 * @param proxy 方法代理(用于调用目标方法) * @return 目标方法返回值 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 增强逻辑:方法执行前 System.out.println("【日志】方法 " + method.getName() + " 开始执行,参数:" + args[0]); // 调用目标对象的方法(核心业务) Object result = proxy.invokeSuper(obj, args); // 注意:通过methodProxy调用父类(目标类)方法 // 增强逻辑:方法执行后 System.out.println("【日志】方法 " + method.getName() + " 执行结束"); return result; } } -
Step 4:生成代理对象(通过Enhancer类)
Enhancer是CGLIB的核心类,用于生成代理对象,需指定父类(目标类)和回调(代理逻辑)。javaimport net.sf.cglib.proxy.Enhancer; public class Main { public static void main(String[] args) { // 1. 创建增强器 Enhancer enhancer = new Enhancer(); // 2. 设置父类(目标类,代理类会继承该类) enhancer.setSuperclass(UserService.class); // 3. 设置回调(代理逻辑) enhancer.setCallback(new LogMethodInterceptor()); // 4. 生成代理对象(代理对象是UserService的子类) UserService proxy = (UserService) enhancer.create(); // 5. 调用代理对象的方法(会触发intercept方法) proxy.addUser("李四"); } }
执行结果:
【日志】方法 addUser 开始执行,参数:李四
执行核心业务:添加用户 李四
【日志】方法 addUser 执行结束
四、JDK动态代理 vs CGLIB动态代理
| 维度 | JDK动态代理 | CGLIB动态代理 |
|---|---|---|
| 底层原理 | 基于接口实现(代理类实现目标接口) | 基于继承实现(代理类继承目标类) |
| 目标类要求 | 必须实现至少一个接口 | 不能是final类,方法不能是final |
| 性能(JDK8+) | 生成代理对象快,运行时性能略优 | 生成代理对象慢(需生成字节码),运行时性能略差(差异极小) |
| 依赖 | Java原生支持,无需额外依赖 | 需引入CGLIB库 |
五、动态代理的典型应用场景
动态代理是许多框架的核心技术,例如:
- Spring AOP:通过动态代理实现切面逻辑(如事务、日志)与业务逻辑的分离(默认对接口用JDK代理,对类用CGLIB)。
- RPC框架:通过动态代理生成远程服务的本地代理对象,屏蔽网络通信细节。
- 权限控制:在方法调用前通过代理校验用户权限。
- 日志记录:在方法执行前后自动记录日志。
总结
动态代理的核心价值是**"运行时动态生成代理对象,在不侵入目标类的前提下增强方法逻辑"**。JDK动态代理基于接口,适用于目标类有接口的场景;CGLIB基于继承,适用于目标类无接口的场景。两者各有优劣,实际开发中可根据目标类特性选择。