靜態(tài)代理與動態(tài)代理詳解

? ? ? ? 最近常常看到一些資料時,是不是會看到動態(tài)代理黎休,但是在項目中卻好像沒怎么使用過動態(tài)代理浓领,所以對動態(tài)代理的理解也大概只有一個概念,最近部門規(guī)定势腮,每兩周最好要有一次技術(shù)分享联贩,所以就借著這個機(jī)會,好好梳理一下動態(tài)代理到底是什么東西捎拯,下面是今天需要了解的相關(guān)知識點泪幌,讓我們由淺入深一步步的了解什么是代理模式吧。


知識點匯總:

一:什么是代理模式

二:代理模式的常見實現(xiàn)方式:基于接口的代理模式 和 基于類繼承的代理模式

三:靜態(tài)代理與動態(tài)代理的區(qū)別與實現(xiàn)

四:代理模式在Android中的使用場景

五:擴(kuò)展閱讀


一:什么是代理模式

簡介:在Java編程里就有一種設(shè)計模式署照,即代理模式祸泪,提供了一種對目標(biāo)對象的訪問方式,即通過代理對象訪問目標(biāo)對象建芙,代理對象是指具有與被代理對象相同的接口的類浴滴,客戶端必須通過代理對象與被代理的目標(biāo)類進(jìn)行交互。

? ? ? ?代理模式主要分為三個角色:客戶端岁钓,代理類升略,目標(biāo)類;而代理類需要與目標(biāo)類實現(xiàn)同一個接口屡限,并在內(nèi)部維護(hù)目標(biāo)類的引用品嚣,進(jìn)而執(zhí)行目標(biāo)類的接口方法,并實現(xiàn)在不改變目標(biāo)類的情況下前攔截钧大,后攔截等所需的業(yè)務(wù)功能翰撑。


代理模式的優(yōu)點:

中間隔離:某些情況下,客戶端不想或者不能直接引用一個目標(biāo)對象啊央,而代理類可以在客戶端和目標(biāo)類之前起到中介作用

開閉原則眶诈,擴(kuò)展功能:代理類除了是客戶類和目標(biāo)類的中介,還可以通過給代理類增加額外的功能來擴(kuò)展目標(biāo)類的功能瓜饥,這樣我們只需要修改代理類而不需要再修改目標(biāo)類逝撬,符合代碼設(shè)計的開閉原則(對擴(kuò)展開放,對修改關(guān)閉)乓土。代理類主要負(fù)責(zé)為目標(biāo)類預(yù)處理消息宪潮、過濾消息、把消息轉(zhuǎn)發(fā)給目標(biāo)類趣苏,以及事后對返回結(jié)果的處理等狡相。

? ? ? 代理類本身并不真正實現(xiàn)服務(wù),而是同過調(diào)用目標(biāo)類的相關(guān)方法食磕,來提供特定的服務(wù)尽棕。真正的業(yè)務(wù)功能還是由目標(biāo)類來實現(xiàn),但是可以在業(yè)務(wù)功能執(zhí)行的前后加入一些公共的服務(wù)彬伦。例如我們想給項目加入緩存滔悉、日志這些功能蟀悦,我們就可以使用代理類來完成,而沒必要打開已經(jīng)封裝好的目標(biāo)類氧敢。

代理模式的結(jié)構(gòu):

圖解:

代理模式的分類:(大概了解一下就好)

遠(yuǎn)程代理:為不同地理的對象提供局域網(wǎng)代表對象日戈。

虛擬代理:根據(jù)需要將資源消耗很大的對象進(jìn)行延遲,真正需要的時候再創(chuàng)建孙乖。

安全代理:控制用戶的訪問權(quán)限浙炼。

智能代理:提供對目標(biāo)對象額外的服務(wù)「使用最多的」。(靜態(tài)代理和動態(tài)代理)


二:代理模式的常見實現(xiàn)方式:基于接口的代理模式 和 基于類繼承的代理模式

基于接口的代理模式:

描述:一個比較直觀的方式唯袄,就是定義一個功能接口弯屈,然后讓Proxy和RealSubject來實現(xiàn)這個接口。

定義接口:

