文章目录
- 前言
- 一、概念
- 二、核心思想
- 三、Java代码实现
-
- [1. 定义抽象主题(用户服务接口)](#1. 定义抽象主题(用户服务接口))
- [2. 定义真实主题(用户服务实现类)](#2. 定义真实主题(用户服务实现类))
- [3. 静态代理(手动编写代理类,增强逻辑固定)](#3. 静态代理(手动编写代理类,增强逻辑固定))
-
- [3.1 静态代理核心说明](#3.1 静态代理核心说明)
- [3.2 静态代理代码实现](#3.2 静态代理代码实现)
- [4. JDK动态代理(基于接口,增强逻辑可复用)](#4. JDK动态代理(基于接口,增强逻辑可复用))
-
- [4.1 JDK动态代理核心说明](#4.1 JDK动态代理核心说明)
- [4.2 JDK动态代理代码实现](#4.2 JDK动态代理代码实现)
- [5. CGLIB动态代理(适配无接口的类)](#5. CGLIB动态代理(适配无接口的类))
-
- [5.1 CGLIB动态代理核心说明](#5.1 CGLIB动态代理核心说明)
- [5.2 CGLIB动态代理代码实现](#5.2 CGLIB动态代理代码实现)
- [6. 客户端使用代码(静态代理+JDK动态代理+CGLIB动态代理)](#6. 客户端使用代码(静态代理+JDK动态代理+CGLIB动态代理))
- 四、三种代理方式核心对比
- 五、优缺点
-
- [1. 优点](#1. 优点)
- [2. 缺点](#2. 缺点)
- 六、应用场景
- 七、注意事项
- 总结
前言
在AI时代,代码的编写可以被大模型辅助甚至替代,但程序员真正的核心竞争力是技术思维------设计模式这类沉淀了数十年的"内功心法",决定了代码的可维护性、扩展性和稳定性,是AI无法完全替代的核心能力。代理模式作为结构型模式的核心成员,专注于"控制对象访问、增强对象行为",解决了直接访问对象带来的权限管控、性能损耗、功能缺失等问题,是企业级开发中管控对象访问的核心范式。
一、概念
代理模式(Proxy Pattern)是一种结构型设计模式,核心目标是为其他对象提供一个代理以控制对这个对象的访问,同时可在访问前后添加额外的逻辑(如权限校验、日志记录、缓存、延迟加载)。简单来说,代理就像生活中的"经纪人"------明星(目标对象)不直接对接粉丝/商家,而是由经纪人(代理)处理预约、谈判、行程安排等事务,明星只需专注于表演核心功能,经纪人则负责管控访问和额外事务。
在编程中,当需要对一个对象的访问进行控制(如权限校验)、增强其行为(如添加日志),或延迟其创建(如懒加载)时,代理模式是最优选择------代理对象与目标对象实现相同接口,客户端通过代理访问目标对象,既不改变目标对象的核心逻辑,又能灵活添加管控和增强逻辑。
二、核心思想
- 抽象主题(Subject):定义目标对象和代理对象的共同接口,规范核心业务方法,保证客户端能以相同方式调用目标对象和代理对象;
- 真实主题(Real Subject):实现抽象主题接口,是真正执行业务逻辑的目标对象,提供核心功能;
- 代理(Proxy):实现抽象主题接口,同时持有真实主题的引用,在调用真实主题方法的前后添加额外逻辑(如权限校验、日志),并控制对真实主题的访问;
- 客户端(Client):仅依赖抽象主题接口,通过代理对象间接访问真实主题,无需知道真实主题的存在。
代理模式的核心本质是控制访问、增强行为 ------通过代理封装真实主题,既管控了对真实主题的访问权限,又能在不修改真实主题代码的前提下增强其行为,符合"开闭原则"和"单一职责原则"。
三、Java代码实现
以"用户服务接口"场景为例:系统中有UserService接口(包含查询用户、修改用户信息功能),UserServiceImpl是真实实现类。需通过代理模式实现:① 访问权限校验(仅管理员可修改用户信息);② 日志记录(记录接口调用时间和参数);③ 延迟加载(真实对象仅在首次调用时创建)。
1. 定义抽象主题(用户服务接口)
java
/**
* 抽象主题:用户服务接口
* 定义核心业务方法,是代理和真实主题的共同接口
*/
public interface UserService {
// 查询用户信息
String getUserInfo(String userId);
// 修改用户信息
boolean updateUserInfo(String userId, String newInfo);
}
2. 定义真实主题(用户服务实现类)
java
/**
* 真实主题:用户服务实现类
* 实现核心业务逻辑,仅关注功能本身,无额外管控逻辑
*/
public class UserServiceImpl implements UserService {
// 模拟数据库存储用户信息
private final static Map<String, String> USER_DATA = new HashMap<>();
// 静态初始化测试数据
static {
USER_DATA.put("001", "张三,普通用户");
USER_DATA.put("002", "李四,管理员");
}
@Override
public String getUserInfo(String userId) {
// 核心业务:查询用户信息
System.out.println("【真实主题】查询用户" + userId + "信息");
return USER_DATA.getOrDefault(userId, "用户不存在");
}
@Override
public boolean updateUserInfo(String userId, String newInfo) {
// 核心业务:修改用户信息
System.out.println("【真实主题】修改用户" + userId + "信息为:" + newInfo);
USER_DATA.put(userId, newInfo);
return true;
}
}
3. 静态代理(手动编写代理类,增强逻辑固定)
3.1 静态代理核心说明
静态代理是编译期确定代理类与目标类关系的实现方式,开发者需手动编写与目标类实现相同接口的代理类,在代理类中硬编码增强逻辑(如权限、日志),并持有目标类引用。其优点是实现简单、调试方便、无运行时性能损耗;缺点是类数量易膨胀,目标接口变更时代理类需同步修改,增强逻辑复用性差。
3.2 静态代理代码实现
java
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* 静态代理:用户服务代理类
* 手动编写代理类,实现权限校验、日志记录、延迟加载逻辑
*/
public class UserServiceStaticProxy implements UserService {
// 持有真实主题引用(延迟加载:初始为null,首次调用时创建)
private UserService userService;
// 模拟当前登录用户的角色(实际开发中从上下文获取)
private String currentUserRole;
public UserServiceStaticProxy(String currentUserRole) {
this.currentUserRole = currentUserRole;
}
// 延迟加载真实主题(首次调用时创建)
private UserService getRealSubject() {
if (userService == null) {
System.out.println("【静态代理】首次调用,创建真实主题实例");
userService = new UserServiceImpl();
}
return userService;
}
@Override
public String getUserInfo(String userId) {
// 增强逻辑1:日志记录(调用前)
System.out.println("【静态代理】调用getUserInfo,参数:" + userId + ",时间:" + LocalDateTime.now());
// 调用真实主题方法
String result = getRealSubject().getUserInfo(userId);
// 增强逻辑2:日志记录(调用后)
System.out.println("【静态代理】getUserInfo调用完成,结果:" + result);
return result;
}
@Override
public boolean updateUserInfo(String userId, String newInfo) {
// 增强逻辑1:权限校验(调用前)
if (!"管理员".equals(currentUserRole)) {
System.out.println("【静态代理】权限校验失败:非管理员禁止修改用户信息");
return false;
}
// 增强逻辑2:日志记录(调用前)
System.out.println("【静态代理】调用updateUserInfo,参数:" + userId + "," + newInfo + ",时间:" + LocalDateTime.now());
// 调用真实主题方法
boolean result = getRealSubject().updateUserInfo(userId, newInfo);
// 增强逻辑3:日志记录(调用后)
System.out.println("【静态代理】updateUserInfo调用完成,结果:" + result);
return result;
}
}
4. JDK动态代理(基于接口,增强逻辑可复用)
4.1 JDK动态代理核心说明
JDK动态代理是JDK原生支持、运行时动态生成代理类 的实现方式,基于java.lang.reflect.Proxy和InvocationHandler实现。核心要求:目标类必须实现至少一个接口,代理类会动态实现与目标类相同的接口,通过反射调用目标方法。其优点是无需手动编写代理类、增强逻辑可复用;缺点是目标类必须实现接口,存在轻微反射性能损耗。
4.2 JDK动态代理代码实现
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* 动态代理:InvocationHandler实现类
* 定义通用的增强逻辑(权限、日志),可适配任意接口,无需手动编写代理类
*/
public class UserServiceDynamicProxy implements InvocationHandler {
// 持有真实主题对象(任意类型)
private Object target;
// 当前用户角色
private String currentUserRole;
public UserServiceDynamicProxy(Object target, String currentUserRole) {
this.target = target;
this.currentUserRole = currentUserRole;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
// 获取方法名,区分不同业务方法
String methodName = method.getName();
try {
// 增强逻辑1:日志记录(调用前)
System.out.println("【动态代理】调用" + methodName + ",参数:" + Arrays.toString(args) + ",时间:" + LocalDateTime.now());
// 增强逻辑2:权限校验(仅updateUserInfo方法需要)
if ("updateUserInfo".equals(methodName) && !"管理员".equals(currentUserRole)) {
System.out.println("【动态代理】权限校验失败:非管理员禁止修改用户信息");
return false;
}
// 调用真实主题的方法(核心:通过反射调用)
result = method.invoke(target, args);
// 增强逻辑3:日志记录(调用后)
System.out.println("【动态代理】" + methodName + "调用完成,结果:" + result);
} catch (Exception e) {
// 增强逻辑4:异常处理
System.out.println("【动态代理】调用" + methodName + "异常:" + e.getMessage());
throw e;
}
return result;
}
// 生成代理对象的工具方法
public static Object createProxy(Object target, String currentUserRole) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器:与目标类保持一致
target.getClass().getInterfaces(), // 目标类实现的接口:代理类会实现这些接口
new UserServiceDynamicProxy(target, currentUserRole) // 调用处理器:封装增强逻辑
);
}
}
5. CGLIB动态代理(适配无接口的类)
5.1 CGLIB动态代理核心说明
CGLIB(Code Generation Library)是基于ASM字节码生成框架的动态代理方式,无需目标类实现接口,通过在运行时生成目标类的子类作为代理类,重写目标类的非final方法实现增强。其优点是适配范围更广(支持无接口类)、调用效率略高于JDK动态代理;缺点是需引入CGLIB依赖,无法代理final类和final方法。
5.2 CGLIB动态代理代码实现
java
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* CGLIB动态代理:适配无实现接口的类(需引入cglib依赖)
* 核心:继承目标类,生成子类作为代理对象,重写非final方法
*/
public class UserServiceCglibProxy implements MethodInterceptor {
// 目标对象
private Object target;
// 当前用户角色
private String currentUserRole;
public UserServiceCglibProxy(Object target, String currentUserRole) {
this.target = target;
this.currentUserRole = currentUserRole;
}
// 生成CGLIB代理对象
public Object createProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass()); // 设置父类为目标类(核心:生成子类)
enhancer.setCallback(this); // 设置回调函数:增强逻辑写在intercept方法中
return enhancer.create(); // 创建代理对象(目标类的子类)
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object result = null;
String methodName = method.getName();
try {
// 增强逻辑:日志+权限校验(同JDK动态代理)
System.out.println("【CGLIB代理】调用" + methodName + ",参数:" + Arrays.toString(args) + ",时间:" + LocalDateTime.now());
if ("updateUserInfo".equals(methodName) && !"管理员".equals(currentUserRole)) {
System.out.println("【CGLIB代理】权限校验失败:非管理员禁止修改用户信息");
return false;
}
// 调用目标方法(核心:通过MethodProxy调用父类方法,避免反射损耗)
result = proxy.invokeSuper(obj, args);
System.out.println("【CGLIB代理】" + methodName + "调用完成,结果:" + result);
} catch (Exception e) {
System.out.println("【CGLIB代理】调用" + methodName + "异常:" + e.getMessage());
throw e;
}
return result;
}
}
// CGLIB依赖(Maven)
// <dependency>
// <groupId>cglib</groupId>
// <artifactId>cglib-nodep</artifactId>
// <version>3.3.0</version>
// </dependency>
6. 客户端使用代码(静态代理+JDK动态代理+CGLIB动态代理)
java
/**
* 客户端:用户服务调用层
* 分别测试静态代理、JDK动态代理、CGLIB动态代理的使用
*/
public class Client {
public static void main(String[] args) {
// ====================== 测试静态代理 ======================
System.out.println("=== 静态代理测试(普通用户) ===");
UserService staticProxyForNormal = new UserServiceStaticProxy("普通用户");
// 普通用户查询用户信息(允许)
System.out.println(staticProxyForNormal.getUserInfo("001"));
// 普通用户修改用户信息(禁止)
staticProxyForNormal.updateUserInfo("001", "张三,VIP用户");
System.out.println("\n=== 静态代理测试(管理员) ===");
UserService staticProxyForAdmin = new UserServiceStaticProxy("管理员");
// 管理员修改用户信息(允许)
staticProxyForAdmin.updateUserInfo("001", "张三,VIP用户");
// 管理员查询修改后的信息
System.out.println(staticProxyForAdmin.getUserInfo("001"));
// ====================== 测试JDK动态代理 ======================
System.out.println("\n=== JDK动态代理测试(普通用户) ===");
UserService realService = new UserServiceImpl();
// 创建JDK动态代理对象(需传入实现接口的目标对象)
UserService dynamicProxyForNormal = (UserService) UserServiceDynamicProxy.createProxy(realService, "普通用户");
// 普通用户查询用户信息(允许)
System.out.println(dynamicProxyForNormal.getUserInfo("002"));
// 普通用户修改用户信息(禁止)
dynamicProxyForNormal.updateUserInfo("002", "李四,超级管理员");
System.out.println("\n=== JDK动态代理测试(管理员) ===");
UserService dynamicProxyForAdmin = (UserService) UserServiceDynamicProxy.createProxy(realService, "管理员");
// 管理员修改用户信息(允许)
dynamicProxyForAdmin.updateUserInfo("002", "李四,超级管理员");
// 管理员查询修改后的信息
System.out.println(dynamicProxyForAdmin.getUserInfo("002"));
// ====================== 测试CGLIB动态代理 ======================
System.out.println("\n=== CGLIB动态代理测试(管理员) ===");
// 目标类无需实现接口(此处用实现接口的类仅为演示,实际可用于无接口类)
UserServiceImpl realServiceForCglib = new UserServiceImpl();
UserServiceCglibProxy cglibProxy = new UserServiceCglibProxy(realServiceForCglib, "管理员");
// 创建CGLIB代理对象(目标类的子类)
UserServiceImpl proxyService = (UserServiceImpl) cglibProxy.createProxy();
// 调用代理方法
proxyService.updateUserInfo("003", "王五,新用户");
System.out.println(proxyService.getUserInfo("003"));
}
}
输出结果:
=== 静态代理测试(普通用户) ===
【静态代理】调用getUserInfo,参数:001,时间:2026-03-23T15:30:00
【静态代理】首次调用,创建真实主题实例
【真实主题】查询用户001信息
【静态代理】getUserInfo调用完成,结果:张三,普通用户
张三,普通用户
【静态代理】调用updateUserInfo,参数:001,张三,VIP用户,时间:2026-03-23T15:30:00
【静态代理】权限校验失败:非管理员禁止修改用户信息
false
=== 静态代理测试(管理员) ===
【静态代理】调用updateUserInfo,参数:001,张三,VIP用户,时间:2026-03-23T15:30:00
【真实主题】修改用户001信息为:张三,VIP用户
【静态代理】updateUserInfo调用完成,结果:true
【静态代理】调用getUserInfo,参数:001,时间:2026-03-23T15:30:00
【真实主题】查询用户001信息
【静态代理】getUserInfo调用完成,结果:张三,VIP用户
张三,VIP用户
=== JDK动态代理测试(普通用户) ===
【动态代理】调用getUserInfo,参数:[002],时间:2026-03-23T15:30:00
【真实主题】查询用户002信息
【动态代理】getUserInfo调用完成,结果:李四,管理员
李四,管理员
【动态代理】调用updateUserInfo,参数:[002, 李四,超级管理员],时间:2026-03-23T15:30:00
【动态代理】权限校验失败:非管理员禁止修改用户信息
false
=== JDK动态代理测试(管理员) ===
【动态代理】调用updateUserInfo,参数:[002, 李四,超级管理员],时间:2026-03-23T15:30:00
【真实主题】修改用户002信息为:李四,超级管理员
【动态代理】updateUserInfo调用完成,结果:true
【动态代理】调用getUserInfo,参数:[002],时间:2026-03-23T15:30:00
【真实主题】查询用户002信息
【动态代理】getUserInfo调用完成,结果:李四,超级管理员
李四,超级管理员
=== CGLIB动态代理测试(管理员) ===
【CGLIB代理】调用updateUserInfo,参数:[003, 王五,新用户],时间:2026-03-23T15:30:00
【真实主题】修改用户003信息为:王五,新用户
【CGLIB代理】updateUserInfo调用完成,结果:true
【CGLIB代理】调用getUserInfo,参数:[003],时间:2026-03-23T15:30:00
【真实主题】查询用户003信息
【CGLIB代理】getUserInfo调用完成,结果:王五,新用户
王五,新用户
四、三种代理方式核心对比
| 维度 | 静态代理 | JDK动态代理 | CGLIB动态代理 |
|---|---|---|---|
| 依赖条件 | 无 | 目标类必须实现接口 | 需引入CGLIB依赖,目标类非final |
| 实现原理 | 手动编写代理类,实现与目标类相同接口 | 运行时动态生成接口实现类,基于反射调用 | 运行时生成目标类子类,基于字节码调用 |
| 增强逻辑编写 | 硬编码到代理类的每个方法 | 统一写在InvocationHandler.invoke方法 |
统一写在MethodInterceptor.intercept方法 |
| 性能 | 无损耗(编译期确定) | 轻微反射损耗(JDK8+已优化) | 字节码生成,损耗略低于JDK动态代理 |
| 维护成本 | 高(接口变更需同步修改) | 低(通用逻辑可复用) | 低(通用逻辑可复用) |
| 适用场景 | 目标类少、逻辑固定 | 目标类实现接口的场景 | 目标类无接口、需代理非接口方法的场景 |
五、优缺点
1. 优点
- 控制访问:可在调用目标对象前添加权限校验、限流、熔断等逻辑,管控对目标对象的访问;
- 增强行为:可在调用前后添加日志、缓存、事务、监控等逻辑,增强目标对象功能,且无需修改目标对象代码;
- 延迟加载:可延迟创建目标对象(如静态代理中的懒加载),减少系统初始化时的资源消耗;
- 解耦客户端与目标对象:客户端仅依赖抽象接口,代理层封装了目标对象的访问逻辑,降低耦合度;
- 动态代理灵活性高:JDK/CGLIB动态代理可适配任意类/接口,无需手动编写大量静态代理类。
2. 缺点
- 静态代理类数量膨胀:每新增一个接口,需手动编写对应的静态代理类,类数量增多,维护成本高;
- 动态代理学习成本高:JDK/CGLIB动态代理涉及反射、字节码生成,理解和调试难度高于静态代理;
- 性能损耗:动态代理基于反射/字节码生成,调用效率略低于静态代理和直接调用(但日常开发中可忽略);
- CGLIB限制:CGLIB代理无法代理final类和final方法(因为无法生成子类)。
六、应用场景
代理模式适用于需控制对象访问、增强对象行为、延迟对象创建的场景:
- 权限管控:如接口鉴权、数据权限控制(仅允许特定角色访问/修改数据);
- 日志/监控:如记录接口调用日志、统计接口响应时间、埋点监控;
- 缓存增强:如给查询接口添加缓存,避免重复查询数据库;
- 延迟加载:如MyBatis的Mapper代理(延迟创建Mapper实例)、Hibernate的懒加载(延迟加载关联对象);
- 远程代理:如RPC框架(Dubbo、Spring Cloud)的远程代理,客户端通过代理调用远程服务,代理封装网络通信逻辑;
- 框架中的应用:Spring AOP的底层实现(JDK动态代理+CGLIB动态代理)、MyBatis的Mapper代理、Spring的事务代理。
七、注意事项
- 区分代理模式与装饰器模式:代理模式关注"控制访问"(如权限、延迟加载),装饰器模式关注"增强功能"(如添加配料);代理通常持有一个目标对象,装饰器可嵌套多个;
- 选择合适的代理方式:有接口时优先用JDK动态代理,无接口时用CGLIB代理,简单场景用静态代理;
- 避免过度代理:简单接口(无管控/增强需求)无需使用代理,直接调用目标对象更简洁;
- 动态代理的性能优化:高频调用的接口可使用静态代理或缓存动态代理对象,减少反射开销。
总结
- 代理模式核心是控制对象访问、增强对象行为,通过代理封装目标对象,在不修改目标对象代码的前提下实现管控和增强;
- 静态代理简单易实现但类数量膨胀,JDK动态代理依赖接口、基于反射,CGLIB动态代理无接口限制、基于字节码生成,需根据场景选择;
- 三种代理方式核心差异在于实现原理和依赖条件:静态代理编译期确定,JDK动态代理依赖接口,CGLIB动态代理依赖字节码生成;
- Spring AOP是代理模式的经典应用(底层基于JDK/CGLIB动态代理),理解代理模式是掌握Spring AOP的关键。