【从0开始学习Java | 第23篇】动态代理
文章目录
- Java动态代理概述
- 一、动态代理的核心概念
- 形象解释
- 二、两种主流动态代理实现
- 1. JDK动态代理(基于接口)
- 原理
- 示例代码
- 优缺点
- 2. CGLIB动态代理(基于子类)
- 原理
- 示例代码(需引入CGLIB依赖)
- 优缺点
- 三、JDK与CGLIB动态代理对比
- 四、实际应用场景
- 五、总结
Java动态代理概述
在Java开发中,代理模式设计模式之一,而动态代理作为代理模式的进阶形式,在框架开发(如Spring AOP)、日志记录、权限控制等场景中发挥着关键作用。本文将从核心概念出发,拆解两种主流动态代理的实现逻辑,并分析其适用场景。
一、动态代理的核心概念
动态代理指在程序运行时,通过反射机制动态生成代理类,而非在编译期预先定义。其核心价值在于:无需为每个目标类手动编写代理类,即可统一为多个目标类添加横切逻辑(如日志、事务、异常处理),降低代码耦合度。
动态代理包含三个核心角色:
- 目标类(Target):被代理的原始类,包含核心业务逻辑;
- 代理类(Proxy):运行时动态生成的类,持有目标类引用,负责调用目标方法并增强逻辑;
- 增强逻辑(Advice):需统一添加的横切逻辑,如日志打印、参数校验等。
形象解释
二、两种主流动态代理实现
Java中动态代理主要有两种实现方式:JDK动态代理(原生API)和CGLIB动态代理(第三方库),二者在原理和使用上存在显著差异。
1. JDK动态代理(基于接口)
原理
JDK动态代理依赖java.lang.reflect
包下的Proxy
和InvocationHandler
接口,要求目标类必须实现至少一个接口。运行时,JVM会动态生成一个实现目标接口的代理类,代理类的方法调用会转发到InvocationHandler
的invoke
方法中,在该方法中可嵌入增强逻辑并调用目标方法。
示例代码
// 1. 定义接口
public interface UserService {void addUser(String name);
}// 2. 实现目标类
public class UserServiceImpl implements UserService {@Overridepublic void addUser(String name) {System.out.println("添加用户:" + name);}
}// 3. 实现InvocationHandler(增强逻辑)
public class LogInvocationHandler implements InvocationHandler {private Object target; // 目标类引用public LogInvocationHandler(Object target) {this.target = target;}// 代理类的所有方法调用都会触发invoke@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 增强逻辑:前置日志System.out.println("方法" + method.getName() + "开始执行,参数:" + Arrays.toString(args));// 调用目标方法Object result = method.invoke(target, args);// 增强逻辑:后置日志System.out.println("方法" + method.getName() + "执行结束");return result;}
}// 4. 生成代理类并测试
public class JdkProxyTest {public static void main(String[] args) {// 目标对象UserService target = new UserServiceImpl();// 生成代理对象(需传入类加载器、目标接口、InvocationHandler)UserService proxy = (UserService) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new LogInvocationHandler(target));// 调用代理方法proxy.addUser("张三");}
}
优缺点
- 优点:基于JDK原生API,无需依赖第三方库,轻量化;
- 缺点:目标类必须实现接口,无法代理无接口的类。
2. CGLIB动态代理(基于子类)
原理
CGLIB(Code Generation Library)是一个第三方字节码生成库,通过生成目标类的子类作为代理类,无需目标类实现接口。其核心是MethodInterceptor
接口,代理类的方法调用会被拦截到intercept
方法中,在此处嵌入增强逻辑并调用目标方法。
示例代码(需引入CGLIB依赖)
<!-- Maven依赖 -->
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
// 1. 目标类(无需实现接口)
public class OrderService {public void createOrder(String orderId) {System.out.println("创建订单:" + orderId);}
}// 2. 实现MethodInterceptor(增强逻辑)
public class LogMethodInterceptor implements MethodInterceptor {// 拦截代理类方法调用@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {// 增强逻辑:前置日志System.out.println("方法" + method.getName() + "开始执行,参数:" + Arrays.toString(args));// 调用目标方法(推荐用methodProxy.invokeSuper,避免递归调用)Object result = methodProxy.invokeSuper(o, args);// 增强逻辑:后置日志System.out.println("方法" + method.getName() + "执行结束");return result;}
}// 3. 生成代理类并测试
public class CglibProxyTest {public static void main(String[] args) {// CGLIB核心类:EnhancerEnhancer enhancer = new Enhancer();// 设置父类(目标类)enhancer.setSuperclass(OrderService.class);// 设置方法拦截器enhancer.setCallback(new LogMethodInterceptor());// 生成代理对象(子类实例)OrderService proxy = (OrderService) enhancer.create();// 调用代理方法proxy.createOrder("ORDER_001");}
}
优缺点
- 优点:无需目标类实现接口,可代理任意类(除final类和final方法);
- 缺点:依赖第三方库,生成代理类时需操作字节码,性能略低于JDK动态代理(JDK 8后差距已缩小)。
三、JDK与CGLIB动态代理对比
对比维度 | JDK动态代理 | CGLIB动态代理 |
---|---|---|
依赖 | JDK原生API(无第三方依赖) | 需引入CGLIB库 |
代理原理 | 实现目标接口 | 生成目标类子类 |
目标类要求 | 必须实现接口 | 无接口要求(不能是final类) |
方法限制 | 仅代理接口中的方法 | 不能代理final方法 |
性能(JDK 8+) | 生成快,调用效率高 | 生成慢,调用效率略低 |
四、实际应用场景
- Spring AOP:默认优先使用JDK动态代理(目标类有接口时),无接口时使用CGLIB;
- 日志记录:统一记录方法的入参、出参、执行时间;
- 权限控制:方法调用前校验用户权限,无权限则抛出异常;
- 事务管理:方法执行前开启事务,执行后提交/回滚事务。
五、总结
动态代理是Java中“解耦横切逻辑”的核心技术,JDK动态代理和CGLIB各有适用场景:若目标类已实现接口,优先选择JDK动态代理(轻量化、无依赖);若目标类无接口或需代理类的所有方法,选择CGLIB。
如果我的内容对你有帮助,请 点赞 , 评论 , 收藏 。创作不易,大家的支持就是我坚持下去的动力!