代理模式
来源:互联网 发布:淘宝账户限制登录 编辑:程序博客网 时间:2024/05/21 15:40
代理模式
《Head First Design Patterns》对其定义:代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。
这个定义很容易引导我们思考,为什么要为一对象提供“替身”,或者去控制对对象的访问呢?直接访问对象不也是可以吗?
现实是有些时候没有那么简单,例如如果客户端需要访问的对象在服务端,客户端直接访问需要处理网络等一系列复杂的问题,如果使用代理模式那么客户端只需要和代理打交道,客户端不需要知道代理怎么和服务端交互。当然这只是一个例子,在生活中也有例如火车票飞机票代售点这类代理思想的例子。我们就从最简单的代理模式说起吧!
静态代理
顾名思义,代理类是在编译时就生成的,也就是说代理类在编译完成后已经存在.class文件。一般来说它的UML(直接使用Idea生成的,不是很美观)是这样的:
RealSubject:原对象也可以称之为委托对象的类。
SubjectProxy:代理对象类
Subject:委托和代理对象类共同实现的接口,request()是其拥有的方法。
一般来说,代理模式都会包括上述的三个部分。只不过,会出现各种变体罢了,所以从最简单的入手,在慢慢深入是一个很好的方式。
上述UML对应的Java代码如下:
- 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
- 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
麻雀虽小,五脏俱全,通过这个简单的例子,可以快速的掌握代理模式中最核心的委托对象,代理对象和代表其行为的接口。有了这些基础在去看其他的变种就容易的多。
* 动态代理*
有静态代理,当然也有动态代理。所谓动态代理就是在运行时才会生成代理类,和静态代理不同在编译阶段不会生成实在.class文件,而是在运行时动态生成类字节码。然后再通过ClassLoader加载到JVM中,简而言之,代理类是在运行时根据需要代理的接口等一系列参数动态生成的。其UML图如下:
这个UML还不是完整的,只包含了核心的部分,但是没有包括运行时生成的代理类。这个稍后再分析。我们上图代表的是我们编码时涉及到的各部分。
Subject,RealSubject上文中已经说过了,这里着重介绍下边两个类:
java.lang.reflect.Proxy:这个是生成代理类的核心类,位于java.lang.reflect包下,我们大致可以确定该类和反射有关。事实也确实如此,动态代理就是依靠反射生成代理类的,并且生成的代理类都是继承自Proxy,即$ProxyN extends Proxy
。关于生成的代理类所在包和命名也是有讲究的:
1.如果所代理的接口访问权限是public,那么代理类会被定义在root包路径下,如果是package(java中接口不能被定义为private和protect)那么代理类会被定义在接口所在的包。这样做可以有效的防止生成的代理类出现包访问权限的问题。
2.生成的代理类名称都是自动生成的类似”$ProxyN”格式的,N会随着代理类的数量增加,可以保证代理类的命名是唯一的。下边是我Demo中生成的代理类
3.生成的代理类都被final修饰符修饰,保证代理类不允许被继承。
上文说了代理类会继承自Proxy,同时还会实现其代理的接口,这样做的目的是可以将代理类安全的类型转换成所代理的接口类型。代理类完整的继承关系:
结合上文中的UML,构成了动态代理的基本结构。
java.lang.reflect.InvocationHandler接口:该接口有一个invoke()方法,我们在使用动态代理时一般都需要自定义一个类然后实现该接口,自定义的类就是我们的调用处理器,我们可以在这里实现我们需要的任务,当运行时代理类调用任何方法都会回调invoke()方法。
动态代理简单的实现:
- 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
- 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
一般来说,使用Java动态代理的步骤:
1.定义委托类和公共接口
2.自定义一个现实了InvocationHandler接口的调用处理类(代码中的ProxyHandler),然后在invoke方法中实现自己想要的任务,例如监控方法的调用。
3.通过Proxy.newProxyInstance方法生成代理类,代理对象。
第3步中的Proxy.newProxyInstance方法的实现比较复杂,打算单独一篇文章分析,这里简单介绍一下。主要是看一下其参数:
- 1
- 2
- 3
- 1
- 2
- 3
ClassLoader loaer:类加载器,主要是将生成的代理类的字节码装载到JVM,动态生成的代理类同样需要ClassLoader加载,并为其指明对象。所以每次在生成代理类和代理类对象时,都需要指定一个类加载对象。
Class
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
关于Retrofit的分析可以看看我之前的Retrofit2源码解析 。
远程代理
远程代理可以为一个位于不同的地址空间的对象(例如服务端server)提供一个本地代理对象(存根或者桩)。这个不同的地址空间在java中可以是不同的进程甚至是不同的机器。远程代理可以将网络或者跨进程通信的细节隐藏起来,使得客户端不必考虑网络或跨进程的存在。客户完全可以认为被代理的对象是本地的而不是远程的,而代理对象承担了大部份的通信工作。
上图只是概念图,实际要复杂的多,图中proxy即为server在客户端本地的代理对象。这样client可以直接和proxy交互,然后proxy处理和server的通信,他们之间的通信可以很复杂也可以简单。在java中可以有类似RMI一样的机制,在Android中也有其特殊的Binder机制。并且Android中大量使用其作为IPC通信方式。接下来我就结合AIDL来认识一下吧。
Binder作为Android系统IPC重要的通信方式,融合了代理模式和RMI机制的思想。在Android中的很多系统服务中如ActivityManagerService,PackageManagerService等都有很多应用,当然还有我们平时进行跨进程通信的AIDL也是以Binder为基础的。如果对AIDL还不熟悉可以先看一下从AIDL看Android跨进程通信 。AIDL在我们定义好接口后,会自动给我们生成一个对应的java文件,代码如下(直接用的从AIDL看Android跨进程通信 的例子。):
- 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
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 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
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
从代码中可以看出远程代理的影子,如果感觉代码结构不清晰,可以看下图(也是):
IMyService是共同的接口,Proxy是代理,Client通过其发起远程请求。Stub是Server的存根。其简化的框架可以是这样的:
这里不是讲Binder的知识,Binder机制很复杂,需要专门的学习,只是说明一下远程代理在Android中的应用,体会一下远程代理在现实中的应用。掌握了这些基本结构,在分析ActivityManagerService等一系列Android系统服务时对其结构会有很大的帮助。
总结:代理还有其他多种变种,例如虚拟代理,Copy-on-Write代理,保护代理,防火墙(Firewall)代理,智能引用(Smart Reference)代理等。这里不做详细介绍了,总之,代理模式不管是在java,Android甚至是现实生活中都普通存在的模式,有必要掌握其思想。
本文练习的项目地址https://github.com/hgchenkai/DesignPattern 有时间都亲手敲一下。
- 代理模式--动态代理
- 代理模式-静态代理
- 代理模式-静态代理
- 代理模式 & 动态代理
- 代理模式--静态代理
- 代理模式--动态代理
- 代理模式(动态代理)
- 代理模式-动态代理
- 代理模式-动态代理
- 代理模式动态代理
- 代理模式-静态代理
- 代理模式-动态代理
- 代理模式 -动态代理
- 代理模式---动态代理
- 代理模式-动态代理
- 代理模式--静态代理
- 代理模式!
- 代理模式
- HDU-1261 字串数
- Intellij IDEA 中无法编译lombok的解决方法
- winscp安装
- Codeforces 675C Money Transfers【贪心】【新年快乐】T T窝要去打联盟咯!
- learn python the hard way---ex48
- 代理模式
- JDBC与JNDI这两种连接方式的区别
- 斐波那契堆(Fibonacci heaps)
- 小猴子下落
- volley
- 观察者模式
- 洛谷 p2320 鬼子的钱袋
- 序言-- UI基础
- React组件开发流程——利用React构建简单的可检索产品数据表