public interface ILogin {

voiduserLogin();

}

定義目標(biāo)類:(被代理類)

public class UserLogin implements ILogin {

@Override

public void userLogin() {

System.out.print("用戶登錄");

???}

}

定義代理類:

public classUserLoginProxyimplementsILogin{

privateUserLoginmLogin;

???publicUserLoginProxy() {

???????mLogin= newUserLogin();

???}

???@Override

???public voiduserLogin() {

???????System.out.print("登錄前恋拷。资厉。。");

???????mLogin.userLogin();

???????System.out.print("登錄后蔬顾。宴偿。。");

???}

}

客戶端:

public class Test {

???public static void main(String[]args){

???????ILoginloginProxy= newUserLoginProxy();

???????loginProxy.userLogin();

???}

}


基于類繼承的代理模式:

描述:還有比較隱晦的方式诀豁,就是通過繼承窄刘,因為如果Proxy繼承自RealSubject,這樣Proxy則擁有了RealSubject的功能舷胜,Proxy還可以通過重寫RealSubject中的方法娩践,來實現(xiàn)多態(tài)。

//定義目標(biāo)類:(被代理類)

???public classRealSubject{

???????public voiduserLogin() {

???????}

???}

???//定義代理類

???public class Proxy extendsRealSubject{

???????@Override

???????public voiduserLogin() {

???????????//todo:添加代理類代碼

???????????super.userLogin();

???????????//todo:添加代理類代碼

???????}

???}


三:靜態(tài)代理與動態(tài)代理的區(qū)別與實現(xiàn)

代理實現(xiàn)方式:如果按照代理創(chuàng)建的時期來進(jìn)行分類的話烹骨, 可以分為靜態(tài)代理翻伺、動態(tài)代理。

一:靜態(tài)代理是由程序員創(chuàng)建或特定工具自動生成代理類沮焕,再對其編譯吨岭,在程序運(yùn)行之前,代理類.class文件就已經(jīng)被創(chuàng)建了遇汞。

二:動態(tài)代理是在程序運(yùn)行時通過反射機(jī)制動態(tài)創(chuàng)建代理對象未妹。

圖解:

圖解二:

類加載詳細(xì)流程:

靜態(tài)代理總結(jié):

優(yōu)點:

1簿废、在符合開閉原則的情況下空入,對目標(biāo)對象功能進(jìn)行擴(kuò)展和攔截。

2族檬、在訪問無法訪問的資源歪赢,增強(qiáng)現(xiàn)有的接口業(yè)務(wù)功能方面有很大的優(yōu)點。

3单料、一個代理類只能代理一個真實的對象埋凯。

缺點:

1点楼、需要為每個目標(biāo)類創(chuàng)建代理類和接口,導(dǎo)致類的數(shù)量大大增加白对,工作量大掠廓,導(dǎo)致系統(tǒng)結(jié)構(gòu)比較臃腫和松散。

2甩恼、接口功能一旦修改蟀瞧,代理類和目標(biāo)類也得相應(yīng)修改,不易維護(hù)条摸。


動態(tài)代理實現(xiàn):

技術(shù)實現(xiàn):

簡述:動態(tài)代理是java設(shè)計模式中代理模式的另一種實現(xiàn)悦污,通過JVM在運(yùn)行期通過反射為委托的接口類動態(tài)的生成代理的一種技術(shù)。

目前動態(tài)代理技術(shù)主要分為:

1钉蒲、Java自己提供的JDK動態(tài)代理技術(shù)(只能實現(xiàn)基于接口的代理模式)

2切端、CGLIB技術(shù)(Android中不能使用,使用原理相似技術(shù):ASM和Javaasis)

一:基于接口的代理模式(動態(tài)實現(xiàn))

??在動態(tài)代理中顷啼,不需要我們再手動創(chuàng)建代理類踏枣,只需要編寫一個動態(tài)處理器及指定要代理的目標(biāo)對象實現(xiàn)的接口,真正的代理對象由JDK在運(yùn)行時為我們創(chuàng)建钙蒙;JDK提供了java.lang.reflect.InvocationHandler和java.lang.reflect.Proxy來實現(xiàn)動態(tài)代理椰于。

