第六章、動(dòng)態(tài)代理與反射機(jī)制

反射機(jī)制是java語(yǔ)言提供的一種基礎(chǔ)功能,賦予程序在運(yùn)行時(shí)自省的能力。通過反射可以直接操作類或者對(duì)象,比如獲取某個(gè)對(duì)象的類定義,獲取類聲明的屬性和方法,調(diào)用方法或者構(gòu)造對(duì)象,甚至可以運(yùn)行時(shí)修改類定義。

動(dòng)態(tài)代理時(shí)一種方便運(yùn)行時(shí)動(dòng)態(tài)構(gòu)建代理、動(dòng)態(tài)處理代理方法調(diào)用的機(jī)制,很多場(chǎng)景都是利用類似機(jī)制做到的,比如用來包裝RPC調(diào)用、面向切面的編程(AOP)。

實(shí)現(xiàn)動(dòng)態(tài)代理的方式很多,比如JDK自身提供的動(dòng)態(tài)代理,就是主要利用了上面提到的反射機(jī)制,還有其他的實(shí)現(xiàn)方式,比如利用傳說中更高興能的字節(jié)碼操作機(jī)制。


package test; public interface Hello { void sayHello(); }


package test;
?public class HelloImpl implements Hello{
?public void sayHello() { System.out.println("hello world"); }
?}

package test;
?import java.lang.reflect.InvocationHandler;
?import java.lang.reflect.Method;
?class MyInvocationHandler implements InvocationHandler{
?????private Object target;
?????public MyInvocationHandler(Object target) { this.target=target; }
?????public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { ????????System.out.println("Invoking sayHello");
?????Object result = method.invoke(target, args); return result;
?????}
}

package test;
?import java.lang.reflect.Proxy;
?public class MyDynamicProxy {
?public static void main(String[] args){
?HelloImpl hello=new HelloImpl();
?MyInvocationHandler handler = new MyInvocationHandler(hello); //構(gòu)造代碼實(shí)例 Hello proxyHello=(Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(),HelloImpl.class.getInterfaces(), handler); //調(diào)用代理方法 proxyHello.sayHello(); } }?

反射機(jī)制的動(dòng)態(tài)代理運(yùn)行實(shí)例



擴(kuò)展

靜態(tài)代理:事先寫好代理類,可以手工編寫,也可以用工具生成。缺點(diǎn)是每個(gè)業(yè)務(wù)類都要對(duì)應(yīng)一個(gè)代理類,非常不靈活。

動(dòng)態(tài)代理:運(yùn)行時(shí)自動(dòng)生成代理對(duì)象。缺點(diǎn)是生成代理代理對(duì)象和調(diào)用代理方法都要額外花費(fèi)時(shí)間。

JDK動(dòng)態(tài)代理:基于Java反射機(jī)制實(shí)現(xiàn),必須要實(shí)現(xiàn)了接口的業(yè)務(wù)類才能用這種辦法生成代理對(duì)象。新版本也開始結(jié)合ASM機(jī)制。

cglib動(dòng)態(tài)代理:基于ASM機(jī)制實(shí)現(xiàn),通過生成業(yè)務(wù)類的子類作為代理類。

Java 反射機(jī)制的常見應(yīng)用:動(dòng)態(tài)代理(AOP、RPC)、提供第三方開發(fā)者擴(kuò)展能力(Servlet容器,JDBC連接)、第三方組件創(chuàng)建對(duì)象(DI)




1 關(guān)于反射

反射最大的作用之一就在于我們可以不在編譯時(shí)知道某個(gè)對(duì)象的類型,而在運(yùn)行時(shí)通過提供完整的”包名+類名.class”得到。注意:不是在編譯時(shí),而是在運(yùn)行時(shí)。

功能:

?在運(yùn)行時(shí)能判斷任意一個(gè)對(duì)象所屬的類。

?在運(yùn)行時(shí)能構(gòu)造任意一個(gè)類的對(duì)象。

?在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法。

?在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法。

利用Java反射機(jī)制我們可以加載一個(gè)運(yùn)行時(shí)才得知名稱的class,獲悉其構(gòu)造方法,并生成其對(duì)象實(shí)體,能對(duì)其fields設(shè)值并喚起其methods。

應(yīng)用場(chǎng)景:

反射技術(shù)常用在各類通用框架開發(fā)中。因?yàn)闉榱吮WC框架的通用性,需要根據(jù)配置文件加載不同的對(duì)象或類,并調(diào)用不同的方法,這個(gè)時(shí)候就會(huì)用到反射——運(yùn)行時(shí)動(dòng)態(tài)加載需要加載的對(duì)象。

特點(diǎn):

由于反射會(huì)額外消耗一定的系統(tǒng)資源,因此如果不需要?jiǎng)討B(tài)地創(chuàng)建一個(gè)對(duì)象,那么就不需要用反射。另外,反射調(diào)用方法時(shí)可以忽略權(quán)限檢查,因此可能會(huì)破壞封裝性而導(dǎo)致安全問題。


2 動(dòng)態(tài)代理

為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。在某些情況下,一個(gè)對(duì)象不適合或者不能直接引用另一個(gè)對(duì)象,而代理對(duì)象可以在兩者之間起到中介的作用(可類比房屋中介,房東委托中介銷售房屋、簽訂合同等)。

所謂動(dòng)態(tài)代理,就是實(shí)現(xiàn)階段不用關(guān)心代理誰(shuí),而是在運(yùn)行階段才指定代理哪個(gè)一個(gè)對(duì)象(不確定性)。如果是自己寫代理類的方式就是靜態(tài)代理(確定性)。

組成要素:

(動(dòng)態(tài))代理模式主要涉及三個(gè)要素:

其一:抽象類接口

其二:被代理類(具體實(shí)現(xiàn)抽象接口的類)

其三:動(dòng)態(tài)代理類:實(shí)際調(diào)用被代理類的方法和屬性的類

實(shí)現(xiàn)方式:

實(shí)現(xiàn)動(dòng)態(tài)代理的方式很多,比如 JDK 自身提供的動(dòng)態(tài)代理,就是主要利用了反射機(jī)制。還有其他的實(shí)現(xiàn)方式,比如利用字節(jié)碼操作機(jī)制,類似 ASM、CGLIB(基于 ASM)、Javassist 等。

舉例,??刹捎玫腏DK提供的動(dòng)態(tài)代理接口InvocationHandler來實(shí)現(xiàn)動(dòng)態(tài)代理類。其中invoke方法是該接口定義必須實(shí)現(xiàn)的,它完成對(duì)真實(shí)方法的調(diào)用。通過InvocationHandler接口,所有方法都由該Handler來進(jìn)行處理,即所有被代理的方法都由InvocationHandler接管實(shí)際的處理任務(wù)。此外,我們??梢栽趇nvoke方法實(shí)現(xiàn)中增加自定義的邏輯實(shí)現(xiàn),實(shí)現(xiàn)對(duì)被代理類的業(yè)務(wù)邏輯無侵入。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容