代理模式(Proxy Pattern):控制对象访问的中间层
代理模式是结构型设计模式的一种,核心思想是为目标对象提供一个代理对象,通过代理对象控制对目标对象的访问。代理模式在不修改目标对象的前提下,可对其功能进行扩展(如增强、限制、监控等),广泛应用于远程调用、权限控制、日志记录等场景。
代理模式的核心结构

代理模式
代理模式包含三个核心角色,通过分工实现对目标对象的访问控制:
- 抽象角色(Subject):
声明目标对象和代理对象的共同接口(或抽象类),确保代理对象可替代目标对象。
- 真实角色(Real Subject):
被代理的目标对象,负责实现核心业务逻辑。
- 代理角色(Proxy):
实现抽象角色,内部持有真实角色的引用,负责控制对真实角色的访问,并可在调用前后添加额外操作(如日志、权限校验)。
静态代理(Static Proxy)
静态代理是最简单的代理模式实现,代理类在编译期就已确定,与目标类实现相同的接口。
实现方式
- 定义抽象接口(抽象角色)。
- 真实角色实现接口,提供核心业务逻辑。
- 代理角色实现同一接口,内部持有真实角色的引用,在调用真实角色方法前后添加扩展逻辑。
代码示例
以 “服装工厂代理销售” 为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| interface ClothFactory { void produceCloth(); }
class NikeFactory implements ClothFactory { @Override public void produceCloth() { System.out.println("Nike工厂生产运动鞋和T恤"); } }
class ProxyFactory implements ClothFactory { private ClothFactory target;
public ProxyFactory(ClothFactory target) { this.target = target; }
@Override public void produceCloth() { System.out.println("代理商:检查工厂资质,准备生产"); target.produceCloth(); System.out.println("代理商:验收产品,安排销售"); } }
public class StaticProxyDemo { public static void main(String[] args) { ClothFactory nike = new NikeFactory(); ClothFactory proxy = new ProxyFactory(nike); proxy.produceCloth(); } }
|
输出结果
1 2 3
| 代理商:检查工厂资质,准备生产 Nike工厂生产运动鞋和T恤 代理商:验收产品,安排销售
|
优缺点
优点
- 简单直观,代理逻辑清晰,易于理解。
- 不修改目标对象,通过代理扩展功能,符合开闭原则。
缺点
- 代码冗余:代理类与目标类必须实现同一接口,若接口方法增多,两者需同步修改。
- 维护成本高:每增加一个目标类,需对应创建一个代理类,类数量膨胀。
动态代理(Dynamic Proxy)
动态代理解决了静态代理的缺陷,代理类在运行时动态生成,无需手动编写代理类代码。常见实现方式有两种:JDK 动态代理和 CGLIB 动态代理。
1. JDK 动态代理(基于接口)
JDK 动态代理是 Java 原生支持的代理方式,要求目标类必须实现接口,底层通过反射机制生成代理类。
核心组件
java.lang.reflect.Proxy:生成代理对象的工具类,核心方法newProxyInstance()用于动态创建代理实例。
java.lang.reflect.InvocationHandler:接口,定义代理逻辑(如增强操作),代理对象的所有方法调用都会转发到其invoke()方法。
实现步骤
- 定义抽象接口(同静态代理)。
- 真实角色实现接口。
- 实现
InvocationHandler接口,在invoke()方法中定义代理逻辑(调用目标方法前后的增强操作)。
- 通过
Proxy.newProxyInstance()生成代理对象,关联目标对象和InvocationHandler。
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| interface Subject { void doAction(); }
class RealSubject implements Subject { @Override public void doAction() { System.out.println("真实对象:执行核心业务逻辑"); } }
class MyInvocationHandler implements InvocationHandler { private Object target;
public Object bind(Object target) { this.target = target; return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this ); }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("JDK代理:调用前增强(如日志记录)"); Object result = method.invoke(target, args); System.out.println("JDK代理:调用后增强(如结果缓存)"); return result; } }
public class JdkDynamicProxyDemo { public static void main(String[] args) { Subject realSubject = new RealSubject(); MyInvocationHandler handler = new MyInvocationHandler(); Subject proxy = (Subject) handler.bind(realSubject); proxy.doAction(); } }
|
输出结果
1 2 3
| JDK代理:调用前增强(如日志记录) 真实对象:执行核心业务逻辑 JDK代理:调用后增强(如结果缓存)
|
2. CGLIB 动态代理(基于继承)
CGLIB(Code Generation Library)是第三方库,无需目标类实现接口,通过生成目标类的子类作为代理类实现代理功能,底层依赖字节码处理框架 ASM 修改字节码。
适用场景
- 目标类未实现接口(弥补 JDK 动态代理的局限)。
- 需对类的非接口方法进行代理。
实现步骤
- 引入 CGLIB 依赖。
- 定义目标类(无需实现接口)。
- 实现
MethodInterceptor接口,在intercept()方法中定义代理逻辑。
- 通过
Enhancer生成代理子类实例。
代码示例
1 2 3 4 5 6
| <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| class TargetClass { public void doSomething() { System.out.println("目标类:执行具体操作"); } }
class MyMethodInterceptor 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 Object getProxy(Class<?> targetClass) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(targetClass); enhancer.setCallback(this); return enhancer.create(); } }
public class CglibProxyDemo { public static void main(String[] args) { MyMethodInterceptor interceptor = new MyMethodInterceptor(); TargetClass proxy = (TargetClass) interceptor.getProxy(TargetClass.class); proxy.doSomething(); } }
|
输出结果
1 2 3
| CGLIB代理:调用前增强(如权限校验) 目标类:执行具体操作 CGLIB代理:调用后增强(如资源释放)
|
3. JDK 动态代理与 CGLIB 的对比
| 维度 |
JDK 动态代理 |
CGLIB 动态代理 |
| 底层原理 |
基于反射,生成实现接口的代理类 |
基于 ASM,生成目标类的子类 |
| 目标类要求 |
必须实现接口 |
可无接口(但不能是 final 类 / 方法) |
| 性能 |
调用效率较高(反射优化) |
生成代理类耗时,调用效率略低 |
| 灵活性 |
仅代理接口方法 |
可代理所有非 final 方法 |
代理模式的应用场景
- 远程代理
为远程对象(如分布式系统中的服务)提供本地代理,屏蔽网络通信细节(如 RPC 框架中的服务代理)。
- 安全代理
在调用目标对象前进行权限校验,限制未授权访问(如权限管理系统中的资源访问控制)。
- 日志代理
记录方法调用的参数、返回值和耗时,用于调试或审计(如 AOP 中的日志切面)。
- 延迟加载
延迟初始化重量级对象(如大型文件、数据库连接),仅在首次使用时创建,节省资源。
- 缓存代理
对方法调用结果进行缓存,重复调用时直接返回缓存值(如 Redis 缓存的本地代理)。
代理模式的优缺点
优点
- 解耦:代理类与目标类分离,扩展功能无需修改目标类,符合开闭原则。
- 增强灵活性:可在不改变目标逻辑的前提下,动态添加日志、权限、缓存等功能。
- 保护目标对象:通过代理控制访问,隐藏目标对象的实现细节。
缺点
- 性能损耗:代理层的存在会增加方法调用的间接性,可能降低执行效率(尤其是动态代理的反射或字节码操作)。
- 复杂度提升:动态代理的实现(如 CGLIB)涉及字节码或反射,增加了代码理解难度。
总结
代理模式通过引入中间层(代理对象)实现对目标对象的访问控制,分为静态代理和动态代理:
- 静态代理适用于简单场景,实现简单但维护成本高;
- 动态代理(JDK/CGLIB)适用于复杂场景,可动态生成代理类,灵活性更高。
代理模式的核心价值在于 “控制与增强”,在框架开发(如 Spring AOP)、分布式系统、安全控制等领域应用广泛
v1.3.10