设计模式之代理模式
设计模式之代理模式
在 Java 开发的进阶之路中,设计模式是区分初级程序员与架构师的核心分水岭,而代理模式作为结构型设计模式中最经典、应用最广泛的模式之一,是 Spring AOP、Mybatis、Dubbo、RPC 框架等主流技术的底层核心原理。
对于新手而言,代理模式看似抽象晦涩,但本质上和我们生活中的中介、代购、经纪人、律师完全一致;对于架构师而言,代理模式是实现无侵入式功能增强、权限控制、远程调用、懒加载的终极方案,同时完美契合软件工程的七大设计原则。
本文将由浅入深、通俗易懂地拆解代理模式,从生活案例到代码实战,从静态代理到动态代理(JDK/CGLIB),从设计原则结合到框架源码分析,全方位覆盖代理模式的所有知识点,帮助你彻底掌握这一核心设计模式。
第一部分:代理模式基础认知(由浅入深第一步)
1.1 生活中的代理:秒懂核心逻辑
在正式讲解技术概念前,我们先看 3 个生活中最常见的代理场景,你会发现代理的本质从未改变:
-
租房代理(中介)
租客(客户端)想租房,不会直接找房东(真实业务对象),而是找房产中介(代理对象)。中介帮房东带看、签合同、收押金,租客只和中介沟通,房东只负责收租。
中介就是房东的代理,租客不直接接触房东。
-
明星经纪人
商家想找明星合作,不会直接联系明星,而是联系经纪人。经纪人负责谈报价、排档期、签合同,明星只负责表演。
经纪人是明星的代理。
-
海外代购
你想买海外商品,不会亲自出国,而是找代购。代购帮你采购、报关、物流,你只需要付款收货。
代购是你的代理。
生活代理的核心共性
- 存在一个真实对象:拥有核心能力(房东有房、明星会表演、你有购买需求);
- 存在一个代理对象:代替真实对象处理辅助工作(中介带看、经纪人谈合作);
- 客户端不直接接触真实对象:所有交互都通过代理完成;
- 代理不创造核心能力:最终的核心业务还是由真实对象完成。
这就是代理模式的底层逻辑:通过代理对象间接访问真实对象,在不修改真实对象的前提下,对功能进行增强或控制访问。
1.2 代理模式的官方定义(GoF)
代理模式(Proxy Pattern),也叫委托模式,是GoF23 种设计模式中的结构型设计模式。
官方定义:
为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,可用于隐藏目标对象、增强目标对象功能、控制访问权限等。
简单翻译:
不让客户端直接调用目标对象,而是让代理对象拦在中间,客户端调用代理,代理再调用目标对象,同时代理可以在调用前后做额外操作。
1.3 代理模式的四大核心角色
代理模式的结构非常固定,包含4 个核心角色,所有代理实现(静态 / 动态)都遵循这个结构:
| 角色名称 | 英文名称 | 核心作用 | 生活类比 |
|---|---|---|---|
| 抽象主题 | Subject | 定义真实主题和代理主题的公共接口,保证代理和真实对象对外暴露的方法一致 | 租房接口(带看、签合同) |
| 真实主题 | RealSubject | 实现抽象主题,真正执行业务逻辑的对象,是代理最终要调用的目标 | 房东(真正拥有房子) |
| 代理主题 | Proxy | 实现抽象主题,持有真实主题的引用,在调用真实主题前后做增强 / 控制 | 中介(持有房东信息) |
| 客户端 | Client | 使用代理对象,不直接使用真实主题对象 | 租客 |
标准 UML 结构
- Subject 接口定义方法;
- RealSubject 实现 Subject,实现核心业务;
- Proxy 实现 Subject,内部组合 RealSubject;
- Client 调用 Proxy 的方法,Proxy 调用 RealSubject 的方法。
1.4 代理模式的核心价值
为什么我们要多一层代理,而不是直接调用真实对象?核心价值有 5 点:
- 无侵入增强:不修改真实对象的代码,就能添加日志、事务、缓存等功能;
- 访问控制:对真实对象的方法进行权限校验,禁止非法调用;
- 远程调用:代理本地对象,实际调用远程服务(Dubbo/RPC);
- 懒加载:延迟创建真实对象,节省资源(虚拟代理);
- 解耦分离:客户端与真实对象完全解耦,符合软件工程设计原则。
第二部分:静态代理 —— 入门级实现(由浅入深第二步)
2.1 静态代理原理
静态代理是代理模式最基础的实现方式:
- 手动编写代理类,代理类和真实类在编译期就已经确定;
- 代理类实现抽象主题接口,内部持有真实主题对象;
- 客户端调用代理类的方法,代理类在前后做增强,再调用真实类的方法。
关键词:编译期生成、手动编码、一对一代理。
2.2 静态代理代码实战
我们以用户服务为例,需求:在不修改用户业务代码的前提下,为用户的增删改方法添加日志记录功能。
步骤 1:定义抽象主题(Subject)
创建公共接口 IUserService,定义用户业务方法:
/**
* 抽象主题:用户服务接口
*/
public interface IUserService {
// 新增用户
void addUser(String username);
// 删除用户
void deleteUser(Long id);
}
步骤 2:定义真实主题(RealSubject)
实现接口,编写核心业务逻辑(无任何日志代码):
/**
* 真实主题:用户服务实现类
* 只关注核心业务,不处理日志、事务等非核心功能
*/
public class UserServiceImpl implements IUserService {
@Override
public void addUser(String username) {
System.out.println("【核心业务】执行新增用户:" + username);
}
@Override
public void deleteUser(Long id) {
System.out.println("【核心业务】执行删除用户:" + id);
}
}
步骤 3:定义代理主题(Proxy)
手动编写代理类,实现相同接口,组合真实对象,添加日志增强:
/**
* 静态代理类:用户服务代理
* 实现相同接口,持有真实对象引用,添加日志增强
*/
public class UserServiceStaticProxy implements IUserService {
// 持有真实主题对象(组合关系)
private final IUserService userService;
// 构造方法注入真实对象
public UserServiceStaticProxy(IUserService userService) {
this.userService = userService;
}
@Override
public void addUser(String username) {
// 前置增强:打印日志
System.out.println("【静态代理】前置日志:开始调用addUser方法,参数:" + username);
// 调用真实主题的核心方法
userService.addUser(username);
// 后置增强:打印日志
System.out.println("【静态代理】后置日志:addUser方法执行完成\n");
}
@Override
public void deleteUser(Long id) {
System.out.println("【静态代理】前置日志:开始调用deleteUser方法,参数:" + id);
userService.deleteUser(id);
System.out.println("【静态代理】后置日志:deleteUser方法执行完成\n");
}
}
步骤 4:客户端测试(Client)
客户端只使用代理对象,不直接使用真实对象:
/**
* 客户端:测试静态代理
*/
public class Client {
public static void main(String[] args) {
// 1. 创建真实对象
IUserService realService = new UserServiceImpl();
// 2. 创建代理对象,注入真实对象
IUserService proxyService = new UserServiceStaticProxy(realService);
// 3. 客户端调用代理对象的方法
proxyService.addUser("张三");
proxyService.deleteUser(1001L);
}
}
运行结果
【静态代理】前置日志:开始调用addUser方法,参数:张三
【核心业务】执行新增用户:张三
【静态代理】后置日志:addUser方法执行完成
【静态代理】前置日志:开始调用deleteUser方法,参数:1001
【核心业务】执行删除用户:1001
【静态代理】后置日志:deleteUser方法执行完成
2.3 静态代理优缺点分析
优点
- 实现简单:入门门槛低,适合简单场景;
- 编译期确定:性能高,无运行时开销;
- 职责清晰:真实对象只做核心业务,代理做增强。
缺点(致命痛点)
- 代码冗余:一个接口需要一个代理类,100 个接口就要写 100 个代理类;
- 维护困难:接口新增方法,所有代理类都必须修改,违反开闭原则;
- 扩展性差:无法通用,只能针对特定接口代理。
2.4 静态代理适用场景
静态代理仅适用于代理类数量极少、接口几乎不变化的场景,企业开发中几乎不用,仅作为学习代理模式的入门案例。
第三部分:动态代理 —— 进阶级实现(解决静态代理痛点)
静态代理的核心问题是手动编写代理类,编译期固定,而动态代理彻底解决了这个问题:
动态代理:运行时自动生成代理类字节码,无需手动编写代理类,一个代理类可以代理所有接口 / 类。
Java 中主流的动态代理有两种:
- JDK 动态代理:Java 原生,基于接口实现;
- CGLIB 动态代理:第三方库,基于继承实现。
3.1 JDK 动态代理(Java 原生,必学)
3.1.1 核心原理
JDK 动态代理是 Java 官方提供的动态代理方案,基于接口实现:
- 运行时通过反射生成代理类字节码;
- 生成的代理类实现目标接口,继承
java.lang.reflect.Proxy类; - 通过
InvocationHandler拦截所有方法调用,统一做增强。
3.1.2 核心 API
JDK 动态代理只需要两个核心类:
java.lang.reflect.Proxy:用于生成代理对象的核心类;- 核心方法:
newProxyInstance(类加载器, 接口数组, 调用处理器)
- 核心方法:
java.lang.reflect.InvocationHandler:方法调用处理器;- 核心方法:
invoke(代理对象, 目标方法, 方法参数)→ 拦截所有方法。
- 核心方法:
3.1.3 JDK 动态代理代码实战
我们复用第二部分的 IUserService 和 UserServiceImpl,不编写任何代理类,实现通用日志代理。
步骤 1:编写通用调用处理器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* JDK动态代理:通用方法调用处理器
* 所有被代理的方法都会进入invoke方法
*/
public class LogInvocationHandler implements InvocationHandler {
// 持有真实目标对象
private final Object target;
public LogInvocationHandler(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 {
// 前置增强:通用日志
System.out.println("【JDK动态代理】前置日志:方法=" + method.getName() + ",参数=" + (args != null ? args[0] : null));
// 调用真实目标对象的方法
Object result = method.invoke(target, args);
// 后置增强:通用日志
System.out.println("【JDK动态代理】后置日志:方法=" + method.getName() + " 执行完成\n");
return result;
}
}
步骤 2:编写代理工厂(通用生成代理对象)
import java.lang.reflect.Proxy;
/**
* 代理工厂:通用生成JDK动态代理对象
*/
public class JdkProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标对象的类加载器
target.getClass().getInterfaces(), // 目标对象实现的所有接口
new LogInvocationHandler(target) // 方法调用处理器
);
}
}
步骤 3:客户端测试
/**
* 客户端:测试JDK动态代理
*/
public class JdkProxyClient {
public static void main(String[] args) {
// 1. 创建真实对象
IUserService realService = new UserServiceImpl();
// 2. 通过工厂生成代理对象
IUserService proxyService = (IUserService) JdkProxyFactory.getProxy(realService);
// 3. 调用代理方法
proxyService.addUser("李四");
proxyService.deleteUser(1002L);
}
}
运行结果
【JDK动态代理】前置日志:方法=addUser,参数=李四
【核心业务】执行新增用户:李四
【JDK动态代理】后置日志:方法=addUser 执行完成
【JDK动态代理】前置日志:方法=deleteUser,参数=1002
【核心业务】执行删除用户:1002
【JDK动态代理】后置日志:方法=deleteUser 执行完成
3.1.4 JDK 动态代理的局限性
- 必须实现接口:如果目标类没有实现任何接口,无法使用 JDK 动态代理;
- 基于反射:性能略低于 CGLIB(高版本 JDK 已优化)。
3.2 CGLIB 动态代理(无接口也能代理)
3.2.1 简介
CGLIB(Code Generation Library)是一个第三方字节码生成框架,Spring、Mybatis 都依赖它。
核心原理:基于继承,运行时生成目标类的子类作为代理对象,重写父类方法实现增强。
3.2.2 核心依赖
Spring 框架已内置 CGLIB,单独使用需引入 Maven 依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
3.2.3 核心 API
Enhancer:代理生成器,用于创建代理对象;MethodInterceptor:方法拦截器,拦截所有方法调用。
3.2.4 CGLIB 动态代理代码实战
我们创建一个没有实现任何接口的用户服务类,用 CGLIB 实现代理增强。
步骤 1:创建无接口的真实主题
/**
* 真实主题:无接口的用户服务
*/
public class UserService {
public void addUser(String username) {
System.out.println("【核心业务】执行新增用户:" + username);
}
public void deleteUser(Long id) {
System.out.println("【核心业务】执行删除用户:" + id);
}
}
步骤 2:编写 CGLIB 方法拦截器
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* CGLIB动态代理:方法拦截器
*/
public class CglibLogInterceptor implements MethodInterceptor {
/**
* 拦截所有方法
* @param o 代理对象
* @param method 目标方法
* @param args 参数
* @param methodProxy 方法代理(用于调用父类方法,比反射快)
* @return 返回值
* @throws Throwable 异常
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 前置增强
System.out.println("【CGLIB动态代理】前置日志:方法=" + method.getName());
// 调用父类(真实对象)的方法(CGLIB推荐方式,性能更高)
Object result = methodProxy.invokeSuper(o, args);
// 后置增强
System.out.println("【CGLIB动态代理】后置日志:方法=" + method.getName() + " 执行完成\n");
return result;
}
}
步骤 3:编写 CGLIB 代理工厂
import net.sf.cglib.proxy.Enhancer;
/**
* CGLIB代理工厂
*/
public class CglibProxyFactory {
public static Object getProxy(Class<?> targetClass) {
// 1. 创建Enhancer对象
Enhancer enhancer = new Enhancer();
// 2. 设置父类(目标类)
enhancer.setSuperclass(targetClass);
// 3. 设置方法拦截器
enhancer.setCallback(new CglibLogInterceptor());
// 4. 创建代理对象
return enhancer.create();
}
}
步骤 4:客户端测试
/**
* 客户端:测试CGLIB动态代理
*/
public class CglibProxyClient {
public static void main(String[] args) {
// 生成代理对象
UserService proxyService = (UserService) CglibProxyFactory.getProxy(UserService.class);
// 调用方法
proxyService.addUser("王五");
proxyService.deleteUser(1003L);
}
}
运行结果
【CGLIB动态代理】前置日志:方法=addUser
【核心业务】执行新增用户:王五
【CGLIB动态代理】后置日志:方法=addUser 执行完成
【CGLIB动态代理】前置日志:方法=deleteUser
【核心业务】执行删除用户:1003
【CGLIB动态代理】后置日志:方法=deleteUser 执行完成
3.2.5 CGLIB 注意事项
- 不能代理 final 类:final 类无法被继承,CGLIB 会报错;
- 不能增强 final 方法:final 方法无法被重写,拦截失效;
- 性能优于 JDK 动态代理:基于字节码生成,无反射开销。
3.3 JDK 动态代理 vs CGLIB 动态代理(全面对比)
| 对比维度 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|
| 实现原理 | 基于接口,实现目标接口 | 基于继承,生成目标类子类 |
| 依赖要求 | Java 原生,无第三方依赖 | 需引入 CGLIB 依赖(Spring 内置) |
| 限制条件 | 目标类必须实现接口 | 目标类不能是 final,方法不能是 final |
| 性能 | 低版本 JDK 反射较慢,高版本优化 | 字节码生成,性能更高 |
| Spring 使用 | 目标类实现接口时默认使用 | 目标类无接口时使用(Spring Boot2.0 + 默认 CGLIB) |
| 应用场景 | 接口代理、Mybatis Mapper | 无接口类代理、Spring AOP |
第四部分:代理模式与七大设计原则深度结合(架构师核心)
软件工程的七大设计原则是架构设计的基石,代理模式之所以成为经典设计模式,核心原因是它完美契合所有七大设计原则。这也是企业面试、架构设计中最常考察的知识点。
我们逐一结合代理模式进行深度解析:
4.1 单一职责原则(Single Responsibility Principle)
原则定义
一个类只负责一项职责,一个类只有一个引起它变化的原因。
代理模式的体现
- 真实主题类:只负责核心业务逻辑(如用户增删改);
- 代理类:只负责非核心增强逻辑(如日志、事务、权限);
- 职责完全分离,互不干扰。
代码佐证
UserServiceImpl 只写业务代码,LogInvocationHandler 只写日志代码,两个类各司其职,完全符合单一职责。
4.2 开闭原则(Open Closed Principle)
原则定义
对扩展开放,对修改关闭;软件实体应通过扩展实现变化,而非修改原有代码。
代理模式的体现
- 不修改真实主题类:核心业务代码完全不动;
- 通过代理扩展功能:新增日志、缓存、事务等功能,只需要新增代理类 / 拦截器;
- 拥抱变化,无侵入式扩展。
代码佐证
我们为用户服务添加日志功能,从未修改 UserServiceImpl 一行代码,仅通过动态代理实现扩展,完美符合开闭原则。
4.3 里氏替换原则(Liskov Substitution Principle)
原则定义
子类 / 代理类必须能够替换父类 / 接口,且程序逻辑不变;保证继承 / 实现的正确性。
代理模式的体现
- 代理类实现了抽象主题接口;
- 客户端可以用代理对象无缝替换真实对象,业务逻辑不受影响;
- 保证了程序的兼容性和稳定性。
代码佐证
客户端将 realService 替换为 proxyService,业务执行结果完全一致,符合里氏替换原则。
4.4 依赖倒置原则(Dependency Inversion Principle)
原则定义
- 高层模块依赖抽象,不依赖具体;
- 抽象不依赖具体,具体依赖抽象。
代理模式的体现
- 客户端依赖抽象主题接口(IUserService),不依赖具体的真实类(UserServiceImpl)和代理类;
- 真实类和代理类都依赖抽象接口;
- 彻底解耦高层与底层实现。
代码佐证
客户端代码中,变量类型是 IUserService 接口,而非具体实现类,符合依赖倒置。
4.5 接口隔离原则(Interface Segregation Principle)
原则定义
使用多个专门的接口,不使用单一的总接口;客户端不应依赖不需要的方法。
代理模式的体现
- 抽象主题接口是精简的、专门的(如用户服务接口只定义用户方法);
- 代理类只实现需要的接口方法,不依赖冗余方法;
- 避免接口臃肿,保证接口的单一性。
4.6 迪米特法则(最少知道原则,Law of Demeter)
原则定义
一个对象应当对其他对象有尽可能少的了解;只和直接朋友通信,不和陌生人说话。
代理模式的体现
- 客户端只认识代理对象,完全不知道真实对象的存在;
- 客户端不依赖真实对象的内部细节,降低系统耦合;
- 代理对象屏蔽了真实对象的复杂性。
代码佐证
租客(客户端)只和中介(代理)沟通,不知道房东(真实对象)的任何信息,完美符合迪米特法则。
4.7 合成复用原则(Composite Reuse Principle)
原则定义
优先使用组合 / 聚合关系复用代码,不优先使用继承;继承会导致强耦合。
代理模式的体现
- 代理类组合真实对象(持有引用),而非继承真实对象;
- 复用真实对象的核心功能,同时避免继承的强耦合;
- 灵活、低耦合的复用方式。
代码佐证
静态代理中 UserServiceStaticProxy 持有 IUserService 对象,是组合关系,而非继承,符合合成复用原则。
第五部分:代理模式的分类与企业级实战场景
代理模式根据用途可以分为 6 大类,覆盖企业开发 90% 的场景,我们结合实战讲解:
5.1 远程代理(Remote Proxy)
核心作用
代理远程服务对象,让客户端像调用本地方法一样调用远程服务。
实战场景
Dubbo、Spring Cloud、RMI、RPC 框架的底层核心。
- 客户端调用本地代理对象;
- 代理对象通过网络请求调用远程服务器;
- 屏蔽网络通信细节,简化调用。
5.2 虚拟代理(Virtual Proxy)
核心作用
延迟创建重量级对象,节省资源,实现懒加载。
实战场景
图片加载、大文件加载:
- 页面先显示代理的占位符图片;
- 当用户需要查看时,再加载真实高清图片;
- 避免一次性加载大量资源导致内存溢出。
5.3 保护代理(Protection Proxy)
核心作用
控制真实对象的访问权限,拦截非法调用。
实战场景
权限校验、登录拦截:
- 代理对象在调用方法前校验用户是否登录、是否有权限;
- 无权限直接抛出异常,不执行核心业务。
代码示例(保护代理)
// 在invoke方法中添加权限校验
if (!isAdmin(args[0])) {
throw new RuntimeException("无权限访问!");
}
5.4 缓存代理(Cache Proxy)
核心作用
为真实对象的方法添加缓存,避免重复执行耗时操作。
实战场景
数据查询接口:
- 代理先查询缓存;
- 缓存有数据直接返回,无数据再调用真实方法;
- 提升系统性能。
5.5 日志 / 事务 / 性能监控代理(企业最常用)
核心作用
为业务方法添加非侵入式的日志、事务、性能监控。
实战场景
Spring AOP 的核心实现原理:
- 事务管理:
@Transactional底层通过动态代理实现事务开启 / 提交 / 回滚; - 日志记录:统一打印接口日志;
- 性能监控:统计方法执行耗时。
5.6 Spring AOP 实战:代理模式的终极应用
Spring AOP(面向切面编程)完全基于动态代理实现:
- 目标类实现接口 → JDK 动态代理;
- 目标类无接口 → CGLIB 动态代理;
- 通过切面(Aspect)定义增强逻辑,代理自动织入到目标方法。
这是企业开发中代理模式最核心的应用场景。
第六部分:主流框架源码中的代理模式解析
6.1 Spring AOP 代理创建源码
Spring AOP 通过 AopProxy 接口统一代理创建:
JdkDynamicAopProxy:实现 JDK 动态代理;CglibAopProxy:实现 CGLIB 动态代理;DefaultAopProxyFactory:根据目标类自动选择代理方式。
核心源码逻辑:
public AopProxy createAopProxy(AdvisedSupport config) {
// 目标类有接口 → JDK代理
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
// 无接口 → CGLIB代理
return new CglibAopProxy(config);
}
6.2 Mybatis Mapper 代理模式源码
Mybatis 中,我们只写 Mapper 接口,不写实现类,Mybatis 通过 JDK 动态代理生成实现类:
MapperProxyFactory:生成代理对象;MapperProxy:实现InvocationHandler,拦截接口方法;- 拦截后执行 SQL 语句,返回结果。
核心源码:
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface);
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),
new Class[]{mapperInterface}, mapperProxy);
}
第七部分:代理模式优缺点、适用场景与选型
7.1 代理模式优点
- 解耦彻底:客户端与真实对象完全分离;
- 无侵入增强:不修改核心代码,扩展功能;
- 访问控制:权限、远程、懒加载统一管控;
- 通用灵活:动态代理可代理所有类 / 接口;
- 符合设计原则:完美契合七大设计原则。
7.2 代理模式缺点
- 静态代理冗余:代码量大,维护难;
- 动态代理调试复杂:运行时生成类,断点调试困难;
- 增加调用层级:性能略有损耗(可忽略)。
7.3 适用场景总结
- 需要对方法进行增强(日志、事务、缓存);
- 需要控制访问(权限、远程调用);
- 需要懒加载重量级对象;
- 框架底层设计(Spring、Mybatis、Dubbo)。
7.4 代理模式选型指南
- 简单场景、接口固定 → 静态代理;
- 目标类实现接口 → JDK 动态代理;
- 目标类无接口 → CGLIB 动态代理;
- 企业开发 → 直接使用 Spring AOP(底层自动选择代理)。
第八部分:代理模式与相似设计模式的区别
很多新手会混淆代理模式、装饰器模式、适配器模式,我们做核心区分:
8.1 代理模式 vs 装饰器模式
| 维度 | 代理模式 | 装饰器模式 |
|---|---|---|
| 核心目的 | 控制访问,隐藏真实对象 | 增强功能,不隐藏对象 |
| 关系 | 代理与真实对象是一对一 | 装饰器可多层嵌套 |
| 客户端 | 不知道真实对象 | 知道真实对象,手动装饰 |
| 典型场景 | 权限、远程、事务 | IO 流、多层增强 |
8.2 代理模式 vs 适配器模式
| 维度 | 代理模式 | 适配器模式 |
|---|---|---|
| 核心目的 | 控制访问,接口不变 | 转换接口,解决不兼容 |
| 接口 | 代理与真实对象接口相同 | 适配器将不兼容接口转为兼容接口 |
| 典型场景 | 中介、AOP | 旧系统适配新接口 |
第九部分:代理模式进阶与最佳实践
9.1 动态代理性能优化
- 高并发场景优先使用CGLIB;
- 缓存代理对象,避免重复生成;
- Spring Boot 中开启 CGLIB 代理:
spring.aop.proxy-target-class=true。
9.2 使用禁忌
- 不要用代理增强核心业务逻辑(核心逻辑应写在真实类中);
- final 类 / 方法无法被 CGLIB 代理,避免滥用 final;
- 不要过度使用代理,增加系统复杂度。
9.3 企业最佳实践
- 统一使用Spring AOP实现日志、事务、监控;
- 底层框架设计优先使用动态代理;
- 代理逻辑保持精简,不写复杂业务代码。
第十部分:总结
代理模式是 Java 架构师必须掌握的核心设计模式,本文从生活案例→静态代理→动态代理→设计原则→框架源码→实战场景全方位拆解,核心知识点总结:
- 本质:通过代理对象间接访问真实对象,实现控制与增强;
- 实现:静态代理(入门)、JDK 动态代理(接口)、CGLIB 动态代理(无接口);
- 核心价值:无侵入增强、解耦、访问控制;
- 设计原则:完美契合七大设计原则,是架构设计的典范;
- 企业应用:Spring AOP、Mybatis、Dubbo 的底层核心。
掌握代理模式,不仅能让你写出更优雅、更易维护的代码,更能让你读懂主流框架的底层原理,完成从初级程序员到 Java 架构师的蜕变。
浙公网安备 33010602011771号