代理是一種軟件設(shè)計模式,目的是通過不直接調(diào)用被代理對象而訪問其方法,提高代碼重用率砍艾。
一般用于框架本身處理多種業(yè)務(wù)邏輯時,通過代理機制方便業(yè)務(wù)方調(diào)用巍举,也利于框架的拓展和解耦脆荷。在代碼編譯時就確定目標(biāo)類是哪個,可用靜態(tài)代理。動態(tài)代理是在代碼運行時加載目標(biāo)類蜓谋。
靜態(tài)代理步驟:
- 目標(biāo)類和代理類都實現(xiàn)相同的業(yè)務(wù)接口梦皮;
- 代理類的構(gòu)造方法中傳入目標(biāo)類的實例;
- 在代理類的接口實現(xiàn)中調(diào)用目標(biāo)類實例的接口方法桃焕;
靜態(tài)代理的缺點是不同的業(yè)務(wù)剑肯,需要實現(xiàn)多個目標(biāo)實現(xiàn)類,代碼冗余高观堂。
動態(tài)代理原理:
動態(tài)代理中涉及到InvocationHandler接口和Proxy類让网。
Proxy類的newProxyInstance()方法負(fù)責(zé)代理類實例的創(chuàng)建。
public static Object newProxyInstance(
ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
- ClassLoader loader:指定一個動態(tài)加載代理類的類加載器师痕。
- Class<?>[] interfaces:指明目標(biāo)類實現(xiàn)的接口溃睹。
- InvocationHandler h:方法委托類。
Proxy類與普通類的唯一區(qū)別就是其字節(jié)碼是由 JVM 在運行時動態(tài)生成的而非預(yù)存在于任何一個 .class 文件中胰坟。
InvocationHandler的接口方法是invoke()因篇,通過代理類調(diào)用目標(biāo)類方法時,最終都會委托此方法執(zhí)行笔横。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
- Object proxy:代理類實例
- Method method:調(diào)用的方法對象
- Object[] args:調(diào)用的參數(shù)
一般會在此方法前后增加一些操作竞滓,對某個業(yè)務(wù)流程補充完善子流程,即面向切面編程(AOP)吹缔。
面向切面編程(AOP):是在一系列縱向的控制流程中商佑,把那些相同的子流程提取成一個橫向的面。即通過預(yù)編譯方式和運行期動態(tài)代理涛菠,實現(xiàn)在不修改源代碼的情況下給程序統(tǒng)一添加功能的技術(shù)思想莉御。
動態(tài)代理步驟:
- 創(chuàng)建目標(biāo)業(yè)務(wù)接口和實現(xiàn)類實例;
- 通過Proxy類的newProxyInstance()方法獲取代理類實例俗冻;
- 創(chuàng)建InvocationHandler方法委托類實例,實現(xiàn)代理類到目標(biāo)類的方法分派牍颈,并支持在目標(biāo)類業(yè)務(wù)前后增改操作迄薄;
目標(biāo)類及其接口 示例如下:
/**
* 目標(biāo)業(yè)務(wù)接口
*/
public interface IProxy {
void print();
}
/**
* 目標(biāo)類——實現(xiàn)目標(biāo)業(yè)務(wù)接口
*/
public class MyProxy implements IProxy {
@Override
public void print() {
Log.d("test","面向AOP編程——進行中");
}
}
方法委托類 示例如下:
/**
* 方法委托類——實現(xiàn)InvocationHandler接口
*/
public class MyInvocationHandler implements InvocationHandler {
private MyProxy myProxy;
/**
* 通過構(gòu)造器注入被目標(biāo)類實例
* @param myProxy
*/
public MyInvocationHandler(MyProxy myProxy) {
this.myProxy = myProxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.d("test", "面向AOP編程——準(zhǔn)備中");//模擬前置操作
Object object = method.invoke(this.myProxy, args);//通過反射調(diào)用目標(biāo)類方法
Log.d("test", "面向AOP編程——結(jié)束后");//模擬后置操作
return object;
}
}
外部框架調(diào)用目標(biāo)類 示例如下:
private void testProxy() {
MyProxy myProxy = new MyProxy();
IProxy iProxy = (IProxy) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
myProxy.getClass().getInterfaces(),
new MyInvocationHandler(myProxy));
iProxy.print();
}
ClassLoader是類裝載器,將類的字節(jié)碼裝載到 Java 虛擬機(JVM)中并為其定義類對象煮岁,然后該類才能被使用讥蔽。 因此每次生成動態(tài)代理類對象時都需要指定一個類裝載器對象。
打印log如下:
D/test: 面向AOP編程——準(zhǔn)備中
D/test: 面向AOP編程——進行中
D/test: 面向AOP編程——結(jié)束后