今天想閱讀Retrofit的源碼绑警,發(fā)現(xiàn)一開始就是一個(gè)代理。對代理的模式又不是很了解芝加。學(xué)習(xí)了一波硅卢。
什么是代理
我們買衣服有代理商,存在于我們和廠家之間藏杖。廠家是賣衣服的将塑,代理商也是賣衣服的,我們?nèi)ベI衣服蝌麸,一般不
能直接找廠家点寥,所以我們找代理商買衣服,代理商找廠家買衣服来吩,代理商進(jìn)行提價(jià)敢辩,就是我們需要
讓代理,進(jìn)行的操作弟疆。
代理的理解
代理模式需要3個(gè)角色戚长。
- 抽象角色 聲明真實(shí)對象和代理對象共有的操作
- 代理對象 代理對象持有真實(shí)對象的引用,從而可以對真實(shí)對象進(jìn)行操作怠苔,同時(shí)代理對象和真實(shí)對象實(shí)現(xiàn)相同的接口同廉,可以在任何時(shí)刻替換掉真實(shí)對象。
代理對象調(diào)用真實(shí)對象的操作是柑司,可以加上自己的操作迫肖,相當(dāng)于對真實(shí)對象進(jìn)行封裝。 - 真實(shí)對象 代理對象代表的對象攒驰,是我們最終引用的對象蟆湖。
代理模式對外部提供統(tǒng)一的接口方法,玻粪,而代理類在接口中實(shí)現(xiàn)對真實(shí)類的附加操作行為隅津,從而可以在不影響外部調(diào)用情況下,進(jìn)行系統(tǒng)擴(kuò)展奶段。也就是說饥瓷,我要修改真實(shí)角色的操作的時(shí)候,盡量不要修改他痹籍,而是在外部在“包”一層進(jìn)行附加行為呢铆,即代理類。
從而達(dá)到對修改關(guān)閉蹲缠,對擴(kuò)展開放
,保證系統(tǒng)的穩(wěn)定性棺克。
與真實(shí)對象不存在聯(lián)系悠垛,降低耦合
靜態(tài)代理
在編碼過程中對代理進(jìn)行指定。和生活中的代理商一樣娜谊。
抽象對象 賣衣服
public interface ClothingSell {
void sellClothes();
}
代理對象 代理商
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è)買衣服的代理商,先漲價(jià)3倍");
clothingFactory.sellClothes();
System.out.println("衣服賣好了,再見");
}
}
真實(shí)對象
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("我要買衣服");
clothingFactor.sellClothes();
System.out.println("衣服買好了");
}
}
結(jié)果
我要買衣服
我是一個(gè)買衣服的代理商,先漲價(jià)3倍
我是一個(gè)衣服廠家
衣服賣好了,再見
衣服買好了
動(dòng)態(tài)代理
在程序運(yùn)行過程中确买,才生出代理類,并執(zhí)行.由于是在執(zhí)行過程中纱皆,才會身材代理類湾趾。如果大規(guī)模采用靜態(tài)代理,產(chǎn)生大量的類同時(shí)代理角色與真實(shí)角色功能重復(fù)派草,是系統(tǒng)臃腫復(fù)制搀缠。
java編譯器編譯完成以后,生成class文件近迁,虛擬機(jī)讀取字節(jié)碼文件艺普,加載內(nèi)存,解析鉴竭,生成Class對象歧譬。而動(dòng)態(tài)代理就是在程序運(yùn)行過程中直接生成新的class字節(jié)碼二進(jìn)制數(shù)據(jù),由jvm加載執(zhí)行搏存,從而完成動(dòng)態(tài)代理的操作瑰步。
但是在動(dòng)態(tài)生成代碼的過程,如果完成一行一行的實(shí)現(xiàn)一個(gè)新的類祭埂,是十分繁瑣而且容易出錯(cuò)面氓,我們使用代理模式是系統(tǒng)在不影響系統(tǒng)穩(wěn)定是的前提下兵钮,對系統(tǒng)進(jìn)行擴(kuò)展蛆橡。
代理模式中,Proxy角色掘譬,在執(zhí)行代理操作時(shí)泰演,也就是在執(zhí)行真實(shí)對象的操作之前和之后執(zhí)行了”新添加的邏輯“,所以我們完全可以這一部分抽象出來。這就有了
InvocationHandler
JDK動(dòng)態(tài)代理創(chuàng)建機(jī)制
代理類有java動(dòng)態(tài)生成葱轩,所以將會執(zhí)行以下操作
- 獲取真實(shí)對象的接口
- 生成代理名稱
- 動(dòng)態(tài)創(chuàng)建代理對象的字節(jié)碼
- 創(chuàng)建InvocationHanlder的實(shí)例Handler睦焕,出來代理對象的方法調(diào)用。
- 實(shí)例化代理對象靴拱。
場景
電動(dòng)汽車的特點(diǎn)垃喊,能開,能充電袜炕。
真實(shí)對象 電動(dòng)車
代理獨(dú)享 (動(dòng)態(tài)生成)
抽象對象 駕駛接口 充電接口
InVocationHandler handler
電動(dòng)車
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;
}
}
生成代理類本谜,并執(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)車實(shí)體類
ElectricCar car = new ElectricCar();
//獲取車的類加載器
ClassLoader classLoader = car.getClass().getClassLoader();
Class[] interfacess = car.getClass().getInterfaces();
InvocationHandler handler = new InvocationHandlerImpl(car);
//拿到代理類
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é)
程序只是對生活的抽象,又一次真實(shí)的感受到了偎窘,設(shè)計(jì)模式是對程序的抽象乌助,所以是所有的設(shè)計(jì)模式溜在,都是可以在生活中找到實(shí)例的。
1.遠(yuǎn)程代理 Remote Proxy
通過代理來表示另一個(gè)地址空間中的對象
2.虛代理 Virtual Proxy
按需創(chuàng)建開銷很大的對象. 常用于延遲加載.
3.保護(hù)代理 Protection Proxy
-控制對原始對象的訪問.
-在訪問對象時(shí)可以附加一些操作
4.智能指引 Smart Reference
-充當(dāng)智能指針: 能自動(dòng)釋放所引用的對象
-在訪問對象時(shí)可以附加一些操作
在面向?qū)ο笾蟹Q之為: 職責(zé)分明他托。
很不錯(cuò)的解釋
打個(gè)比喻掖肋,明星為什么需要經(jīng)紀(jì)人來代理他呢?因?yàn)槊餍堑膶B毷浅杌蜓輵蛏筒危绻殉艘酝獾钠渌虑楸热缪莩鲑M(fèi)用談判等等都攬?jiān)谏砩现玖麜鬯馈?
這就是體現(xiàn)一個(gè)思想:專業(yè)分工,用面向?qū)ο笮g(shù)語說:就是職責(zé)分明把篓。
所以籽腕,代理類一般是做些除原始類核心功能以外的其他功能,比如權(quán)限 事務(wù)等等都要專門的代理來實(shí)現(xiàn)纸俭。
當(dāng)我們的代碼每個(gè)類代表一個(gè)主要功能皇耗,而不是將所有功能混在一個(gè)類中,那么代碼無疑清晰有條理的揍很,易于維護(hù)郎楼,比如我要修改權(quán)限,就不必打開原始類代碼窒悔,直接修改權(quán)限代理類就可以了呜袁。就很少發(fā)生修改一個(gè)BUG,帶來一個(gè)新BUG的煩惱問題简珠。
No matter how slow you are writing clean code, you will always be slower if you make a mess 寫干凈的代碼無論有多慢阶界,總是快于把代碼寫得一團(tuán)糟。