代理模式是结构型设计模式的一种,核心思想:给一个对象找一个 “代理人”,让代理人代替原对象处理请求,原对象只做核心业务逻辑。
代理模式在Java的使用
声明式事务 @Transactional、AOP 切面增强 @Aspect等等
- 日志记录
- 权限校验
- 耗时统计
- 参数校验
- 异常统一处理
代理对象负责重复的非业务操作,如记录前后日志、权限校验。原对象只需要关注核心业务。
一、核心概念
- 抽象主题(Subject):定义原对象和代理对象的公共接口(同时做的事情)
- 真实主题(RealSubject):真正做事的原对象(例如项目中的核心业务逻辑)
- 代理(Proxy):代替原对象处理请求,可附加额外功能(给业务加的日志等功能)
- 客户端(Client):使用代理对象,不直接接触原对象(调用代理对象,先执行完代理对象的操作再执行核心业务逻辑)
核心特点
- 不修改原对象代码:通过代理扩展功能(开闭原则)
- 职责分离:原对象只做核心业务,代理做日志、权限、缓存等附加操作
- 保护 / 控制访问:代理可以拦截、过滤对原对象的请求
二、两种代理实现方式
1. 静态代理(手动写代理类)
适合代理对象固定的场景,代码写死,编译时就确定代理关系。
代码示例
/** * @author T * @version 1.0 2026-04-19 */ public interface UserService { void deleteUser(Long userId); } public class UserServiceImpl implements UserService{ @Override public void deleteUser(Long userId) { // 这里我们来写核心的业务逻辑 System.out.println("删除用户id为"+userId+"的用户!"); } } public class ProxyServiceImpl implements UserService{ private UserService userService; public ProxyServiceImpl(UserService userService){ this.userService = userService; } @Override public void deleteUser(Long userId) { System.out.println("执行删除前的日志记录、权限校验"); userService.deleteUser(userId); System.out.println("执行删除成功后的操作"); } } public class Client { public static void main(String[] args) { UserService proxy = new ProxyServiceImpl(new UserServiceImpl()); proxy.deleteUser(1L); } }输出:
2. 动态代理(自动生成代理类)
不用手动写代理类,运行时自动生成代理对象,适合批量代理多个类。Java 中常用两种:
- JDK 动态代理:JDK 动态代理通过生成“实现接口的代理类”,因此必须依赖接口,否则无法代理
- CGLIB 动态代理:基于类(无需接口),通过继承 + 方法重写 + 方法拦截
JDK 动态代理示例
public interface UserService { void deleteUser(Long userId); } public class UserServiceImpl implements UserService { @Override public void deleteUser(Long userId) { // 核心业务逻辑 System.out.println("删除用户id为"+userId+"的用户!"); } } public class DynamicProxyFactory { // 传入目标对象,返回 动态代理对象 public static Object getProxy(Object target) { // 方法拦截处理器 return Proxy.newProxyInstance( target.getClass().getClassLoader(), // 类加载器 target.getClass().getInterfaces(), // 目标对象实现的接口 (proxy, method, args) -> { // ===================== 前置增强 ===================== System.out.println("执行删除前的日志记录、权限校验"); // ===================== 执行目标方法 ===================== Object result = method.invoke(target, args); // ===================== 后置增强 ===================== System.out.println("执行删除成功后的操作"); return result; } ); } }输出:
CGLIB动态代理示例
public class UserService { public void deleteUser(Long userId) { System.out.println("删除用户id为"+userId+"的用户!"); } } public class CglibProxyFactory { public static UserService getProxy(Class<?> clazz) { Enhancer enhancer = new Enhancer(); // 继承目标类,生成子类代理 enhancer.setSuperclass(clazz); // 设置回调拦截器 enhancer.setCallback(new UserMethodInterceptor()); return (UserService) enhancer.create(); } } public class UserMethodInterceptor implements MethodInterceptor { /** * 拦截所有目标方法执行 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 前置增强 System.out.println("CGLIB前置:日志记录、权限校验"); // 执行目标原有方法 Object result = proxy.invokeSuper(obj, args); // 后置增强 System.out.println("CGLIB后置:删除操作完毕"); return result; } } public class Client { public static void main(String[] args) { // 直接获取代理对象,无需接口 UserService proxy = CglibProxyFactory.getProxy(UserService.class); proxy.deleteUser(1L); } }结果
三、代理模式的典型应用场景
- 远程代理:代理远程对象(比如 RPC 框架、微服务调用)
- 虚拟代理:延迟创建耗资源的对象(比如:网页先显示图片占位符,加载完再显示真实图片)
- 安全代理:控制访问权限(比如:接口权限校验)
- 日志 / 监控代理:统一记录日志、统计接口耗时
- 缓存代理:给方法加缓存,避免重复执行
- Spring AOP:底层就是动态代理,实现切面编程(日志、事务、权限)
四、静态代理 vs 动态代理
| 特点 | 静态代理 | 动态代理 |
|---|---|---|
| 实现方式 | 手动编写代理类 | 运行时自动生成代理对象 |
| 灵活性 | 低(一个类一个代理) | 高(一个代理类代理多个类) |
| 效率 | 编译期确定,性能高 | 运行时生成,略低 |
| 适用场景 | 代理对象少且固定 | 批量代理、框架开发 |
五、CGlib和JDK动态代理
JDK 动态代理
- 基于接口实现
- 底层反射
- 目标类可以不用继承
- JDK 动态代理通过生成“实现接口的代理类”,因此必须依赖接口,否则无法代理
CGLIB 动态代理
- 基于继承子类实现
- 无需接口
- 不能代理
final类、final方法 - Spring 没有接口时自动用 CGLIB
六、总结
- 代理模式 = 代理人 + 真实对象,代理帮原对象处理杂事,原对象专注核心业务
- 核心价值:不修改原代码,扩展功能(日志、权限、缓存等)
- 两种实现:静态代理(简单)、动态代理(灵活,Spring 底层核心)