今天想閱讀Retrofit的源碼,發(fā)現(xiàn)一開(kāi)始就是一個(gè)代理。對(duì)代理的模式又不是很了解。學(xué)習(xí)了一波。
什么是代理
我們買(mǎi)衣服有代理商,存在于我們和廠家之間。廠家是賣(mài)衣服的,代理商也是賣(mài)衣服的,我們?nèi)ベI(mǎi)衣服,一般不
能直接找廠家,所以我們找代理商買(mǎi)衣服,代理商找廠家買(mǎi)衣服,代理商進(jìn)行提價(jià),就是我們需要
讓代理,進(jìn)行的操作。
代理的理解
代理模式需要3個(gè)角色。
- 抽象角色 聲明真實(shí)對(duì)象和代理對(duì)象共有的操作
- 代理對(duì)象 代理對(duì)象持有真實(shí)對(duì)象的引用,從而可以對(duì)真實(shí)對(duì)象進(jìn)行操作,同時(shí)代理對(duì)象和真實(shí)對(duì)象實(shí)現(xiàn)相同的接口,可以在任何時(shí)刻替換掉真實(shí)對(duì)象。
代理對(duì)象調(diào)用真實(shí)對(duì)象的操作是,可以加上自己的操作,相當(dāng)于對(duì)真實(shí)對(duì)象進(jìn)行封裝。 - 真實(shí)對(duì)象 代理對(duì)象代表的對(duì)象,是我們最終引用的對(duì)象。
代理模式對(duì)外部提供統(tǒng)一的接口方法,,而代理類(lèi)在接口中實(shí)現(xiàn)對(duì)真實(shí)類(lèi)的附加操作行為,從而可以在不影響外部調(diào)用情況下,進(jìn)行系統(tǒng)擴(kuò)展。也就是說(shuō),我要修改真實(shí)角色的操作的時(shí)候,盡量不要修改他,而是在外部在“包”一層進(jìn)行附加行為,即代理類(lèi)。
從而達(dá)到對(duì)修改關(guān)閉,對(duì)擴(kuò)展開(kāi)放,保證系統(tǒng)的穩(wěn)定性。
與真實(shí)對(duì)象不存在聯(lián)系,降低耦合
靜態(tài)代理
在編碼過(guò)程中對(duì)代理進(jìn)行指定。和生活中的代理商一樣。
抽象對(duì)象 賣(mài)衣服
public interface ClothingSell {
void sellClothes();
}
代理對(duì)象 代理商
package staticProxy;
/**
* Created by azezer0 on 16/10/21.
*/
public class ClothingFactor implements ClothingSell {
private ClothingFactory clothingFactory;
public ClothingFactor(ClothingFactory clothingFactory) {
this.clothingFactory = clothingFactory;
}
public ClothingFactor() {
this.clothingFactory = new ClothingFactory();
}
@Override
public void sellClothes() {
System.out.println("我是一個(gè)買(mǎi)衣服的代理商,先漲價(jià)3倍");
clothingFactory.sellClothes();
System.out.println("衣服賣(mài)好了,再見(jiàn)");
}
}
真實(shí)對(duì)象
package staticProxy;
/**
* Created by azezer0 on 16/10/21.
*/
public class ClothingFactory implements ClothingSell {
@Override
public void sellClothes() {
System.out.println("我是一個(gè)衣服廠家");
}
}
執(zhí)行
package staticProxy;
/**
* Created by azezer0 on 16/10/21.
*/
public class Main {
public static void main(String str[])
{
ClothingFactor clothingFactor = new ClothingFactor();
System.out.println("我要買(mǎi)衣服");
clothingFactor.sellClothes();
System.out.println("衣服買(mǎi)好了");
}
}
結(jié)果
我要買(mǎi)衣服
我是一個(gè)買(mǎi)衣服的代理商,先漲價(jià)3倍
我是一個(gè)衣服廠家
衣服賣(mài)好了,再見(jiàn)
衣服買(mǎi)好了
動(dòng)態(tài)代理
在程序運(yùn)行過(guò)程中,才生出代理類(lèi),并執(zhí)行.由于是在執(zhí)行過(guò)程中,才會(huì)身材代理類(lèi)。如果大規(guī)模采用靜態(tài)代理,產(chǎn)生大量的類(lèi)同時(shí)代理角色與真實(shí)角色功能重復(fù),是系統(tǒng)臃腫復(fù)制。

