意图
代理模式主要是为了控制对象的访问,意图只要有以下三种:
1、代理模式主要是为另一个对象提供代理,以控制对另一个对象的访问。
2、通过代理间接支持分布式、受控以及智能访问,
3、添加一个包装器或者委托以保护真正的组件不受过度复杂性的影响。
代理的种类
设计一个代理:当客户端第一次请求代理时,就实例化真实的对象,并将引发的请求转发给这个真实的对象。然后,所有之后的请求都直接转发到封装的真实对象。
主要有四种代理:
1、虚拟代理。当创建对象的代价非常高时,使用这种代理作为该对象的一个“替代品”。真实的对象只有在客户端第一次访问或者请求的时候创建(比如说图片加载)。
2、远程代理。此为远程对象的本地代表(在不同的地址空空间运行的远程对象)。本地代表是指可以由本地方法调用的对象,其行为会转发到远程对象中。比如说RPC中的stub,skeleton,就是提供了远程代理的功能。
3、保护代理。这种代理主要控制对敏感对象的访问。代理对象检查调用方是否具有各种所需的访问权限。
4、智能代理。主要用在访问对象时插入其他操作。
- 计算实际对象的引用次数,当没有其他对象引用该对象时,自动释放该对象(智能指针)
- 当第一次引用持久对象时,将其加载到内存中
- 在访问真实对象之前检查它是否被锁定,以确保没有其他对象可以更改它。
远程代理
能够调用本地对象,然后将每个请求转发到远程对象上。此时,需要一些辅助对象,来协助我们做这些事情。这些辅助对象使客户就像在调用本地对象的方法一样。
客户调用客户辅助对象上的方法,方法客户辅助对象就是真正的服务。客户辅助对象再负责为我们转发这些请求。
在服务端,服务辅助对象从客户辅助对象中接受请求(通过socket连接),将调用的信息解包,然后调用真正服务对象上的真正方法。
服务辅助对象可以从服务中得到返回值,将它打包,然后运回客户辅助对象,客户辅助对象对信息解包,最后将返回值交给客户对象,
java RMI
RMI提供了客户辅助对象和服务辅助对象,为客户辅助对象创建和服务对象相同的方法。RMI的好处在于你不必晴子写任何网络或I/O代码。客户程序调用远程方法就和在运行在客户自己的本地JVM上对对象进行正常方法调用一样。
RMI将客户辅助对象称为stub(桩),把服务辅助对象称为skeleton(骨架)。
类图
java动态代理
类图
我们使用java的动态代理来实现保护代理。因为java已经为你创建了proxy类,所以你需要有什么办法来告诉Proxy类你要做什么。我们不能像以前一样把代码放到proxy中,因为proxy不是我们直接实现的,我们把它放到InvocationHandle中,这个类的工作是相应代理的任何调用,你可以把InvocationHandler想成是代理收到方法调用后,请求做实际工作的对象。
我们为什么要使用动态代理呢?比如说我们实现一个打分系统,该系统有个人年龄性别等信息,也有别人给自己的评分信息。,我们自己不可以改变自己的HotOrNot评分,也不可以改变其他人的个人信息,因此,我们需要创建两个代理,一个访问你自己的PersonBean对象,另一个访问另一人的PersonBean对象。这样代理就可以控制在每一种情况下允许哪一种请求。
创建这种代理,必须使用java中的动态代理。
代码
个人信息接口类
interface PersonBean{
fun getName() : String?
fun getGender() : String?
fun getInterests() : String?
fun getHotOrNotRating() : Int?
fun setName(name: String)
fun setGender(gender : String)
fun setInterests(interest : String)
fun setHotOrNotRating(hotRating : Int)
}
个人信息实现类
class PersonBeanImpl : PersonBean{
var nameStr : String? = null
var genderStr : String? = null
var interestStr : String? = null
var hotRatingInt : Int? = 0
var ratingCount = 0
override fun getName(): String? {
return nameStr
}
override fun getGender(): String? {
return genderStr
}
override fun getInterests(): String? {
return interestStr
}
override fun getHotOrNotRating(): Int? {
if (ratingCount == 0){
return 0
}
return hotRatingInt?.div(ratingCount)
}
override fun setName(name: String) {
nameStr = name
}
override fun setGender(gender: String) {
genderStr = gender
}
override fun setInterests(interest: String) {
interestStr = interest
}
override fun setHotOrNotRating(hotRating: Int) {
hotRatingInt?.plus(hotRating)
ratingCount++
}
}
真正实现控制访问的handler
自己的信息
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
/**
* 自己只能修改年龄性别等信息,而不能修改自己的评分等信息。
*/
class OwnerInvocationHandler(val personBean: PersonBeanImpl) : InvocationHandler{
override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any {
if (method!!.name.startsWith("get")){
return method.invoke(personBean, args!![0])
}else if (method.name == "setHotOrNotRating"){
throw IllegalAccessException()
}else if (method.name.startsWith("set")){
method.invoke(personBean, args!![0])
}
return "null"
}
}
别人的信息
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
/**
* 别人只能更改其他的评分,而不能修改别人的性别年龄等信息
*/
class NonOwnerInvocationHandler (val personBean: PersonBeanImpl): InvocationHandler{
override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any {
if (method!!.name.startsWith("get")){
throw IllegalAccessException()
}else if (method.name == "setHotOrNotRating"){
return method.invoke(personBean, args!![0])
}else if (method.name.startsWith("set")){
throw IllegalAccessException()
}
return "null"
}
}
测试类
import java.lang.reflect.Proxy
fun main(args : Array<String>){
val person = PersonBeanImpl()
val personProxy = Proxy.newProxyInstance(person.javaClass.classLoader,person.javaClass.interfaces, OwnerInvocationHandler(person)) as PersonBean
personProxy.setGender("3")
print(personProxy)
}