Java動態(tài)代理

????????Spring的核心AOP的原理就是java的動態(tài)代理機制。所以,我們需要好好將java動態(tài)代理機制進行梳理。

? ? ? ? 在java的動態(tài)代理機制中,有兩個重要的類或接口,一個是InvocationHandler(Interface)、另一個是Proxy(Class),這一個類和接口是實現(xiàn)我們動態(tài)代理所必須用到的。首先我們先來看看java的API幫助文檔是怎么樣對這兩個類進行描述的:????

???????1、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.

? ? ? ? 每一個動態(tài)代理類都必須要實現(xiàn)InvocationHandler這個接口,并且每個代理類的實例都關聯(lián)到了一個handler,當我們通過代理對象調(diào)用一個方法的時候,這個方法的調(diào)用就會被轉(zhuǎn)發(fā)為由InvocationHandler這個接口的invoke 方法來進行調(diào)用。我們開看看InvocationHandler這個接口的唯一一個invoke方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

????????三個參數(shù)分別代表的意思:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

proxy:指代我們所代理的那個真實對象。

method:指代的是我們所要調(diào)用真實對象的某個方法的Method對象。

args:指代的是調(diào)用真實對象某個方法時接受的參數(shù)。

? ? ? ? ?2、Proxy:

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這個類的作用就是用來動態(tài)創(chuàng)建一個代理對象,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance?這個方法:

public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)throws IllegalArgumentException

????????這個方法的作用就是得到一個動態(tài)的代理對象,其接收三個參數(shù),我們來看看這三個參數(shù)所代表的含義:

public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)throwsIllegalArgumentException

loader:一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象進行加載。

interfaces:一個Interface對象的數(shù)組,表示的是我將要給我需要代理的對象提供一組什么接口,如果我提供了一組接口給它,那么這個代理對象就宣稱實現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了。

h:一個InvocationHandler對象,表示的是當我這個動態(tài)代理對象在調(diào)用方法的時候,會關聯(lián)到哪一個InvocationHandler對象上。

? ??????3、實例

????????首先我們定義了一個Subject類型的接口,為其聲明了兩個方法:

public interface Subject{

? ? ? ? ?publicvoid rent();

? ? ? ? ?publicvoid hello(String str);

}

????????接著,定義了一個類來實現(xiàn)這個接口,這個類就是我們的真實對象,RealSubject類:

public? class? RealSubject? implements Subject{

? ????????? @Override

? ? ????????public void rent(){

? ? ? ????????????? System.out.println("I want to rent my house");

? ????????? }

? ????????? @Override

? ? ????????public????void hello(String str){

? ? ? ? ????????????System.out.println("hello: " + str);

????????? ? }

}

????????下一步,我們就要定義一個動態(tài)代理類了,前面說過,每一個動態(tài)代理類都必須要實現(xiàn) InvocationHandler 這個接口,因此我們這個動態(tài)代理類也不例外:

public class? DynamicProxy? implements InvocationHandler{

? ????? // 這個就是我們要代理的真實對象

????????private Object subject;

? ? ????//? ? 構(gòu)造方法,給我們要代理的真實對象賦初值

????????public DynamicProxy(Object subject) {

? ? ? ????????? this.subject = subject;

? ? ? ? ?}

? ? ? ? ?@Override

? ? ????public Object invoke(Object object, Method method, Object[] args) throws Throwable {

? ? ? ????? //在代理真實對象前我們可以添加一些自己的操作

? ? ? ?????System.out.println("before rent house");

? ? ? ????? System.out.println("Method:" + method);

????????????//當代理對象調(diào)用真實對象的方法時,其會自動的跳轉(zhuǎn)到代理對象關聯(lián)的handler對象的invoke方法來進行調(diào)用? ? ? ? ????????????method.invoke(subject, args);

? ? ? ????? //在代理真實對象后我們也可以添加一些自己的操作

????????????System.out.println("after rent house");

? ? ????? ?return? null;

? ? ? ?}

}

????????最后,來看看我們的Client類:

public? class Client{

? ? public static void main(String[] args)

? ? {

? ? ? ? //我們要代理的真實對象

????????Subject realSubject =new RealSubject();

? ? ? ? //我們要代理哪個真實對象,就將該對象傳進去,最后是通過該真實對象來調(diào)用其方法的

? ? ? ?InvocationHandler handler =new DynamicProxy(realSubject);

? ? ? ? /** 通過Proxy的newProxyInstance方法來創(chuàng)建我們的代理對象,我們來看看其三個參數(shù)

? ? ? ? * 第一個參數(shù) handler.getClass().getClassLoader() ,我們這里使用handler這個類的ClassLoader對象來加載我們的代理對象

? ? ? ? * 第二個參數(shù)realSubject.getClass().getInterfaces(),我們這里為代理對象提供的接口是真實對象所實現(xiàn)的表示我要代理的是該真實對象,這樣我就能調(diào)用這組接口中的方法了

? ? ? * 第三個參數(shù)handler, 我們這里將這個代理對象關聯(lián)到了上方的 InvocationHandler 這個對象上

? ? ? ? */? ? ? ??

????????Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject

? ? ? ? ? ? ? ? .getClass().getInterfaces(), handler);

? ? ? ? System.out.println(subject.getClass().getName());

? ? ? ? subject.rent();

? ? ? ? subject.hello("world");

? ? }

}

我們先來看看控制臺的輸出:

$Proxy0

before rent houseMethod:public abstract void com.xiaoluo.dynamicproxy.Subject.rent()

I want to rent my houseafter rent house

before rent houseMethod:?public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)

hello: worldafter rent house

????????我們首先來看看?$Proxy0 這東西,我們看到,這個東西是由?System.out.println(subject.getClass().getName()); 這條語句打印出來的,那么為什么我們返回的這個代理對象的類名是這樣的呢?

Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject

? ? ? ? ? ? ? ? .getClass().getInterfaces(), handler);

????????可能我以為返回的這個代理對象會是Subject類型的對象,或者是InvocationHandler的對象,結(jié)果卻不是,首先我們解釋一下為什么我們這里可以將其轉(zhuǎn)化為Subject類型的對象?原因就是在newProxyInstance這個方法的第二個參數(shù)上,我們給這個代理對象提供了一組什么接口,那么我這個代理對象就會實現(xiàn)了這組接口,這個時候我們當然可以將這個代理對象強制類型轉(zhuǎn)化為這組接口中的任意一個,因為這里的接口是Subject類型,所以就可以將其轉(zhuǎn)化為Subject類型了。

? ??????同時我們一定要記住,通過?Proxy.newProxyInstance 創(chuàng)建的代理對象是在jvm運行時動態(tài)生成的一個對象,它并不是我們的InvocationHandler類型,也不是我們定義的那組接口的類型,而是在運行是動態(tài)生成的一個對象,并且命名方式都是這樣的形式,以$開頭,proxy為中,最后一個數(shù)字表示對象的標號。

????????接著我們來看看這兩句?

????????subject.rent();

????????subject.hello("world");

????????這里是通過代理對象來調(diào)用實現(xiàn)的那個接口中的方法,這個時候程序就會跳轉(zhuǎn)到由這個代理對象關聯(lián)到的 handler 中的invoke方法去執(zhí)行,而我們的這個 handler 對象又接受了一個 RealSubject類型的參數(shù),表示我要代理的就是這個真實對象,所以此時就會調(diào)用 handler 中的invoke方法去執(zhí)行:

public Object invoke(Object object, Method method, Object[] args) throws Throwable {

? ? ? ? //在代理真實對象前我們可以添加一些自己的操作System.out.println("before rent house");

? ? ? ? System.out.println("Method:" + method);

????????//當代理對象調(diào)用真實對象的方法時,其會自動的跳轉(zhuǎn)到代理對象關聯(lián)的handler對象的invoke方法來進行調(diào)用? ? ? ??

????????method.invoke(subject, args);

? ? ? ? //在代理真實對象后我們也可以添加一些自己的操作System.out.println("after rent house");

????????return null;

? ? }

????????我們看到,在真正通過代理對象來調(diào)用真實對象的方法的時候,我們可以在該方法前后添加自己的一些操作,同時我們看到我們的這個 method 對象是這樣的:

public abstract void com.xiaoluo.dynamic proxy.Subject.rent()

public abstract void com.xiaoluo.dynamic proxy.Subject.hello(java.lang.String)

????????正好就是我們的Subject接口中的兩個方法,這也就證明了當我通過代理對象來調(diào)用方法的時候,起實際就是委托由其關聯(lián)到的 handler 對象的invoke方法中來調(diào)用,并不是自己來真實調(diào)用,而是通過代理的方式來調(diào)用的。

????????這就是我們的java動態(tài)代理機制。

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

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

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