簡述:Proxy.getProxyClass(ClassLoader, interfaces)方法只需要接收一個類加載器和一組接口就可以返回一個代理Class對象,然后就可以通過反射創(chuàng)建代理實例仪搔,其原理就是從傳入的接口Class中瘾婿,拷貝類結(jié)構(gòu)信息到一個新的Class對象中,并繼承Proxy類烤咧,擁有構(gòu)造方法偏陪;站在我們的角度就是通過接口Class對象創(chuàng)建代理類Class對象。

代碼實現(xiàn):

方法一:代理類對象生成代碼

public static ObjectloadProxy(Object target) throws Exception {

???????//通過接口Class對象創(chuàng)建代理Class對象

???????Class?proxyClass=Proxy.getProxyClass(target.getClass().getClassLoader(),target.getClass().getInterfaces());

???????//拿到代理Class對象的有參構(gòu)造方法

???????Constructor<?> constructors =proxyClass.getConstructor(InvocationHandler.class);

???????//反射創(chuàng)建代理實例

???????Object proxy =constructors.newInstance(newInvocationHandler() {

???????????@Override

???????????public Object invoke(Object proxy, Methodmethod, Object[]args) throwsThrowable{

???????????????System.out.println("執(zhí)行前日志..."+"\n");

???????????????//執(zhí)行目標(biāo)類的方法

???????????????Object result =method.invoke(target,args);

???????????????System.out.println("執(zhí)行后日志..."+"\n");

???????????????return result;

???????????}

???????});

???????return proxy;

???}

客戶端調(diào)用:

public static void main(String[]args) throws Exception {

???????ILoginproxy = (ILogin)loadProxy(newUserLogin());

???????proxy.userLogin();

???}

方法二:代理類對象生成代碼(與retrofit類似)

Proxy類還有個更簡單的方法newProxyInstance煮嫌,直接返回代理對象笛谦,如下:

???public static ObjectloadProxy(Object object) {

???????returnProxy.newProxyInstance(

?????????????object.getClass().getClassLoader(), //和目標(biāo)對象的類加載器保持一致

?????????????object.getClass().getInterfaces(), //目標(biāo)對象實現(xiàn)的接口,因為需要根據(jù)接口動態(tài)生成代理對象

???????????????newInvocationHandler() { //事件處理器昌阿,即對目標(biāo)對象方法的執(zhí)行

???????????@Override

???????????publicObject invoke(Object proxy, Methodmethod, Object[]args) throwsThrowable{

???????????????????????System.out.println("執(zhí)行前日志...");

???????????????????????Object result =method.invoke(object,args);

???????????????????????System.out.println("執(zhí)行后日志...");

???????????????????????return result;

???????????????????}

???????????????});

???}

代理類:(動態(tài)生成基于接口的代理類代碼)

public final class $Proxy0 extends Proxy implementsILogin

{

???public $Proxy0(InvocationHandlerinvocationhandler){

???????super(invocationhandler);

???}

???public finalbooleanequals(Objectobj){

???????try{

???????????return ((Boolean)super.h.invoke(this, m1, new Object[] {

???????????????obj

???????????})).booleanValue();

???????}

???????catch(Error _ex) { }

???????catch(Throwablethrowable){

???????????throw newUndeclaredThrowableException(throwable);

???????}

???}???

?public final StringtoString(){

???????try{

???????????return (String)super.h.invoke(this, m2, null);

???????}

???????catch(Error _ex) { }

???????catch(Throwablethrowable){

???????????throw newUndeclaredThrowableException(throwable);

???????}

???}

???public final voiduserLogin(){

???????try{

???????????super.h.invoke(this, m3, null);

???????????return;

???????}

???????catch(Error _ex) { }

???????catch(Throwablethrowable){

???????????throw newUndeclaredThrowableException(throwable);}

???}

public finalinthashCode(){

???????try{

???????????return ((Integer)super.h.invoke(this, m0, null)).intValue();

???????}

???????catch(Error _ex) { }

???????catch(Throwablethrowable){

???????????throw newUndeclaredThrowableException(throwable);

???????}

???}

???private static Method m1;

???private static Method m2;

???private static Method m3;

???private static Method m0;

}

