意圖
代理模式主要是爲了控制對象的訪問,意圖只要有以下三種:
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)
}