一、代理模式核心概念
1. 定义
代理模式是一种结构型设计模式,核心思想是通过一个 "代理类" 为目标类提供间接访问,在不修改目标类代码的前提下,对目标类的功能进行增强(如日志记录、权限校验、性能监控等)。
2. 核心角色
表格
| 角色 | 职责 |
|---|---|
| 抽象主题(Subject) | 定义目标类和代理类的公共接口,确保代理类与目标类行为一致 |
| 目标对象(RealSubject) | 真正执行业务逻辑的类,是代理的目标 |
| 代理对象(Proxy) | 实现抽象主题接口,持有目标对象引用,在调用目标方法前后添加增强逻辑 |
3. 代理模式的价值
- 解耦:业务逻辑与非业务逻辑(如日志、事务)分离;
- 增强:无侵入式扩展目标类功能;
- 控制:可控制对目标对象的访问(如权限校验);
- 是 AOP(面向切面编程)的底层实现核心。
二、静态代理
1. 实现步骤
步骤 1:定义抽象主题接口
// 抽象主题:订单服务接口
public interface OrderService {
// 创建订单
void createOrder(String orderNo);
// 取消订单
void cancelOrder(String orderNo);
}
步骤 2:实现目标类(真实主题)
// 目标对象:订单服务实现类
public class OrderServiceImpl implements OrderService {
@Override
public void createOrder(String orderNo) {
System.out.println("【目标类】创建订单:" + orderNo);
}
@Override
public void cancelOrder(String orderNo) {
System.out.println("【目标类】取消订单:" + orderNo);
}
}
步骤 3:实现静态代理类
// 静态代理类:增强订单服务的功能
public class OrderServiceStaticProxy implements OrderService {
// 持有目标对象引用
private final OrderService target;
// 通过构造器注入目标对象
public OrderServiceStaticProxy(OrderService target) {
this.target = target;
}
@Override
public void createOrder(String orderNo) {
// 前置增强:日志记录 + 权限校验
before("createOrder", orderNo);
// 调用目标方法
target.createOrder(orderNo);
// 后置增强:记录耗时
after("createOrder");
}
@Override
public void cancelOrder(String orderNo) {
before("cancelOrder", orderNo);
target.cancelOrder(orderNo);
after("cancelOrder");
}
// 前置增强逻辑
private void before(String methodName, String orderNo) {
System.out.println("【静态代理】前置增强:调用" + methodName + "方法,参数:" + orderNo);
System.out.println("【静态代理】权限校验通过");
}
// 后置增强逻辑
private void after(String methodName) {
System.out.println("【静态代理】后置增强:" + methodName + "方法执行完成");
}
}
步骤 4:测试静态代理
public class StaticProxyTest {
public static void main(String[] args) {
// 1. 创建目标对象
OrderService target = new OrderServiceImpl();
// 2. 创建代理对象
OrderService proxy = new OrderServiceStaticProxy(target);
// 3. 通过代理对象调用方法
proxy.createOrder("ORDER_001");
System.out.println("------------------------");
proxy.cancelOrder("ORDER_001");
}
}
输出结果
【静态代理】前置增强:调用createOrder方法,参数:ORDER_001
【静态代理】权限校验通过
【目标类】创建订单:ORDER_001
【静态代理】后置增强:createOrder方法执行完成
------------------------
【静态代理】前置增强:调用cancelOrder方法,参数:ORDER_001
【静态代理】权限校验通过
【目标类】取消订单:ORDER_001
【静态代理】后置增强:cancelOrder方法执行完成
2. 静态代理的优缺点
| 优点 | 缺点 |
|---|---|
| 实现简单,易于理解和调试 | 代理类与目标类一一对应,类数量爆炸(维护成本高) |
| 无侵入式增强目标类功能 | 接口变更时,代理类和目标类需同步修改 |
| 逻辑清晰,增强代码集中 | 无法动态扩展,新增目标类需手动编写代理类 |
三、JDK 动态代理
1. 核心原理
JDK 动态代理是 JDK 原生支持的代理方式,基于接口实现:
- 无需手动编写代理类,运行时通过
java.lang.reflect.Proxy动态生成代理类字节码; - 核心依赖
InvocationHandler接口,统一处理代理对象的方法调用; - 要求目标类必须实现至少一个接口(否则无法使用)。
2. 核心类 / 接口
| 类 / 接口 | 核心作用 |
|---|---|
Proxy |
生成动态代理类的核心类,核心方法:newProxyInstance() |
InvocationHandler |
代理逻辑处理器,通过 invoke() 方法统一处理所有代理方法的增强逻辑 |
3. 实现步骤
步骤 1:复用抽象主题和目标类(同静态代理的 OrderService/OrderServiceImpl)
步骤 2:实现 InvocationHandler 接口
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
// JDK动态代理处理器:统一处理所有代理方法的增强逻辑
public class JdkDynamicProxyHandler implements InvocationHandler {
// 目标对象(通用类型,适配所有接口)
private final Object target;
public JdkDynamicProxyHandler(Object target) {
this.target = target;
}
/**
* 代理方法的核心处理逻辑
* @param proxy 生成的代理对象(一般不用)
* @param method 被调用的目标方法
* @param args 方法入参
* @return 目标方法的返回值
* @throws Throwable 方法执行异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强
before(method.getName(), args);
// 执行目标方法
Object result = method.invoke(target, args);
// 后置增强
after(method.getName());
return result;
}
// 通用前置增强逻辑
private void before(String methodName, Object[] args) {
System.out.println("【JDK动态代理】前置增强:调用" + methodName + "方法,参数:" + (args != null ? args[0] : "无"));
System.out.println("【JDK动态代理】权限校验通过");
}
// 通用后置增强逻辑
private void after(String methodName) {
System.out.println("【JDK动态代理】后置增强:" + methodName + "方法执行完成");
}
}
步骤 3:生成动态代理对象并测试
import java.lang.reflect.Proxy;
public class JdkDynamicProxyTest {
public static void main(String[] args) {
// 1. 创建目标对象
OrderService target = new OrderServiceImpl();
// 2. 创建代理处理器
InvocationHandler handler = new JdkDynamicProxyHandler(target);
// 3. 生成动态代理对象(核心方法)
OrderService proxy = (OrderService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类类加载器
target.getClass().getInterfaces(), // 目标类实现的接口
handler // 代理处理器
);
// 4. 通过代理对象调用方法
proxy.createOrder("ORDER_002");
System.out.println("------------------------");
proxy.cancelOrder("ORDER_002");
// 打印代理类的类名(验证动态生成)
System.out.println("------------------------");
System.out.println("代理类的类名:" + proxy.getClass().getName());
}
}
输出结果
【JDK动态代理】前置增强:调用createOrder方法,参数:ORDER_002
【JDK动态代理】权限校验通过
【目标类】创建订单:ORDER_002
【JDK动态代理】后置增强:createOrder方法执行完成
------------------------
【JDK动态代理】前置增强:调用cancelOrder方法,参数:ORDER_002
【JDK动态代理】权限校验通过
【目标类】取消订单:ORDER_002
【JDK动态代理】后置增强:cancelOrder方法执行完成
------------------------
代理类的类名:com.sun.proxy.$Proxy0
4. JDK 动态代理的优缺点
| 优点 | 缺点 |
|---|---|
| 无需手动编写代理类,灵活扩展 | 目标类必须实现接口(局限性) |
| 代理逻辑统一,维护成本低 | 仅能代理接口中定义的方法,无法代理类方法 |
| JDK 原生支持,无需额外依赖 | 底层基于反射,性能略低于 CGLIB |
四、CGLIB 动态代理
1. 核心原理
CGLIB(Code Generation Library)是第三方字节码生成库,基于继承实现:
- 无需目标类实现接口,通过继承目标类动态生成子类作为代理类;
- 底层通过 ASM 框架操作字节码,直接生成新的类文件;
- Spring、MyBatis 等框架均内置 CGLIB 依赖。
2. 前置条件
-
引入 CGLIB 依赖(Spring 项目无需手动引入,单独使用需添加):
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
3. 核心类 / 接口
| 类 / 接口 | 核心作用 |
|---|---|
Enhancer |
CGLIB 核心类,用于生成代理类,类似 JDK 的 Proxy |
MethodInterceptor |
方法拦截器,替代 JDK 的 InvocationHandler,通过 intercept() 处理增强 |
MethodProxy |
目标方法的代理对象,可优化方法调用性能(避免反射) |
4. 实现步骤
步骤 1:创建无接口的目标类(CGLIB 适配无接口场景)
// 无接口的目标类:用户服务
public class UserService {
public void addUser(String username) {
System.out.println("【目标类】添加用户:" + username);
}
public void deleteUser(String username) {
System.out.println("【目标类】删除用户:" + username);
}
// 注意:final方法无法被CGLIB代理(子类无法重写)
public final void queryUser(String username) {
System.out.println("【目标类】查询用户:" + username);
}
}
步骤 2:实现 MethodInterceptor 接口
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// CGLIB方法拦截器:处理代理增强逻辑
public class CglibMethodInterceptor implements MethodInterceptor {
// 目标对象
private final Object target;
public CglibMethodInterceptor(Object target) {
this.target = target;
}
/**
* 拦截目标方法调用
* @param proxy 代理对象(子类实例)
* @param method 目标方法
* @param args 方法入参
* @param methodProxy 方法代理对象(优化调用)
* @return 目标方法返回值
* @throws Throwable 执行异常
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 前置增强
before(method.getName(), args);
// 执行目标方法(两种方式:反射 / methodProxy.invokeSuper)
Object result = methodProxy.invokeSuper(proxy, args); // 推荐:性能更高
// Object result = method.invoke(target, args); // 反射方式
// 后置增强
after(method.getName());
return result;
}
private void before(String methodName, Object[] args) {
System.out.println("【CGLIB代理】前置增强:调用" + methodName + "方法,参数:" + (args != null ? args[0] : "无"));
System.out.println("【CGLIB代理】参数校验通过");
}
private void after(String methodName) {
System.out.println("【CGLIB代理】后置增强:" + methodName + "方法执行完成");
}
}
步骤 3:生成 CGLIB 代理对象并测试
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyTest {
public static void main(String[] args) {
// 1. 创建目标对象
UserService target = new UserService();
// 2. 创建CGLIB增强器
Enhancer enhancer = new Enhancer();
// 设置父类(目标类)
enhancer.setSuperclass(UserService.class);
// 设置方法拦截器
enhancer.setCallback(new CglibMethodInterceptor(target));
// 3. 生成代理对象(子类实例)
UserService proxy = (UserService) enhancer.create();
// 4. 调用代理方法
proxy.addUser("张三");
System.out.println("------------------------");
proxy.deleteUser("张三");
System.out.println("------------------------");
proxy.queryUser("张三"); // final方法:无增强逻辑,直接执行目标方法
// 打印代理类的类名
System.out.println("------------------------");
System.out.println("代理类的类名:" + proxy.getClass().getName());
}
}
输出结果
【CGLIB代理】前置增强:调用addUser方法,参数:张三
【CGLIB代理】参数校验通过
【目标类】添加用户:张三
【CGLIB代理】后置增强:addUser方法执行完成
------------------------
【CGLIB代理】前置增强:调用deleteUser方法,参数:张三
【CGLIB代理】参数校验通过
【目标类】删除用户:张三
【CGLIB代理】后置增强:deleteUser方法执行完成
------------------------
【目标类】查询用户:张三
------------------------
代理类的类名:com.example.proxy.UserService$$EnhancerByCGLIB$$xxxxxxx
5. CGLIB 代理的注意事项
- final 方法无法被代理:CGLIB 基于继承,子类无法重写 final 方法;
- 目标类不能是 final 类:final 类无法被继承,无法生成代理子类;
- 性能优于 JDK 动态代理:ASM 直接操作字节码,避免反射开销;
- 构造方法会被调用两次:生成代理类时会先创建目标类实例,需注意无参构造的影响。
6. CGLIB 与 JDK 动态代理对比
| 特性 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|
| 实现方式 | 基于接口(实现接口) | 基于继承(继承目标类) |
| 目标类要求 | 必须实现接口 | 无需实现接口(不能是 final) |
| 核心类 | Proxy、InvocationHandler | Enhancer、MethodInterceptor |
| 性能 | 反射调用,性能略低 | 字节码生成,性能更高 |
| 适用场景 | 目标类有接口 | 目标类无接口 |
五、代理模式与 AOP 的关系
1. AOP 核心概念
AOP(面向切面编程)是一种编程思想,核心是将非业务逻辑(如日志、事务、权限)从业务逻辑中抽离,形成 "切面",并动态织入到业务方法中。
2. AOP 与代理模式的映射
| AOP 概念 | 代理模式对应角色 |
|---|---|
| 切面(Aspect) | 代理类中的增强逻辑(before/after) |
| 连接点(JoinPoint) | 目标类的所有方法 |
| 切入点(Pointcut) | 被增强的具体方法 |
| 通知(Advice) | 前置 / 后置 / 环绕等增强逻辑 |
| 目标对象(Target) | 代理模式中的目标对象 |
| 代理对象(Proxy) | 代理模式中的代理对象 |
3. Spring AOP 的代理选择策略
Spring AOP 底层默认使用动态代理,选择规则:
- 如果目标类实现了接口:优先使用 JDK 动态代理;
- 如果目标类未实现接口:使用 CGLIB 动态代理;
- 可通过配置
spring.aop.proxy-target-class=true强制使用 CGLIB。
六、实战案例:基于代理实现统一日志切面
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.time.LocalDateTime;
// 通用日志代理处理器
public class LogProxyHandler implements InvocationHandler {
private final Object target;
public LogProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 前置日志:记录方法调用信息
String log = String.format(
"[%s] 调用方法:%s.%s,参数:%s,调用者:%s",
LocalDateTime.now(),
target.getClass().getName(),
method.getName(),
args == null ? "无" : String.join(",", args.toString()),
Thread.currentThread().getName()
);
System.out.println("【日志切面】" + log);
// 2. 执行目标方法(处理异常)
Object result = null;
try {
result = method.invoke(target, args);
// 3. 后置日志:记录方法成功执行
System.out.println("【日志切面】方法执行成功,返回值:" + result);
} catch (Exception e) {
// 4. 异常日志:记录方法执行异常
System.out.println("【日志切面】方法执行异常:" + e.getMessage());
throw e; // 抛出异常,不影响业务逻辑
} finally {
// 5. 最终日志:记录方法执行完成
System.out.println("【日志切面】方法执行完成");
}
return result;
}
// 通用代理生成方法
public static <T> T createProxy(T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LogProxyHandler(target)
);
}
}
// 测试:为任意业务类添加日志增强
public class LogProxyTest {
public static void main(String[] args) {
// 1. 业务类(实现接口)
OrderService orderService = LogProxyHandler.createProxy(new OrderServiceImpl());
orderService.createOrder("ORDER_003");
// 2. 若业务类无接口,可替换为CGLIB实现
// UserService userService = CglibLogProxy.createProxy(new UserService());
// userService.addUser("李四");
}
}
总结
- 代理模式分为静态代理和动态代理:静态代理手动编写代理类,适用于简单场景;动态代理(JDK/CGLIB)运行时生成代理类,是 AOP 核心;
- JDK 与 CGLIB 代理的核心区别:JDK 基于接口,CGLIB 基于继承,Spring AOP 会自动选择适配方式;
- 代理模式是 AOP 的底层实现:通过代理类将切面逻辑(日志、事务等)织入目标方法,实现业务与非业务逻辑的解耦。