static{

???????try{

???????????m1 =Class.forName("java.lang.Object").getMethod("equals", new Class[] {

???????????????Class.forName("java.lang.Object")

???????????});

???????????m2 =Class.forName("java.lang.Object").getMethod("toString", new Class[0]);

???????????m3 =Class.forName("com.mango.demo.proxy.ILogin").getMethod("userLogin", new Class[0]);

???????????m0 =Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);

???????}

???????catch(NoSuchMethodExceptionnosuchmethodexception){

???????????throw newNoSuchMethodError(nosuchmethodexception.getMessage());

???????}

???????catch(ClassNotFoundExceptionclassnotfoundexception){

???????????throw newNoClassDefFoundError(classnotfoundexception.getMessage());

???????}

???}

生成代理類解析:

1饥脑、JDK為我們生成了一個$Proxy0(這個名字后面的0是編號,有多個代理類會一次遞增)的代理類懦冰。

2灶轰、代理類實現(xiàn)了ILogin接口,繼承了Proxy類刷钢,并有一個帶InvocationHandler參數(shù)的構(gòu)造方法笋颤,使用super調(diào)用Proxy類的構(gòu)造方法,證實了上面的分析内地。

3伴澄、實現(xiàn)了接口的userLogin方法赋除,并在其內(nèi)部調(diào)用InvocationHandler的invoke方法,其h正是Proxy類定義的成員變量非凌。

4举农、最下面是通過反射拿到類中的幾個方法,作為參數(shù)傳遞到InvocationHandler.invoke方法中敞嗡,即調(diào)用動態(tài)代理對象的任何方法并蝗,最終都是走到InvocationHandler.invoke方法中(所以在invoke方法中寫日志需要判斷下,是否是調(diào)用代理對象指定的方法走到這里)秸妥。


JDK主要會做以下工作:

1滚停、獲取 RealSubject上的所有接口列表。

2粥惧、確定要生成的代理類的類名键畴,默認(rèn)為:com.sun.proxy.$ProxyXXXX。

3突雪、根據(jù)需要實現(xiàn)的接口信息起惕,在代碼中動態(tài)創(chuàng)建該P(yáng)roxy類的字節(jié)碼。

4咏删、將對應(yīng)的字節(jié)碼轉(zhuǎn)換為對應(yīng)的class對象惹想。

5、創(chuàng)建InvocationHandler實例handler督函,用來處理Proxy所有方法調(diào)用嘀粱。

6、Proxy的class對象以創(chuàng)建的handler對象為參數(shù)辰狡,實例化一個proxy對象锋叨。

仔細(xì)觀察可以看出生成的動態(tài)代理類有以下特點:(并非上圖代碼demo)

1、繼承自java.lang.reflect.Proxy宛篇,實現(xiàn)了Rechargable,Vehicle這兩個ElectricCar實現(xiàn)的接口娃磺;

2、類中的所有方法都是final的叫倍;

3偷卧、所有的方法功能的實現(xiàn)都統(tǒng)一調(diào)用了InvocationHandler的invoke()方法。

JDK動態(tài)代理總結(jié):(基于接口的代理模式)

優(yōu)點:

1吆倦、相對于靜態(tài)代理听诸,極大的減少類的數(shù)量,降低工作量逼庞,減少對業(yè)務(wù)接口的依賴蛇更,降低耦合,便于后期維護(hù)赛糟;

2派任、同時在某些情況下是最大的優(yōu)勢,即可以統(tǒng)一修改代理類的方法邏輯璧南,而不需要像靜態(tài)代理需要修改每個代理類掌逛。

3、動態(tài)代理中所謂的“動態(tài)”司倚,是針對使用Java代碼實際編寫了代理類的“靜態(tài)”代理而言的豆混,它的主要優(yōu)勢不在于省去了編寫代理類那一點工作量,而是實現(xiàn)了可以在原始類和接口還未知的時候动知,就確定代理類的代理行為皿伺,當(dāng)代理類與原始類脫離直接聯(lián)系后,就可以很靈活地重用于不同的應(yīng)用場景之中盒粮。(深入理解Java虛擬機(jī))

缺點:

