無(wú)論是在服務(wù)端開(kāi)發(fā)還是客戶(hù)端開(kāi)發(fā)很多基本的思想和設(shè)計(jì)模式都是想通。比如去年記得阿里開(kāi)源了BeeHive就是受了java中的Spring的思想,除此之外其實(shí)還有很多很多類(lèi)似由一門(mén)語(yǔ)言遷移到另一門(mén)語(yǔ)言的三方庫(kù)。
平時(shí)在看一些iOS三方源碼的時(shí)候發(fā)現(xiàn)都會(huì)用到swizzle技術(shù),今天就來(lái)一起看看在java中,或者說(shuō)andriod中通過(guò)什么方式可以達(dá)到類(lèi)似的效果。
讀完本文,你可以了解到:
- iOS中的代理和java中的代理是怎么回事
- java中的靜態(tài)代理與靜態(tài)代理的原理
- 面向切片(AOP)的思想
- Android中Aop和iOS中Aop的比較。
代理模式
關(guān)于什么是代理設(shè)計(jì)模式,這里就不多說(shuō)了??紤]到有些同學(xué)坑你還不是特別了解設(shè)計(jì)模式。那么如果想繼續(xù)讀下去還是去復(fù)習(xí)一下設(shè)計(jì)模式基礎(chǔ)知識(shí)會(huì)好一點(diǎn)。這里推薦C#可以看《大話設(shè)計(jì)模式》,java可以看《Java設(shè)計(jì)模式》。不用一一看完,大致了解一下,需要用得到時(shí)候在仔細(xì)看也不遲。
在iOS開(kāi)發(fā)中經(jīng)常會(huì)用到代理設(shè)計(jì)模式,代理解決的問(wèn)題當(dāng)兩個(gè)類(lèi)需要通信時(shí),引入第三方代理類(lèi),將兩個(gè)類(lèi)的關(guān)系解耦,讓我們只了解代理類(lèi)即可,而且代理的出現(xiàn)還可以讓我們完成與另一個(gè)類(lèi)之間的關(guān)系的統(tǒng)一管理。在iOS面試的時(shí)候經(jīng)常會(huì)問(wèn)的就是請(qǐng)介紹一下block、代理、通知三者之間的區(qū)別和聯(lián)系。
這里我用最為通俗的方式來(lái)解釋一下我對(duì)代理的理解:就是在原有的代碼里面打個(gè)標(biāo)記(調(diào)用一個(gè)函數(shù),這個(gè)函數(shù)一般情況下需要接口來(lái)約束一下。),當(dāng)走到這里的時(shí)候就去其他執(zhí)行代碼,那么以后如果有更為復(fù)雜的邏輯就可以在其他地方改了。這樣就達(dá)到一定的解耦。
目的一般就是:
- 不修改已有代碼進(jìn)行功能增強(qiáng)。
- 剔除方法中的非核心邏輯,精簡(jiǎn)代碼。
Java靜態(tài)代理
所謂靜態(tài)也就是在程序運(yùn)行前就已經(jīng)存在代理類(lèi)的字節(jié)碼文件,代理類(lèi)在運(yùn)行前就確定了。
例子
- 定義代理接口 (類(lèi)似于OC中定義protocal):一般用于約束代理的行為
public interface UserManager {
public void addUser(String userId);
}
- 定義委托類(lèi)(類(lèi)似于OC中單獨(dú)用一個(gè)類(lèi)實(shí)現(xiàn)協(xié)議一個(gè)協(xié)議):具體處理業(yè)務(wù)
public class UserManagerImpl implements UserManager {
public void addUser(String userId) {
System.out.print("add User----UserManagerImpl");
}
}
- 靜態(tài)代理類(lèi) (OC中委托類(lèi)和代理類(lèi)一般是放在一個(gè)類(lèi)中,這點(diǎn)和java有比較大的差別)
public class UserManagerImplProxy implements UserManager {
// 目標(biāo)對(duì)象
private UserManager userManager;
// 通過(guò)構(gòu)造方法傳入目標(biāo)對(duì)象
public UserManagerImplProxy(UserManager userManager){
this.userManager=userManager;
}
public void addUser(String userId) {
// 調(diào)用實(shí)際處理業(yè)務(wù)的委托類(lèi),達(dá)到解耦的目的
userManager.addUser(userId);
System.out.println("add User--UserManagerImplProxy" + userId);
// userManager.addUser(userId);
}
}
- 測(cè)試代碼
public static void main(String[] args) {
UserManager userManagerImpl = new UserManagerImpl();
UserManager userManagerImplProxy = new UserManagerImplProxy(userManagerImpl);
userManagerImplProxy.addUser("1234");
}
運(yùn)行結(jié)果:
add User----UserManagerImpl1234
add User--UserManagerImplProxy1234
其實(shí)上面的代碼有一點(diǎn)點(diǎn)Aop的雛形。也就是在不改變?cè)瓉?lái)代碼的情況下,增加新的功能。這里如果以后需要擴(kuò)展業(yè)務(wù)就不用改UserManagerImplProxy代碼了,直接在UserManagerImpl中修改業(yè)務(wù)邏輯就可以了。不知道自己講清楚沒(méi)有。
始終記住aop,或者代理的目的就是:
- 不修改已有代碼進(jìn)行功能增強(qiáng)。
- 剔除方法中的非核心邏輯,精簡(jiǎn)代碼。
缺點(diǎn)明顯
靜態(tài)代理在iOS其實(shí)用得非常之多,這里只是用java舉了個(gè)例。一個(gè)很明顯的缺點(diǎn)就是:
- 代理類(lèi)和委托類(lèi)實(shí)現(xiàn)了相同的接口,代理類(lèi)通過(guò)委托類(lèi)實(shí)現(xiàn)了相同的方法。這樣就出現(xiàn)了大量的代碼重復(fù)。如果接口增加一個(gè)方法,除了所有實(shí)現(xiàn)類(lèi)需要實(shí)現(xiàn)這個(gè)方法外,所有代理類(lèi)也需要實(shí)現(xiàn)此方法。增加了代碼維護(hù)的復(fù)雜度。
一個(gè)更為實(shí)際的例子,比如在iOS中要統(tǒng)計(jì)每個(gè)頁(yè)面停留的時(shí)間,不可能每個(gè)控制器都去統(tǒng)計(jì)吧;在java中如果想讓每個(gè)實(shí)現(xiàn)類(lèi)都添加打印日志的功能的話,就需要添加多個(gè)代理類(lèi),以及代理類(lèi)中各個(gè)方法都需要添加打印日志功能。
靜態(tài)代理最為嚴(yán)重的缺點(diǎn)就是:靜態(tài)代理類(lèi)只能為特定的接口(Service)服務(wù)。如想要為多個(gè)接口服務(wù)則需要建立很多個(gè)代理類(lèi)。
java動(dòng)態(tài)代理
動(dòng)態(tài)代理解決了靜態(tài)代理的缺點(diǎn),可以不與某個(gè)類(lèi)或接口強(qiáng)綁定。它的原理也是有點(diǎn)復(fù)雜的,大致過(guò)程就是程序運(yùn)行期間動(dòng)態(tài)的生成一個(gè)比特?cái)?shù)組,這個(gè)數(shù)組能夠表示為目標(biāo)類(lèi)的子類(lèi),然后把數(shù)組交給ClassLoader進(jìn)行解析,并返回子類(lèi)的實(shí)例,這個(gè)子類(lèi)的實(shí)例實(shí)際上可以看做目標(biāo)類(lèi)的代理類(lèi)。(有點(diǎn)類(lèi)似iOS中一個(gè)很著名的Aop框架Aspect的原理,都是在運(yùn)行的時(shí)候生成子類(lèi)。)
java能夠做到動(dòng)態(tài)代理因?yàn)?strong>Java是個(gè)編譯型的語(yǔ)言,首先會(huì)把Java代碼編譯成class字節(jié)碼,然后JVM去加載、解釋字節(jié)碼,通過(guò)ClassLoader類(lèi)可以在程序運(yùn)行期間動(dòng)態(tài)的加載字節(jié)碼生成一個(gè)類(lèi)。
java實(shí)現(xiàn)動(dòng)態(tài)代理有兩種方式,一種是JDK自帶的動(dòng)態(tài)代理,要求目標(biāo)類(lèi)必須實(shí)現(xiàn)一個(gè)接口InvocationHandler,接口方法就是增強(qiáng)方法;另一種是CGLIB動(dòng)態(tài)代理,要求目標(biāo)類(lèi)不能為final,否則不能生成子類(lèi)。今天講的就是用InvocationHandler實(shí)現(xiàn)動(dòng)態(tài)代理。
目前大致的如下方法實(shí)現(xiàn)Aop:

接口InvocationHandler
文檔里面這樣介紹的:
public interface InvocationHandler
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
大致意思就是:
每一個(gè)動(dòng)態(tài)代理類(lèi)都必須要實(shí)現(xiàn)InvocationHandler這個(gè)接口,并且每個(gè)代理類(lèi)的實(shí)例都關(guān)聯(lián)到了一個(gè)handler,當(dāng)我們通過(guò)代理對(duì)象調(diào)用一個(gè)方法的時(shí)候,這個(gè)方法的調(diào)用就會(huì)被轉(zhuǎn)發(fā)為由InvocationHandler這個(gè)接口的 invoke 方法來(lái)進(jìn)行調(diào)用。
invoke方法:Object invoke(Object proxy, Method method, Object[] args) throws Throwable。三個(gè)參數(shù)意思分別是
- proxy: 指代我們所代理的那個(gè)真實(shí)對(duì)象
- method: 指代的是我們所要調(diào)用真實(shí)對(duì)象的某個(gè)方法的Method對(duì)象
- args: 指代的是調(diào)用真實(shí)對(duì)象某個(gè)方法時(shí)接受的參數(shù)
Proxy
文檔說(shuō)明:
Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.
Proxy這個(gè)類(lèi)的作用就是用來(lái)動(dòng)態(tài)創(chuàng)建一個(gè)代理對(duì)象的類(lèi),它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個(gè)方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException參數(shù)說(shuō)明:
- loader: 一個(gè)ClassLoader對(duì)象,定義了由哪個(gè)ClassLoader對(duì)象來(lái)對(duì)生成的代理對(duì)象進(jìn)行加載。
- interfaces: 一個(gè)Interface對(duì)象的數(shù)組,表示的是我將要給我需要代理的對(duì)象提供一組什么接口,如果我提供了一組接口給它,那么這個(gè)代理對(duì)象就宣稱(chēng)實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了。
- h: 一個(gè)InvocationHandler對(duì)象,表示的是當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上。
最后我們還是來(lái)看看實(shí)際例子
例子
接口還是用UserManager.
- 定義代理接口:
public interface UserManager {
public void addUser(String userId);
}
- 定義委托類(lèi):
public class UserManageDynamicImpl implements UserManager {
public void addUser(String userId) {
System.out.println("UserManageDynamicImpl: add User" + userId);
}
}
- 定義動(dòng)態(tài)代理類(lèi)(日志記錄)
public class LogInvocationHander implements InvocationHandler {
// 引入日志框架
private Logger logger = Logger.getLogger(this.getClass().getSimpleName());
// 真實(shí)對(duì)象,目標(biāo)代理類(lèi)
private Object target;
// 代理類(lèi)
private Object proxy;
// 存儲(chǔ)日記記錄字典
private static HashMap<Class<?>, LogInvocationHander> invoHandlers = new HashMap<Class<?>, LogInvocationHander>();
// java中單例的寫(xiě)法
private LogInvocationHander() {
}
/**
* 類(lèi)方法
* @param clazz
* @param <T>
* @return
*/
public synchronized static <T> T getProxyInstance(Class<T> clazz) {
LogInvocationHander invoHandler = invoHandlers.get(clazz);
if (invoHandler == null) {
invoHandler = new LogInvocationHander();
try {
T tar = clazz.newInstance();
// 設(shè)置目標(biāo)代理類(lèi)
invoHandler.setTarget(tar);
// Proxy這個(gè)類(lèi)的作用就是用來(lái)動(dòng)態(tài)創(chuàng)建一個(gè)代理對(duì)象的類(lèi),它提供了許多的方法,
// 但是我們用的最多的就是 newProxyInstance 這個(gè)方法
/*loader: 一個(gè)ClassLoader對(duì)象,定義了由哪個(gè)ClassLoader對(duì)象來(lái)對(duì)生成的代理對(duì)象進(jìn)行加載
interfaces: 一個(gè)Interface對(duì)象的數(shù)組,表示的是我將要給我需要代理的對(duì)象提供一組什么接口,如果我提供了一組接口給它,那么這個(gè)代理對(duì)象就宣稱(chēng)實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了
h: 一個(gè)InvocationHandler對(duì)象,表示的是當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上
*/
// 設(shè)置代理類(lèi)
invoHandler.setProxy(Proxy.newProxyInstance(tar.getClass().getClassLoader(),tar.getClass().getInterfaces(),invoHandler));
} catch (Exception e) {
e.printStackTrace();
}
invoHandlers.put(clazz, invoHandler);
}
return (T)invoHandler.getProxy();
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Object getProxy() {
return proxy;
}
public void setProxy(Object proxy) {
this.proxy = proxy;
}
/**
proxy: 指代我們所代理的那個(gè)真實(shí)對(duì)象
method: 指代的是我們所要調(diào)用真實(shí)對(duì)象的某個(gè)方法的Method對(duì)象
args: 指代的是調(diào)用真實(shí)對(duì)象某個(gè)方法時(shí)接受的參數(shù)
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在代理真實(shí)對(duì)象前我們可以添加一些自己的操作
logger.info("before call");
// 當(dāng)代理對(duì)象調(diào)用真實(shí)對(duì)象的方法時(shí),其會(huì)自動(dòng)的跳轉(zhuǎn)到代理對(duì)象關(guān)聯(lián)的handler對(duì)象的invoke方法來(lái)進(jìn)行調(diào)用
Object result = method.invoke(target, args); // 執(zhí)行業(yè)務(wù)處理
// 打印日志
logger.info("____invoke method: " + method.getName()
+ "; args: " + (null == args ? "null" : Arrays.asList(args).toString())
+ "; return: " + result);
// 在代理真實(shí)對(duì)象后我們也可以添加一些自己的操作
logger.info("after call");
return result;
}
}
上面的代碼注釋已經(jīng)足夠說(shuō)明問(wèn)題了。還有一部分需要解釋下:
通過(guò) Proxy.newProxyInstance 創(chuàng)建的代理對(duì)象是在jvm運(yùn)行時(shí)動(dòng)態(tài)生成的一個(gè)對(duì)象,它并不是我們的InvocationHandler類(lèi)型,也不是我們定義的那組接口的類(lèi)型,而是在運(yùn)行是動(dòng)態(tài)生成的一個(gè)對(duì)象,并且命名方式都是這樣的形式,以$開(kāi)頭,proxy為中,最后一個(gè)數(shù)字表示對(duì)象的標(biāo)號(hào)。
通過(guò)代理對(duì)象來(lái)調(diào)用實(shí)現(xiàn)的那種接口中的方法,這個(gè)時(shí)候程序就會(huì)跳轉(zhuǎn)到由這個(gè)代理對(duì)象關(guān)聯(lián)到的 handler 中的invoke方法去執(zhí)行,而我們的這個(gè) handler 對(duì)象又接受了一個(gè) target類(lèi)型的參數(shù),表示我要代理的就是這個(gè)真實(shí)對(duì)象,所以此時(shí)就會(huì)調(diào)用 handler 中的invoke方法去執(zhí)行。