java編譯器編譯完成以后,生成class文件,虛擬機(jī)讀取字節(jié)碼文件,加載內(nèi)存,解析,生成Class對(duì)象。而動(dòng)態(tài)代理就是在程序運(yùn)行過(guò)程中直接生成新的class字節(jié)碼二進(jìn)制數(shù)據(jù),由jvm加載執(zhí)行,從而完成動(dòng)態(tài)代理的操作。
但是在動(dòng)態(tài)生成代碼的過(guò)程,如果完成一行一行的實(shí)現(xiàn)一個(gè)新的類(lèi),是十分繁瑣而且容易出錯(cuò),我們使用代理模式是系統(tǒng)在不影響系統(tǒng)穩(wěn)定是的前提下,對(duì)系統(tǒng)進(jìn)行擴(kuò)展。
代理模式中,Proxy角色,在執(zhí)行代理操作時(shí),也就是在執(zhí)行真實(shí)對(duì)象的操作之前和之后執(zhí)行了”新添加的邏輯“,所以我們完全可以這一部分抽象出來(lái)。這就有了
InvocationHandler
JDK動(dòng)態(tài)代理創(chuàng)建機(jī)制
代理類(lèi)有java動(dòng)態(tài)生成,所以將會(huì)執(zhí)行以下操作
- 獲取真實(shí)對(duì)象的接口
- 生成代理名稱(chēng)
- 動(dòng)態(tài)創(chuàng)建代理對(duì)象的字節(jié)碼
- 創(chuàng)建InvocationHanlder的實(shí)例Handler,出來(lái)代理對(duì)象的方法調(diào)用。
- 實(shí)例化代理對(duì)象。
場(chǎng)景
電動(dòng)汽車(chē)的特點(diǎn),能開(kāi),能充電。
真實(shí)對(duì)象 電動(dòng)車(chē)
代理獨(dú)享 (動(dòng)態(tài)生成)
抽象對(duì)象 駕駛接口 充電接口
InVocationHandler handler
電動(dòng)車(chē)
package proxyTest;
/**
* Created by azezer0 on 16/10/21.
*/
public class ElectricCar implements Rechargable, Vehicle {
public static final String Tag = ElectricCar.class.getSimpleName();
@Override
public void recharge() {
System.out.println(Tag+"__"+"recharge");
}
@Override
public void driver() {
System.out.println(Tag + "__" + "driver");
}
}
駕駛接口
package proxyTest;
/**
* Created by azezer0 on 16/10/21.
*/
public interface Vehicle {
void driver();
}
充電接口
package proxyTest;
/**
* Created by azezer0 on 16/10/21.
*/
public interface Rechargable {
void recharge();
}
handler 實(shí)現(xiàn) InvocationHandler接口
package proxyTest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* Created by azezer0 on 16/10/21.
*/
public class InvocationHandlerImpl implements InvocationHandler {
private ElectricCar car;
public InvocationHandlerImpl(ElectricCar car) {
this.car = car;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Handler" + "___" + method.getName() + "___Start");
method.invoke(car, null);
System.out.println("Handler" + "___" + method.getName() + "___Stop");
return null;
}
}
生成代理類(lèi),并執(zhí)行
package proxyTest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* Created by azezer0 on 16/10/21.
*/
public class TestMain {
public static void main(String args[]) {
//電動(dòng)車(chē)實(shí)體類(lèi)
ElectricCar car = new ElectricCar();
//獲取車(chē)的類(lèi)加載器
ClassLoader classLoader = car.getClass().getClassLoader();
Class[] interfacess = car.getClass().getInterfaces();
InvocationHandler handler = new InvocationHandlerImpl(car);
//拿到代理類(lèi)
Object o = Proxy.newProxyInstance(classLoader, interfacess, handler);
//執(zhí)行操作
Vehicle vehicle = (Vehicle) o;
vehicle.driver();
Rechargable rechargable = (Rechargable) o;
rechargable.recharge();
//保存到本地 class 文件
// ProxyUtils.generateClassFile(car.getClass(),"ElectirCarProxy");
}
}
執(zhí)行結(jié)果
Handler___driver___Start
ElectricCar__driver
Handler___driver___Stop
Handler___recharge___Start
ElectricCar__recharge
Handler___recharge___Stop
/Users/azezer0/x603/study/HTTPS/out/production/HTTPS/proxyTest/
保存生成的字節(jié)碼工具到本地
package proxyTest;
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Created by azezer0 on 16/10/21.
*/
public class ProxyUtils {
public static void generateClassFile(Class clazz, String proxyName) {
byte[] bytes = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
String paths = clazz.getResource(".").getPath();
System.out.println(paths);
FileOutputStream out = null;
try {
out = new FileOutputStream(paths + proxyName + ".class");
out.write(bytes);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
總結(jié)
程序只是對(duì)生活的抽象,又一次真實(shí)的感受到了,設(shè)計(jì)模式是對(duì)程序的抽象,所以是所有的設(shè)計(jì)模式,都是可以在生活中找到實(shí)例的。
1.遠(yuǎn)程代理 Remote Proxy
通過(guò)代理來(lái)表示另一個(gè)地址空間中的對(duì)象
2.虛代理 Virtual Proxy
按需創(chuàng)建開(kāi)銷(xiāo)很大的對(duì)象. 常用于延遲加載.
3.保護(hù)代理 Protection Proxy
-控制對(duì)原始對(duì)象的訪問(wèn).
-在訪問(wèn)對(duì)象時(shí)可以附加一些操作
4.智能指引 Smart Reference
-充當(dāng)智能指針: 能自動(dòng)釋放所引用的對(duì)象
-在訪問(wèn)對(duì)象時(shí)可以附加一些操作
在面向?qū)ο笾蟹Q(chēng)之為: 職責(zé)分明。
很不錯(cuò)的解釋
打個(gè)比喻,明星為什么需要經(jīng)紀(jì)人來(lái)代理他呢?因?yàn)槊餍堑膶?zhuān)職是唱歌或演戲,如果把除此以外的其他事情比如演出費(fèi)用談判等等都攬?jiān)谏砩?,他?huì)累死。
這就是體現(xiàn)一個(gè)思想:專(zhuān)業(yè)分工,用面向?qū)ο笮g(shù)語(yǔ)說(shuō):就是職責(zé)分明。
所以,代理類(lèi)一般是做些除原始類(lèi)核心功能以外的其他功能,比如權(quán)限 事務(wù)等等都要專(zhuān)門(mén)的代理來(lái)實(shí)現(xiàn)。
當(dāng)我們的代碼每個(gè)類(lèi)代表一個(gè)主要功能,而不是將所有功能混在一個(gè)類(lèi)中,那么代碼無(wú)疑清晰有條理的,易于維護(hù),比如我要修改權(quán)限,就不必打開(kāi)原始類(lèi)代碼,直接修改權(quán)限代理類(lèi)就可以了。就很少發(fā)生修改一個(gè)BUG,帶來(lái)一個(gè)新BUG的煩惱問(wèn)題。
No matter how slow you are writing clean code, you will always be slower if you make a mess 寫(xiě)干凈的代碼無(wú)論有多慢,總是快于把代碼寫(xiě)得一團(tuán)糟。