1鸵鸥、因為使用的是反射,所以在運(yùn)行時會消耗一定的性能丹皱;

2妒穴、同時JDK代理只支持interface的動態(tài)代理,如果你再繼續(xù)深究源碼摊崭,會發(fā)現(xiàn)讼油,所有動態(tài)生成的代理對象都有一個共同的父類,即都繼承于Proxy呢簸;

3矮台、Java的單繼承機(jī)制決定了無法支持class的動態(tài)代理,也就意味著你拿到動態(tài)生成的代理對象根时,只能調(diào)用其實現(xiàn)的接口里的方法嘿架,無法像靜態(tài)代理中的代理類可以在內(nèi)部擴(kuò)展更多的功能。


二:基于類的代理類實現(xiàn)(動態(tài)實現(xiàn))

簡述:JDK動態(tài)代理是實現(xiàn)AOP編程的一種途徑啸箫,可以在執(zhí)行指定方法前后貼入自己的邏輯耸彪,像Spring、Struts2就有用到該技術(shù)(攔截器設(shè)計就是基于AOP的思想)忘苛,只不過JDK動態(tài)代理只能實現(xiàn)基于接口的動態(tài)代理蝉娜,也算是一個遺憾,還有這種實現(xiàn)方式也不能避免類數(shù)量增加扎唾,因為你必須要為每個業(yè)務(wù)類編寫業(yè)務(wù)接口召川。

提問:那么有沒有不用寫代理類、也不用寫業(yè)務(wù)接口的代理方法呢胸遇?

解答:使用CGLib了荧呐,CGLIB(CodeGeneration Library)是一個強(qiáng)大的愁拭,高性能,高質(zhì)量的Code生成類庫梯皿,它可以在運(yùn)行期擴(kuò)展Java類與實現(xiàn)Java接口暑诸。

? ? ? CGLIB比JDK的代理更加強(qiáng)大,不只可以實現(xiàn)接口峰搪,還可以擴(kuò)展類岔冀,通過字節(jié)碼技術(shù)為一個類創(chuàng)建子類,并在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用概耻,順勢植入橫切邏輯使套。但因為采用的是繼承,所以不能對final修飾的類進(jìn)行代理鞠柄。

? ? ? ?CGLIB底層封裝了ASM侦高,通過對字節(jié)碼的操作來生成類,具有更高的性能厌杜,但是CGLIB創(chuàng)建代理對象時所花費的時間卻比JDK多奉呛;ASM是一套JAVA字節(jié)碼生成架構(gòu),能夠動態(tài)生成.class文件并在加載進(jìn)內(nèi)存之前進(jìn)行修改期奔。

? ? ?使用CGLIB需要引用jar包cglib-nodep-3.2.5.jar(如果引入cglib.jar侧馅,還需要引入asm的jar包)。

? ? ? 但是在Android中的字節(jié)碼生成技術(shù)呐萌,一般使用:ASM和Javassist馁痴。


Java字節(jié)碼生成開源框架介紹--ASM:

簡述:ASM 是一個 Java 字節(jié)碼操控框架。它能夠以二進(jìn)制形式修改已有類或者動態(tài)生成類肺孤。ASM 可以直接產(chǎn)生二進(jìn)制 class 文件罗晕,也可以在類被加載入 Java 虛擬機(jī)之前動態(tài)改變類行為。ASM 從類文件中讀入信息后赠堵,能夠改變類行為小渊,分析類信息,甚至能夠根據(jù)用戶要求生成新類茫叭。

? ? ? ?不過ASM在創(chuàng)建class字節(jié)碼的過程中酬屉,操縱的級別是底層JVM的匯編指令級別,這要求ASM使用者要對class組織結(jié)構(gòu)和JVM匯編指令有一定的了解揍愁。

? ? ? ?使用ASM框架提供了ClassWriter接口呐萨,通過訪問者模式進(jìn)行動態(tài)創(chuàng)建class字節(jié)碼,看下面的例子:

ASM動態(tài)生成代碼:

