0%

代理模式

代理模式(Proxy Pattern):控制对象访问的中间层

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

代理模式的核心结构

代理模式包含三个核心角色,通过分工实现对目标对象的访问控制:

  • 抽象角色(Subject)
    声明目标对象和代理对象的共同接口(或抽象类),确保代理对象可替代目标对象。
  • 真实角色(Real Subject)
    被代理的目标对象,负责实现核心业务逻辑。
  • 代理角色(Proxy)
    实现抽象角色,内部持有真实角色的引用,负责控制对真实角色的访问,并可在调用前后添加额外操作(如日志、权限校验)。

静态代理(Static Proxy)

静态代理是最简单的代理模式实现,代理类在编译期就已确定,与目标类实现相同的接口。

实现方式

  1. 定义抽象接口(抽象角色)。
  2. 真实角色实现接口,提供核心业务逻辑。
  3. 代理角色实现同一接口,内部持有真实角色的引用,在调用真实角色方法前后添加扩展逻辑。

代码示例

以 “服装工厂代理销售” 为例:

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
// 1. 抽象角色(接口)
interface ClothFactory {
void produceCloth(); // 生产服装
}

// 2. 真实角色(被代理的工厂)
class NikeFactory implements ClothFactory {
@Override
public void produceCloth() {
System.out.println("Nike工厂生产运动鞋和T恤");
}
}

// 3. 代理角色(代理商)
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()方法。
实现步骤
  1. 定义抽象接口(同静态代理)。
  2. 真实角色实现接口。
  3. 实现InvocationHandler接口,在invoke()方法中定义代理逻辑(调用目标方法前后的增强操作)。
  4. 通过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
// 1. 抽象角色(接口)
interface Subject {
void doAction();
}

// 2. 真实角色
class RealSubject implements Subject {
@Override
public void doAction() {
System.out.println("真实对象:执行核心业务逻辑");
}
}

// 3. 实现InvocationHandler,定义代理逻辑
class MyInvocationHandler implements InvocationHandler {
private Object target; // 目标对象(真实角色)

// 绑定目标对象,返回代理对象
public Object bind(Object target) {
this.target = target;
// 生成代理对象:参数为类加载器、目标类实现的接口、当前InvocationHandler
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}

/**
* 代理方法的核心逻辑
* @param proxy 生成的代理对象(一般不使用)
* @param method 被调用的目标方法
* @param args 方法参数
* @return 目标方法的返回值
*/
@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();
// 创建InvocationHandler并绑定真实对象
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 动态代理的局限)。
  • 需对类的非接口方法进行代理。
实现步骤
  1. 引入 CGLIB 依赖。
  2. 定义目标类(无需实现接口)。
  3. 实现MethodInterceptor接口,在intercept()方法中定义代理逻辑。
  4. 通过Enhancer生成代理子类实例。
代码示例
1
2
3
4
5
6
<!-- Maven依赖 -->
<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
// 1. 目标类(无需实现接口)
class TargetClass {
public void doSomething() {
System.out.println("目标类:执行具体操作");
}
}

// 2. 实现MethodInterceptor,定义代理逻辑
class MyMethodInterceptor implements MethodInterceptor {
/**
* 代理方法的核心逻辑
* @param obj 代理对象(目标类的子类实例)
* @param method 被调用的目标方法
* @param args 方法参数
* @param proxy 方法代理对象(用于调用目标类的原始方法)
* @return 目标方法的返回值
*/
@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 方法

代理模式的应用场景

  1. 远程代理
    为远程对象(如分布式系统中的服务)提供本地代理,屏蔽网络通信细节(如 RPC 框架中的服务代理)。
  2. 安全代理
    在调用目标对象前进行权限校验,限制未授权访问(如权限管理系统中的资源访问控制)。
  3. 日志代理
    记录方法调用的参数、返回值和耗时,用于调试或审计(如 AOP 中的日志切面)。
  4. 延迟加载
    延迟初始化重量级对象(如大型文件、数据库连接),仅在首次使用时创建,节省资源。
  5. 缓存代理
    对方法调用结果进行缓存,重复调用时直接返回缓存值(如 Redis 缓存的本地代理)。

代理模式的优缺点

优点

  • 解耦:代理类与目标类分离,扩展功能无需修改目标类,符合开闭原则。
  • 增强灵活性:可在不改变目标逻辑的前提下,动态添加日志、权限、缓存等功能。
  • 保护目标对象:通过代理控制访问,隐藏目标对象的实现细节。

缺点

  • 性能损耗:代理层的存在会增加方法调用的间接性,可能降低执行效率(尤其是动态代理的反射或字节码操作)。
  • 复杂度提升:动态代理的实现(如 CGLIB)涉及字节码或反射,增加了代码理解难度。

总结

代理模式通过引入中间层(代理对象)实现对目标对象的访问控制,分为静态代理和动态代理:

  • 静态代理适用于简单场景,实现简单但维护成本高;
  • 动态代理(JDK/CGLIB)适用于复杂场景,可动态生成代理类,灵活性更高。

代理模式的核心价值在于 “控制与增强”,在框架开发(如 Spring AOP)、分布式系统、安全控制等领域应用广泛

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10