首先先理解一下為什么要使用代理丛晦。在日常生活中,代理可以解決業(yè)務(wù)相關(guān)雙方直接交流的不便的問題提陶,同時還可以提供比直接交流更多的功能烫沙,而在編程領(lǐng)域,代理類一般是做些除原始類核心功能以外的其他功能隙笆,比如修改權(quán)限等需要專門的代理來實現(xiàn)锌蓄。代碼每個類代表一個主要功能,而不是將所有功能混在一個類中撑柔,這樣的代碼清晰有條理瘸爽,易于維護,如果要修改權(quán)限铅忿,不必修改原始類代碼剪决,直接修改權(quán)限代理類就可以了。這樣不需要修改權(quán)限的代碼可以還用原始類檀训,需要的柑潦,就調(diào)用代理類,不會影響原來的功能峻凫。
代理類提供一個與原始相同的接口渗鬼,以便可以在任何時候替代原始。代理類通常在客戶端調(diào)用傳遞給原始類之前或之后荧琼,執(zhí)行某個操作譬胎,而不是單純地將調(diào)用傳遞給原始類,同時命锄,代理類可以在執(zhí)行原始類操作時堰乔,附加其他的操作,相當(dāng)于對原始類進(jìn)行封裝累舷。
而動態(tài)代理的作用是浩考,只要建立一個動態(tài)代理類,就可以為多個原始類進(jìn)行代理被盈,解決了靜態(tài)代理的問題(一個原始類對應(yīng)一個靜態(tài)代理析孽,有多少原始類就有多少靜態(tài)代理,造成代碼瑣碎)只怎。
在java的動態(tài)代理機制中袜瞬,有一個重要的接口 InvocationHandler(Interface)和一個重要的類 Proxy(Class),這兩個是實現(xiàn)動態(tài)代理所必須用到的身堡。每一個動態(tài)代理類都必須要實現(xiàn)InvocationHandler這個接口邓尤,并且每個代理類的實例都關(guān)聯(lián)到了一個handler,當(dāng)我們通過代理對象調(diào)用一個方法的時候,這個方法的調(diào)用就會被轉(zhuǎn)發(fā)為由InvocationHandler這個接口的 invoke 方法來進(jìn)行調(diào)用
* @param proxy 所代理的真實對象
* the proxy instance on which the method was invoked
* @param method 所要調(diào)用真實對象的某個方法的Method對象
* the method invoked on the proxy instance
* @param args 調(diào)用真實對象某個方法時接受的參數(shù)
* an array of objects containing the parameters passed to the
* method, or {@code null} if no arguments are expected.
* Primitive types are boxed.
*
* @return the result of executing the method. Primitive types are boxed.
*
* @throws Throwable
* the exception to throw from the invoked method on the proxy.
* The exception must match one of the declared exception types
* of the invoked method or any unchecked exception type. If not
* then an {@code UndeclaredThrowableException} is thrown
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
再來看看Proxy類汞扎,它的作用是用來動態(tài)創(chuàng)建一個代理對象的類季稳,它提供了許多的方法,但是用的最多的就是 newProxyInstance 這個方法
* @param loader 一個ClassLoader對象澈魄,
定義了由哪個ClassLoader對象來對生成的代理對象進(jìn)行加載
* the class loader that will define the proxy class
* @param interfaces 一個Interface對象的數(shù)組景鼠,
表示的是將要給需要代理的對象提供一組什么接口,
如果提供了一組接口給它痹扇,
那么這個代理對象就宣稱實現(xiàn)了該接口(多態(tài))铛漓,
這樣就能調(diào)用這組接口中的方法了
* an array of {@code Class} objects, each one identifying an
* interface that will be implemented by the returned proxy
* object
* @param invocationHandler 一個InvocationHandler對象,
表示的是當(dāng)這個動態(tài)代理對象在調(diào)用方法的時候鲫构,
會關(guān)聯(lián)到哪一個InvocationHandler對象上
* the invocation handler that handles the dispatched method
* invocations
* @return a new proxy object that delegates to the handler {@code h}
* @throws IllegalArgumentException
* if any of the interface restrictions are violated
* @throws NullPointerException
* if the interfaces or any of its elements are null
*/
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler invocationHandler)
throws IllegalArgumentException {
之后看一個demo DynamicProxy20170607來看看動態(tài)代理如何使用浓恶。
最后結(jié)果:
“$ProxyN”是代理類名稱,由
打印出的结笨,其中 N 是一個逐一遞增的阿拉伯?dāng)?shù)字包晰,代表 Proxy 類第 N 次生成的動態(tài)代理類,但不是每次調(diào)用 Proxy 的靜態(tài)方法創(chuàng)建動態(tài)代理類都會使得 N 值增加禀梳,因為如果對同一組接口(包括接口排列的順序相同)試圖重復(fù)創(chuàng)建動態(tài)代理類杜窄,它會返回先前已經(jīng)創(chuàng)建好的代理類的類對象,而不會再嘗試去創(chuàng)建一個全新的代理類算途,這樣可以節(jié)省不必要的代碼重復(fù)生成塞耕,提高了代理類的創(chuàng)建效率。
通過 Proxy.newProxyInstance 創(chuàng)建的代理對象是在jvm運行時動態(tài)生成的一個對象嘴瓤,它并不是InvocationHandler類型扫外,也不是定義的那組接口的類型,而是在運行是動態(tài)生成的一個對象廓脆。
之后調(diào)用
通過代理對象來調(diào)用接口中的方法筛谚,程序就會跳轉(zhuǎn)到由這個代理對象關(guān)聯(lián)到的 handler 中的invoke方法去執(zhí)行,而這個 handler 對象又接受了一個 helloImpl類型的參數(shù)
表示要代理的就是這個helloImpl真實對象停忿,所以此時就會調(diào)用 handler(DynamicProxy) 中的invoke方法去執(zhí)行驾讲,可以看到DynamicProxy中的invoke不僅調(diào)用了helloImpl這個真實對象(helloImpl實現(xiàn)了Hello接口),還在前后添加了一些操作席赂,另外通過
可以看到具體調(diào)用了什么方法吮铭,
結(jié)果中before say hello,和after say hello都是添加的操作颅停,從Method:public abstract void DynamicProxy20170607.Hello.helloCat()可以看到谓晌,確實是調(diào)用的Hello接口helloCat的實現(xiàn)方法。
這也就證明了當(dāng)通過代理對象來調(diào)用方法的時候癞揉,起實際就是委托由其關(guān)聯(lián)到的 handler 對象的invoke方法中來調(diào)用纸肉,并不是自己來真實調(diào)用溺欧,而是通過代理的方式來調(diào)用的。