public static void main(String[]args) throwsIOException{

???????????System.out.println();

???????????ClassWriterclassWriter= newClassWriter(0);

???????????//通過visit方法確定類的頭部信息

???????????classWriter.visit(Opcodes.V1_7,// java版本

???????????????????Opcodes.ACC_PUBLIC,//類修飾符

???????????????????"Programmer", //類的全限定名

???????????????????null, "java/lang/Object", null);

???????????//創(chuàng)建構(gòu)造函數(shù)

???????????MethodVisitormv =classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);

???????????mv.visitCode();

???????????mv.visitVarInsn(Opcodes.ALOAD, 0);

???????????mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>","()V");

???????????mv.visitInsn(Opcodes.RETURN);

???????????mv.visitMaxs(1, 1);

???????????mv.visitEnd();

}

ASM動態(tài)生成代碼:(合并上面代碼)?????????

??????????//定義code方法

???????????MethodVisitormethodVisitor=classWriter.visitMethod(Opcodes.ACC_PUBLIC, "code", "()V",

???????????????????null, null);

???????????methodVisitor.visitCode();

???????????methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out",

???????????????????"Ljava/io/PrintStream;");

???????????methodVisitor.visitLdcInsn("I'm aProgrammer,JustCoding.....");

???????????methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",

???????????????????"(Ljava/lang/String;)V");

???????????methodVisitor.visitInsn(Opcodes.RETURN);

???????????methodVisitor.visitMaxs(2, 2);

???????????methodVisitor.visitEnd();

???????????classWriter.visitEnd();

???????????//使classWriter類已經(jīng)完成

???????????//將classWriter轉(zhuǎn)換成字節(jié)數(shù)組寫到文件里面去

???????????byte[] data =classWriter.toByteArray();

???????????Filefile= new File("D://Programmer.class");

???????????FileOutputStreamfout= newFileOutputStream(file);

???????????fout.write(data);

???????????fout.close();


Java字節(jié)碼生成開源框架介紹--Javassist:

簡述:Javassist是一個開源的分析莽囤、編輯和創(chuàng)建Java字節(jié)碼的類庫谬擦。是由東京工業(yè)大學(xué)的數(shù)學(xué)和計算機(jī)科學(xué)系的 Shigeru Chiba(千葉滋)所創(chuàng)建的。它已加入了開放源代碼JBoss應(yīng)用服務(wù)器項目,通過使用Javassist對字節(jié)碼操作為JBoss實現(xiàn)動態(tài)AOP框架朽缎。javassist是jboss的一個子項目惨远,其主要的優(yōu)點谜悟,在于簡單,而且快速北秽。直接使用java編碼的形式葡幸,而不需要了解虛擬機(jī)指令,就能動態(tài)改變類的結(jié)構(gòu)羡儿,或者動態(tài)生成類礼患。

Javassist動態(tài)生成代碼:

publicstatic void main(String[]args) throws Exception {

???????????ClassPoolpool =ClassPool.getDefault();

???????????//創(chuàng)建Programmer類?

???????????CtClasscc=pool.makeClass("com.samples.Programmer");

???????????//定義code方法

???????????CtMethodmethod =CtNewMethod.make("public void code(){}", cc);

???????????//插入方法代碼

???????????method.insertBefore("System.out.println(\"I'm aProgrammer,JustCoding.....\");");

???????????cc.addMethod(method);

???????????//保存生成的字節(jié)碼

???????????cc.writeFile("d://temp");

???????}


四:代理模式在Android中的使用場景

一:Retrofit中的動態(tài)代理

二:AIDL實現(xiàn)進(jìn)程之間的通信(底層使用Binder實現(xiàn))

三:Android的插件化實現(xiàn)原理之--hook機(jī)制


一:Retrofit中的動態(tài)代理

調(diào)用代碼:GitHubServiceservice =retrofit.create(GitHubService.class);

?????????????????????Call<List<Repo>> repos =service.listRepos("octocat");

Retrofit源碼實現(xiàn):

