此篇文章說(shuō)是通過(guò)代理模式來(lái)實(shí)現(xiàn)簡(jiǎn)單的AOP其實(shí)只是順帶的,主要目的還是講一下代理模式,在Android中使用的代理模式主要分為靜態(tài)代理和動(dòng)態(tài)代理,靜態(tài)代理編譯期間就已確認(rèn)代理類,而動(dòng)態(tài)代理在運(yùn)行期間才能確認(rèn)代理類。
1、代理模式簡(jiǎn)介
- 定義:給一個(gè)對(duì)象提供一個(gè)代理對(duì)象,通過(guò)代理對(duì)象來(lái)控制目標(biāo)對(duì)象的訪問(wèn),代理對(duì)象就像是中介,起到客戶到目標(biāo)的溝通,即客戶不直接操控目標(biāo)對(duì)象,而是通過(guò)中介(代理對(duì)象)間接地操控目標(biāo)對(duì)象。
通過(guò)類圖可以發(fā)現(xiàn),代理模式的代理對(duì)象Proxy
和目標(biāo)對(duì)象Subject
實(shí)現(xiàn)同一個(gè)接口,客戶調(diào)用的是Proxy
對(duì)象,Proxy
可以控制Subject
的訪問(wèn),真正的功能實(shí)現(xiàn)是在Subject
完成的。
代理模式在Java中的實(shí)現(xiàn)分為兩種,一種是靜態(tài)代理,一種是動(dòng)態(tài)代理。
- 靜態(tài)代理其實(shí)就是以上UML類圖的標(biāo)準(zhǔn)實(shí)現(xiàn),其在編譯期間就會(huì)確定真正的代理類,即.class文件編譯完成后就確定下來(lái)了。
- 動(dòng)態(tài)代理是標(biāo)準(zhǔn)靜態(tài)代理的一種擴(kuò)展和變形,最主要一點(diǎn)是,動(dòng)態(tài)代理在編譯完成后是沒(méi)有一個(gè)代理類的具體類的,但這不代表它沒(méi)有代理類,其代理類會(huì)在運(yùn)行期間自動(dòng)生成,并通過(guò)反射的方式進(jìn)行方法調(diào)用。
2、靜態(tài)代理
靜態(tài)代理可以說(shuō)是很簡(jiǎn)單了,拿點(diǎn)江湖事來(lái)舉例,一個(gè)武林盟主想去揍一個(gè)人,他一般不會(huì)自己去,他可以找武當(dāng)派的人,或者少林寺的人,最后打人的是各個(gè)門派的弟子,武林盟主其實(shí)除了下命令之后啥都沒(méi)干,干活的都是小弟。
/**
* Author:xishuang
* Date:2018.03.07
* Des:少林絕學(xué)《易筋經(jīng)》
*/
public interface IShaolin {
void playYiGinChing();
}
/**
* Author:xishuang
* Date:2018.03.07
* Des:武當(dāng)派絕學(xué)太極拳
*/
public interface IWuDang {
void playTaijiquan();
}
public class ShaoLinMan implements IShaolin {
@Override
public void playYiGinChing() {
System.out.println("在下少林->易筋經(jīng)");
}
}
public class WuDangMan implements IWuDang {
@Override
public void playTaijiquan() {
System.out.println("在下武當(dāng)->太極拳");
}
}
/**
* Author:xishuang
* Date:2018.02.06
* Des:靜態(tài)代理需要的代理類,編譯器就已經(jīng)確定
*/
public class MengZhuProxy implements IWuDang, IShaolin {
private IWuDang mWuDangMan;
private IShaolin mShaolinMan;
public MengZhuProxy() {
mWuDangMan = new WuDangMan();
mShaolinMan = new ShaoLinMan();
}
@Override
public void playYiGinChing() {
System.out.println("給自己加戲");
mShaolinMan.playYiGinChing();
}
@Override
public void playTaijiquan() {
System.out.println("給自己加戲");
mWuDangMan.playTaijiquan();
}
}
就像代碼里頭展示的,定義好ISubject
接口,這個(gè)接口可以是多個(gè),這里定義了兩個(gè)IShaolin
和IWuDang
,再定義類圖中的目標(biāo)對(duì)象類ISubject
,這里的各自接口實(shí)現(xiàn)類是ShaoLinMan
和WuDangMan
,而MengZhuProxy
就是代理類盟主了,盟主不需要自己會(huì)少林絕學(xué),他只需要叫少林寺的弟子去干活,盟主也不必會(huì)武當(dāng)太極拳,他能控制武當(dāng)派弟子就好。
/**
* 靜態(tài)代理
*/
private void staticProxy() {
MengZhuProxy proxy = new MengZhuProxy();
proxy.playTaijiquan();
proxy.playYiGinChing();
}
運(yùn)行效果就是這樣,沒(méi)啥大驚小怪的,基本上就是按照UML類圖照葫蘆畫瓢就完成了。
3、動(dòng)態(tài)代理
相比于靜態(tài)代理, 動(dòng)態(tài)代理的優(yōu)勢(shì)在于可以很方便的對(duì)代理類的函數(shù)進(jìn)行統(tǒng)一的處理,而不用修改每個(gè)代理類的函數(shù),這也就是AOP的一種實(shí)現(xiàn)方式,但是只能針對(duì)接口方法進(jìn)行處理,具體原因后面會(huì)順勢(shì)提到。
通過(guò)其UML類圖可以發(fā)現(xiàn)其比標(biāo)準(zhǔn)的類圖要復(fù)雜,主要引入了Proxy
類,這是Java自帶的,我們通過(guò)調(diào)用Proxy
類的newProxyInstance
方法來(lái)獲取一個(gè)代理類實(shí)例,此外還引入了InvocationHandler
接口及其實(shí)現(xiàn)類,稱之為調(diào)節(jié)處理器類。
初看起來(lái)有點(diǎn)懵逼,讓我們理一下,會(huì)發(fā)現(xiàn)動(dòng)態(tài)代理包含了兩層的靜態(tài)代理關(guān)系:
- 第一層是
Proxy
和InvocationHandler
,在這層關(guān)系中,Proxy
是代理類,而InvocationHandler
是真正的實(shí)現(xiàn)類,即目標(biāo)對(duì)象類。 - 第二層是
InvocationHandler
和ISubject
,這里的話,InvocationHandler
中持有ISubject
的實(shí)現(xiàn)類對(duì)象,所以InvocationHandler
是代理類,而ISubject
是最終的實(shí)現(xiàn)類。
會(huì)發(fā)現(xiàn),饒了一圈,最后的目標(biāo)對(duì)象還是ISubject
,和靜態(tài)代理的最主要區(qū)別在于SubjectProxy
這個(gè)類是在程序運(yùn)行中動(dòng)態(tài)生成的。
續(xù)上面的武林盟主栗子,我們只需要增加一個(gè)InvocationHandler
類,這個(gè)類會(huì)接收到所有對(duì)接口方法的調(diào)用,在其中我們可以對(duì)接口方法進(jìn)行攔截和實(shí)現(xiàn)自己的功能。
/**
* Author:xishuang
* Date:2018.02.07
* Des:具體的代理邏輯實(shí)現(xiàn)
*/
public class MengZhuInvocationHandler implements InvocationHandler {
/**
* InvocationHandler持有的被代理對(duì)象
*/
private IWuDang mWuDangMan;
private IShaolin mShaolinMan;
MengZhuInvocationHandler(IWuDang wuDang, IShaolin shaolin) {
mWuDangMan = wuDang;
mShaolinMan = shaolin;
}
/**
* 進(jìn)行具體的代理操作
*
* @param proxy 所代理的類
* @param method 正在調(diào)用得方法
* @param args 方法的參數(shù)
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("playTaijiquan")) {
System.out.println("給自己加戲-動(dòng)態(tài)代理");
mWuDangMan.playTaijiquan();
} else if (method.getName().equals("playYiGinChing")) {
System.out.println("給自己加戲-動(dòng)態(tài)代理");
mShaolinMan.playYiGinChing();
}
return proxy;
}
}
最關(guān)鍵在于invoke()
方法,它會(huì)攔截所有實(shí)現(xiàn)了接口的方法,根據(jù)不同的方法名來(lái)達(dá)到我們的目的,在InvocationHandler
類完成之后,就可以進(jìn)行調(diào)用,調(diào)用的代碼和運(yùn)行結(jié)果如下:
/**
* 動(dòng)態(tài)代理
*/
private static void daynamicProxy() {
// 創(chuàng)建一個(gè)與代理對(duì)象相關(guān)聯(lián)的InvocationHandler
InvocationHandler handler = new MengZhuInvocationHandler(new WuDangMan(), new ShaoLinMan());
// 創(chuàng)建一個(gè)代理對(duì)象studentProxy來(lái)代理student,代理對(duì)象的每個(gè)執(zhí)行方法都會(huì)替換執(zhí)行Invocation中的invoke方法
IWuDang studentProxy = (IWuDang) Proxy.newProxyInstance(getClassLoader(), new Class[]{IWuDang.class, IShaolin.class}, handler);
studentProxy.playTaijiquan();
((IShaolin) studentProxy).playYiGinChing();
}
對(duì)比靜態(tài)代理的使用過(guò)程,動(dòng)態(tài)代理的使用顯得要復(fù)雜一些,其中最關(guān)鍵的是
Proxy.newProxyInstance()
方法,用于返回動(dòng)態(tài)生成的代理對(duì)象,需要傳入三個(gè)參數(shù),類加載器ClassLoader
,需要實(shí)現(xiàn)的接口數(shù)組,以及InvocationHandler
攔截對(duì)象,這些參數(shù)都是生成動(dòng)態(tài)代理對(duì)象所需要的。
4、動(dòng)態(tài)代理調(diào)用分析
單單從代碼調(diào)用關(guān)系上來(lái)看是比較難看出UML類圖中的那種關(guān)系的,最主要原因是沒(méi)有直觀的看到動(dòng)態(tài)生成的SubjectProxy
類,所以我們就順著Proxy.newProxyInstance()
這條線來(lái)看一下背后的調(diào)用軌跡。
/**
* 返回指定接口的動(dòng)態(tài)生成類的實(shí)例
*/
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler invocationHandler)
throws IllegalArgumentException {
...
return getProxyClass(loader, interfaces)
.getConstructor(InvocationHandler.class)
.newInstance(invocationHandler);
...
}
首先通過(guò)類加載器和接口數(shù)組來(lái)生成類文件,然后通過(guò)反射的方式來(lái)實(shí)例化對(duì)象,并把InvocationHandler
在構(gòu)造方法中傳入以便后續(xù)調(diào)用,繼續(xù)看getProxyClass()
方法。
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
throws IllegalArgumentException {
...
for (Class<?> c : interfaces) {
if (!c.isInterface()) {
// 傳入的要實(shí)現(xiàn)類必須是接口類,否則會(huì)拋出異常
throw new IllegalArgumentException(c + " is not an interface");
}
...
}
...
Class<?> result;
synchronized (loader.proxyCache) {
result = loader.proxyCache.get(interfaceList);
if (result == null) {
String name = baseName + nextClassNameIndex++;
result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray);
loader.proxyCache.put(interfaceList, result);
}
}
return result;
}
這里只抽取關(guān)鍵代碼,可以發(fā)現(xiàn)源碼中限制了我們傳入的需要實(shí)現(xiàn)的類必須是接口類,非接口類會(huì)拋出異常,最后通過(guò)本地native方法generateProxy()
生成并返回類對(duì)象。
這一個(gè)過(guò)程是比較明了的,把動(dòng)態(tài)生成的類文件打印出來(lái)就知道代理類中的具體調(diào)用了,這里我們使用Java中提供的ProxyGenerator
類的靜態(tài)方法generateProxyClass()
,來(lái)打印出動(dòng)態(tài)生成的代理類class字節(jié)碼。
private static void generyClass() {
byte[] classFile = ProxyGenerator.generateProxyClass("GenerateProxy", new Class[]{IWuDang.class, IShaolin.class});
String path = "D:/項(xiàng)目/proxyTest/GenerateProxy.class";
try (FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("文件寫入成功");
} catch (Exception e) {
System.out.println("文件寫入失敗");
}
}
需要提到的一點(diǎn)是,在Android Studio中調(diào)用不了ProxyGenerator
這個(gè)類,避免太麻煩,所以我這邊是直接把項(xiàng)目遷到IntelliJ IDEA中,最后生成代理class文件。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class GenerateProxy extends Proxy implements IWuDang, IShaolin {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public GenerateProxy(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void playTaijiquan() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void playYiGinChing() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
// 通過(guò)反射機(jī)制來(lái)調(diào)用各個(gè)方法,動(dòng)態(tài)代理會(huì)對(duì)運(yùn)行期間的性能造成一定影響
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("IWuDang").getMethod("playTaijiquan");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("IShaolin").getMethod("playYiGinChing");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
SubjectProxy
繼承了Proxy
并實(shí)現(xiàn)我們定義的接口,其中每個(gè)方法調(diào)用時(shí)沒(méi)有做具體的事情,而是直接調(diào)用super.h.invoke()
,super.h
就是InvocationHandler
對(duì)象,所以SubjectProxy
把任務(wù)拋給了InvocationHandler
進(jìn)行處理,而InvocationHandler
就是通過(guò)我們可以控制的,繞來(lái)繞去把控制權(quán)又交到了我們的手中。
前面提到過(guò)動(dòng)態(tài)代理只能代理接口方法,原因也在這里,動(dòng)態(tài)生成的類直接實(shí)現(xiàn)我們定義的接口,從而實(shí)現(xiàn)其中的接口方法,在方法調(diào)用時(shí)再把功能交還到我們的手中。動(dòng)態(tài)代理可以統(tǒng)一對(duì)接口的所有實(shí)現(xiàn)類進(jìn)行操作,而不用修改每個(gè)實(shí)現(xiàn)類,通過(guò)Proxy
->InvocationHandler
->ISubject
的順序把控制權(quán)一步步的交接,最后真正的實(shí)現(xiàn)類和靜態(tài)代理是一樣的,只是因?yàn)橐獎(jiǎng)討B(tài)生成代理類從而導(dǎo)致中間過(guò)程饒了一點(diǎn)。