定义
为其他对象提供一种代理便以控制这个对象的访问。
介绍
代理模式(Proxy)属于结构型模式,也叫委托模式。
代理模式的特征是代理类与委托类实现同样的接口,代理类主要负责为委托类预处理消息,过滤消息,把消息转发给委托类,以及事后消息处理。
代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
UML

角色说明
- Subject (抽象主题类):接口或者抽象类,真实声明主题和代理的共同接口方法。
- RealSubject(真实主题类):被代理类或者委托类,定义代理所表示的真实对象,负责具体业务逻辑的执行,客户端可以通过代理类间接调用真实主题类的方法。
- Proxy(代理类):委托类,持有对真实主题类的引用,在其所实现接口的方法中调用真实主题类中相应的接口方法执行。
- Client(客户端类):使用代理模式的地方
模拟实现
Subject类定义RealSubject和Proxy的公用接口。1
2
3
4
5
6public interface Subject {
/**
* 请求接口
*/
void request();
}
RealSubject类定义Proxy所代表的真实请求1
2
3
4
5
6
7
8
9public class RealSubject implements Subject {
/**
* 请求接口
*/
public void request() {
System.out.println("我是真实的请求");
}
}
Proxy类:保存了一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这种代理就可以用来替代实体;1
2
3
4
5
6
7
8
9
10
11
12
13public class Proxy implements Subject {
// 需要注入bean
private RealSubject realSubject;
/**
* 请求接口
*/
public void request() {
// 调用真实的请求
realSubject.request();
}
}
客户端调用1
2
3
4
5
public void test1(){
Proxy proxy = new Proxy();
proxy.request();
}
案例实现
背景:天朝的码农小明需要一台全新的顶配Thinkpad T480笔记本
问题:国行价格太高,无法定制配置
解决:通过代购从米国直接代购空运回国
代购(代理对象)替小明(真实对象)去购买笔记本
创建抽象主题类 Trade
1 | public interface Trade { |
创建真实主题类
1 | public class XiaoMing implements Trade { |
创建代理类,即海外代购中介
1 | public class Proxy implements Trade { |
代购测试
1 | public class ProxyTest { |
输出结果1
2
3中介: 大家好,我是代购,很高兴为你服务。
我需要代购笔记本ThinkPad T480
对商品进行一次包装
通过上面简单的代码示例,我们可以轻松理解什么是代理模式,通过代理我们可以轻松从国外购买想要的商品。
在购买商品物流过程中,如果代购害怕商品损坏,会对原有的商品在进行一次深层次的包装,避免物流暴力运输损坏商品。这个其实就是代理模式中可以增强类原本的功能。
上面简单的实现属于静态代理模式,即在程序运行前,代理类的.class文件就已经存在了。
代理模式优缺点
优点
- 降低系统的耦合度
- 代理对象作为中介,起到保护目标对象的作用
缺点
- 客户端和目标端之间增加代理层,请求速度会变慢
- 静态代理模式增加系统复杂度
应用场景
| 应用场景 | 具体描述 | 目的 |
|---|---|---|
| 远程代理 | 为一个对象在不同地址空间提供局部代理 | - 隐藏一个对象存在于不同地址空间的事实 - 远程机器有更好的性能可以出来客户端的请求 |
| 虚拟代理 | 通过一个小的对象代理一个开销大的对象 | 减少系统开销 |
| 保护代理 | 控制目标对象访问,给不同用户提供不同的权限 | 控制对真实对象的访问权限 |
| 智能应用代理 | 在访问对象时附加额外操作 | 在不影响对象类的情况下,在访问对象时进行更多的操作 |
| 其他 | 防火墙代理,缓存代理 |
动态代理模式
实现原理
所谓动态代理,就是动态代理类的.class文件是在程序运行时候由JAVA反射机制动态生成,无需提前编译。
动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。
优点
只需要一个动态代理类就可以解决创建多个静态代理的问题,避免发重复、多余代码,增强灵活性。
设计动态代理类(DynamicProxy)时,不需要显式实现与目标对象类(RealSubject)相同的接口,而是将这种实现推迟到程序运行时由 JVM来实现
在使用时(调用目标对象方法时)才会动态创建动态代理类 & 实例,不需要事先实例化
缺点
效率低
相比静态代理中 直接调用目标对象方法,动态代理则需要先通过Java反射机制 从而 间接调用目标对象方法
应用场景局限
因为 Java 的单继承特性(每个代理类都继承了 Proxy 类),即只能针对接口 创建 代理类,不能针对类 创建代理类
即只能动态代理 实现了接口的类
应用场景
- 基于静态代理应用场景下,需要代理对象数量较多的情况下使用动态代理
- AOP 领域
- 定义:即 Aspect Oriented Programming = 面向切面编程,是OOP的延续、函数式编程的一种衍生范型
- 作用:通过预编译方式和运行期动态代理实现程序功能的统一维护。
- 优点:降低业务逻辑各部分之间的耦合度 、 提高程序的可重用性 & 提高了开发的效率
- 具体应用场景:日志记录、性能统计、安全控制、异常处理等
从JDK源码分析
Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类
Interface InvocationHandler
该接口仅定义1个方法
1 | public interface InvocationHandler { |
在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
Proxy 动态代理类
1 | protected Proxy(InvocationHandler h) { |
protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。
static Class getProxyClass (ClassLoaderloader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)
所谓DynamicProxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个DynamicProxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
在使用动态代理类时,我们必须实现InvocationHandler接口
通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系
动态代理步骤
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
4.通过代理调用方法
场景
背景:小明是JAVA服务端开发需要一台全新的顶配Thinkpad T480笔记本,小华是IOS开发者需要MAC PRO顶配
问题:国行价格太高
解决:通过代购从米国直接代购空运回国
即1个代购(动态代理对象)同时 代替 小成 & 小何(目标对象) 去买Mac(间接访问的操作)
该代购是代购任何商品 = 什么人有什么需求就会去代购任何东西(动态代理)
代码实现
声明调用处理类
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/**
* @Package: com.seed.proxy3
* @Description: 1. 生成 动态代理对象
* 2. 指定 代理对象运行目标对象方法时需要完成的 具体任务
* 注:需实现InvocationHandler接口 = 调用处理器 接口
* 所以称为 调用处理器类
* @Author: hanfeng
* @CreateDate: 2018-9-14 14:55
* @Version: 1.0.0
**/
public class DynamicProxy implements InvocationHandler {
// 声明代理对象
// 作用:绑定关系,即关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke()
private Object proxyObject;
public Object newProxyInstance(Object proxyObject) {
this.proxyObject = proxyObject;
return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(),
proxyObject.getClass().getInterfaces(), this);
// Proxy类 = 动态代理类的主类
// Proxy.newProxyInstance()作用:根据指定的类装载器、一组接口 & 调用处理器 生成动态代理类实例,并最终返回
// 参数说明:
// 参数1:指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
// 参数2:指定目标对象的实现接口
// 即要给目标对象提供一组什么接口。若提供了一组接口给它,那么该代理对象就默认实现了该接口,这样就能调用这组接口中的方法
// 参数3:指定InvocationHandler对象。即动态代理对象在调用方法时,会关联到哪个InvocationHandler对象
}
/**
* 复写InvocationHandler接口的invoke()
* 动态代理对象调用目标对象的任何方法前,都会调用调用处理器类的invoke()
*
* @param proxy 动态代理对象(即哪个动态代理对象调用了method()
* @param method 目标对象被调用的方法
* @param args 指定被调用方法的参数
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代购去商城挑选商品啦");
Object result = null;
// 通过Java反射机制调用目标对象方法
result = method.invoke(proxyObject, args);
return result;
}
}声明目标对象抽象接口类
1
2
3
4
5
6public interface Subject {
/**
* 购买商品
*/
void buy(String name);
}声明目标对象 xiaoming
1
2
3
4
5
6
7
8
9
10
11public class XiaoMing implements Subject {
/**
* 购买商品
*
* @param name
*/
public void buy(String name) {
System.out.println("我是小明,我需要购买" + name);
}
}声明目标对象 xiaohua
1
2
3
4
5
6
7
8
9
10
11public class XiaoHua implements Subject {
/**
* 购买商品
*
* @param name
*/
public void buy(String name) {
System.out.println("我是小华,我需要购买" + name);
}
}通过动态代理对象,调用目标对象的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class DynamicProxyTest {
public void test1() {
// 创建调用处理器类对象
DynamicProxy dynamicProxy = new DynamicProxy();
//创建目标对象 小明
XiaoMing xiaoMing = new XiaoMing();
// 创建动态代理类 & 对象:通过调用处理器类对象newProxyInstance()
// 实际上是调用了invoke(),再通过invoke()里的反射机制调用目标对象的方法
Subject xiaoming_subject = (Subject) dynamicProxy.newProxyInstance(xiaoMing);
//通过调用动态代理对象方法从而调用目标对象方法
xiaoming_subject.buy("Thinkpad T480");
XiaoHua xiaoHua = new XiaoHua();
// 创建动态代理类 & 对象:通过调用处理器类对象newProxyInstance()
Subject xiaohua_subject = (Subject) dynamicProxy.newProxyInstance(xiaoHua);
//通过调用动态代理对象方法从而调用目标对象方法
xiaohua_subject.buy("Mac Pro");
}
}测试结果
1
2
3
4代购去商城挑选商品啦
我是小明,我需要购买Thinkpad T480
代购去商城挑选商品啦
我是小华,我需要购买Mac Pro