public<T> T create(final Class<T> service) {

???????Utils.validateServiceInterface(service);

???????if (validateEagerly) {

???????????eagerlyValidateMethods(service);

???????}

???????return (T)Proxy.newProxyInstance(service.getClassLoader(), new Class[] { service },

???????????????newInvocationHandler() {

???????????????????private final Platformplatform=Platform.get();

???????????????????@Override public Object invoke(Object proxy, Methodmethod, @NullableObject[]args)

???????????????????????????throwsThrowable{

???????????????????????// If the method is a method from Object then defer to normal invocation.

???????????????????????if (method.getDeclaringClass() ==Object.class) {

???????????????????????????returnmethod.invoke(this,args);

???????????????????????}

???????????????????????if (platform.isDefaultMethod(method)) {

???????????????????????????returnplatform.invokeDefaultMethod(method, service, proxy,args);

???????????????????????}

???????????????????????ServiceMethod<Object, Object>serviceMethod=

???????????????????????????????(ServiceMethod<Object, Object>)loadServiceMethod(method);

???????????????????????OkHttpCall<Object>okHttpCall= newOkHttpCall<>(serviceMethod,args);

???????????????????????returnserviceMethod.adapt(okHttpCall);

???????????????????}

???????????????});

???}

Retrofit動態(tài)代理實現(xiàn)流程圖:

二:AIDL實現(xiàn)進(jìn)程之間的通信(底層使用Binder實現(xiàn))

自定義通信接口:

interfaceIMyAidlInterface{

???????intgetBookCount();

???????voidaddBook(String book,intprice);

???}

生成的代理類代碼:(去除部分代碼是钥,僅保留關(guān)鍵代碼)

publicinterfaceIMyAidlInterfaceextendsandroid.os.IInterface{

???public static abstract class Stub extendsandroid.os.Binderimplementscom.jerey.learning.IMyAidlInterface{

???????private static finaljava.lang.StringDESCRIPTOR = "com.jerey.learning.IMyAidlInterface";

???????publicStub(){

???????????this.attachInterface(this, DESCRIPTOR);

???????}

???????public staticcom.jerey.learning.IMyAidlInterfaceasInterface(android.os.IBinderobj) {

???????}

???????@Override publicandroid.os.IBinderasBinder(){

???????????return this;

???????}

???????@Override publicbooleanonTransact(intcode,android.os.Parceldata,android.os.Parcelreply,intflags) throwsandroid.os.

???????RemoteException{

???????????returnsuper.onTransact(code, data, reply, flags);

???????}

????????//多么熟悉的Proxy

???????private static class Proxy implementscom.jerey.learning.IMyAidlInterface{

???????????privateandroid.os.IBindermRemote;

???????????Proxy(android.os.IBinderremote){

???????????????mRemote= remote;?

}

@Override publicandroid.os.IBinderasBinder(){

???????????????returnmRemote;

}

???????????publicjava.lang.StringgetInterfaceDescriptor(){

???????????????return DESCRIPTOR;

???????????}

???????????@Override publicintgetBookCount() throwsandroid.os.RemoteException{?//.........................

???????????}

???????????@Override public voidaddBook(java.lang.Stringbook,intprice) throwsandroid.os.RemoteException{?//........................

???????????}

???????}

???????static finalintTRANSACTION_getBookCount= (android.os.IBinder.FIRST_CALL_TRANSACTION+ 0);

???????static finalintTRANSACTION_addBook= (android.os.IBinder.FIRST_CALL_TRANSACTION+ 1);

???}

???publicintgetBookCount() throwsandroid.os.RemoteException;

???public voidaddBook(java.lang.Stringbook,intprice) throwsandroid.os.RemoteException;

}

附加:生成的代理類請參考網(wǎng)址:https://www.reibang.com/p/5646b9b7b898


三:Android的插件化實現(xiàn)原理之--hook機(jī)制

解析:調(diào)用采用了動態(tài)代理的辦法掠归,如果我們自己創(chuàng)建代理對象,然后把原始對象替換為我們的代理對象悄泥,那么就可以在這個代理對象為所欲為了虏冻,修改參數(shù),替換返回值弹囚,我們稱之為Hook厨相。

???首先我們得找到被Hook的對象,我稱之為Hook點鸥鹉;什么樣的對象比較好Hook呢蛮穿?自然是容易找到的對象。什么樣的對象容易找到毁渗?

1践磅、靜態(tài)變量和單例,在一個進(jìn)程之內(nèi)灸异,靜態(tài)變量和單例變量是相對不容易發(fā)生變化的府适,因此非常容易定位,而普通的對象則要么無法標(biāo)志肺樟,要么容易改變檐春。

2、盡量Hookpulic的對象和方法么伯,非public不保證每個版本都一樣疟暖,需要適配。

??找到了Hook點之后田柔,這個hook點就是一個被代理對象俐巴,我們就可以動態(tài)的生成代理類,創(chuàng)建代理類對象凯楔,并通過反射獲取被代理對象窜骄,然后把被代理對象替換成我們的代理類對象,從而達(dá)到擴(kuò)展被代理對象的功能摆屯,甚至完全修改相關(guān)操作行為邻遏。??


五:擴(kuò)展閱讀

1糠亩、https://blog.csdn.net/qq_30993595/article/details/90796869(Android開發(fā)如何理解Java靜態(tài)代理 動態(tài)代理及動態(tài)生成代理對象原理 看這篇就夠了)

2、http://www.reibang.com/p/64d205a159e6(一次Android權(quán)限刪除經(jīng)歷)

3准验、https://blog.csdn.net/sinat_23092639/article/details/102237404(從動態(tài)代理角度看Retrofit)

4赎线、http://www.reibang.com/p/7068295be51a(Android中使用Java的動態(tài)代理)

5、http://weishu.me/2016/01/28/understand-plugin-framework-proxy-hook/(Android插件化原理解析——Hook機(jī)制之動態(tài)代理)

6糊饱、http://www.reibang.com/p/08203d371f1c(將cglib動態(tài)代理思想帶入Android開發(fā))

7垂寥、http://www.reibang.com/p/e709aff78a53(Java動態(tài)代理機(jī)制詳解)

8、https://segmentfault.com/a/1190000012278673(人人都會設(shè)計模式:代理模式--Proxy)

9另锋、https://blog.csdn.net/yingpaixiaochuan/article/details/85232965(動態(tài)代理在Retrofit中的使用)

10滞项、https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html(Java動態(tài)代理機(jī)制分析及擴(kuò)展,第1部分)

11夭坪、http://weishu.me/2016/01/28/understand-plugin-framework-proxy-hook/(Android插件化原理解析——Hook機(jī)制之動態(tài)代理)

12文判、http://weishu.me/2016/01/28/understand-plugin-framework-overview/(Android插件化原理解析——概要)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市室梅,隨后出現(xiàn)的幾起案子戏仓,更是在濱河造成了極大的恐慌,老刑警劉巖亡鼠,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赏殃,死亡現(xiàn)場離奇詭異,居然都是意外死亡间涵,警方通過查閱死者的電腦和手機(jī)仁热,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浑厚,“玉大人股耽,你說我怎么就攤上這事∏” “怎么了物蝙?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長敢艰。 經(jīng)常有香客問我诬乞,道長,這世上最難降的妖魔是什么钠导? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任震嫉,我火速辦了婚禮,結(jié)果婚禮上牡属,老公的妹妹穿的比我還像新娘票堵。我一直安慰自己,他們只是感情好逮栅,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布悴势。 她就那樣靜靜地躺著窗宇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪特纤。 梳的紋絲不亂的頭發(fā)上军俊,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機(jī)與錄音捧存,去河邊找鬼粪躬。 笑死,一個胖子當(dāng)著我的面吹牛昔穴,可吹牛的內(nèi)容都是我干的镰官。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼傻咖,長吁一口氣:“原來是場噩夢啊……” “哼朋魔!你這毒婦竟也來了岖研?” 一聲冷哼從身側(cè)響起卿操,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎孙援,沒想到半個月后害淤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡拓售,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年窥摄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片础淤。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡崭放,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鸽凶,到底是詐尸還是另有隱情币砂,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布玻侥,位于F島的核電站决摧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏凑兰。R本人自食惡果不足惜掌桩,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望姑食。 院中可真熱鬧波岛,春花似錦、人聲如沸音半。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至隔躲,卻和暖如春摩梧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宣旱。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工仅父, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人浑吟。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓笙纤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親组力。 傳聞我的和親對象是個殘疾皇子省容,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345