Java基础-JDK动态代理
JDK的動態(tài)代理依靠接口實(shí)現(xiàn)
?代理模式?
代理模式是常用的java設(shè)計(jì)模式,他的特征是代理類與委托類有同樣的接口,代理類主要負(fù)責(zé)為委托類預(yù)處理消息、過濾消息、把消息轉(zhuǎn)發(fā)給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關(guān)聯(lián)關(guān)系,一個(gè)代理類的對象與一個(gè)委托類的對象關(guān)聯(lián),代理類的對象本身并不真正實(shí)現(xiàn)服務(wù),而是通過調(diào)用委托類的對象的相關(guān)方法,來提供特定的服務(wù)。?
?
?
在java的動態(tài)代理機(jī)制中,有兩個(gè)重要的類或接口,一個(gè)是 InvocationHandler(Interface)、另一個(gè)則是 Proxy(Class),這一個(gè)類和接口是實(shí)現(xiàn)我們動態(tài)代理所必須用到的.
InvocationHandler
每一個(gè)動態(tài)代理類都必須要實(shí)現(xiàn)InvocationHandler這個(gè)接口,并且每個(gè)代理類的實(shí)例都關(guān)聯(lián)到了一個(gè)handler,當(dāng)我們通過代理對象調(diào)用一個(gè)方法的時(shí)候,這個(gè)方法的調(diào)用就會被轉(zhuǎn)發(fā)為由InvocationHandler這個(gè)接口的 invoke 方法來進(jìn)行調(diào)用。我們來看看InvocationHandler這個(gè)接口的唯一一個(gè)方法?invoke?方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable方法一共接受三個(gè)參數(shù) proxy: 指代我們所代理的那個(gè)真實(shí)對象 method: 指代的是我們所要調(diào)用真實(shí)對象的某個(gè)方法的Method對象 args: 指代的是調(diào)用真實(shí)對象某個(gè)方法時(shí)接受的參數(shù)?
?
?
Proxy
Proxy這個(gè)類的作用就是用來動態(tài)創(chuàng)建一個(gè)代理對象的類,它提供了許多的方法,但是我們用的最多的就是?newProxyInstance?這個(gè)方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException作用就是得到一個(gè)動態(tài)的代理對象,其接收三個(gè)參數(shù) loader: 一個(gè)ClassLoader對象,定義了由哪個(gè)ClassLoader對象來對生成的代理對象進(jìn)行加載 interfaces: 一個(gè)Interface對象的數(shù)組,表示的是我將要給我需要代理的對象提供一組什么接口,如果我提供了一組接口給它,那么這個(gè)代理對象就宣稱實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了 h: 一個(gè)InvocationHandler對象,表示的是當(dāng)我這個(gè)動態(tài)代理對象在調(diào)用方法的時(shí)候,會關(guān)聯(lián)到哪一個(gè)InvocationHandler對象上?
?
?
通過實(shí)例理解動態(tài)代理
首先定義一個(gè)類接口
public interface Subject {public void rent();public void hello(String str); }?
接著,定義了一個(gè)類來實(shí)現(xiàn)這個(gè)接口,這個(gè)類就是我們的真實(shí)對象:
public class RealSubject implements Subject {@Overridepublic void rent(){System.out.println("I want to rent my house");}@Overridepublic void hello(String str){System.out.println("hello: " + str);} }下一步,定義一個(gè)動態(tài)代理類了,前面說個(gè),每一個(gè)動態(tài)代理類都必須要實(shí)現(xiàn) InvocationHandler 這個(gè)接口,因此我們這個(gè)動態(tài)代理類也不例外:
public class DynamicProxy implements InvocationHandler {// 這個(gè)就是我們要代理的真實(shí)對象private Object subject;// 構(gòu)造方法,給我們要代理的真實(shí)對象賦初值public DynamicProxy(Object subject){this.subject = subject;}@Overridepublic Object invoke(Object object, Method method, Object[] args)throws Throwable{//在代理真實(shí)對象前我們可以添加一些自己的操作System.out.println("before rent house");System.out.println("Method:" + method);//當(dāng)代理對象調(diào)用真實(shí)對象的方法時(shí),其會自動的跳轉(zhuǎn)到代理對象關(guān)聯(lián)的handler對象的invoke方法來進(jìn)行調(diào)用 method.invoke(subject, args);//在代理真實(shí)對象后我們也可以添加一些自己的操作System.out.println("after rent house");return null;}}?
最后,來看看我們的Client類:
public class Client {public static void main(String[] args){// 我們要代理的真實(shí)對象Subject realSubject = new RealSubject();// 我們要代理哪個(gè)真實(shí)對象,就將該對象傳進(jìn)去,最后是通過該真實(shí)對象來調(diào)用其方法的InvocationHandler handler = new DynamicProxy(realSubject);/** 通過Proxy的newProxyInstance方法來創(chuàng)建我們的代理對象,我們來看看其三個(gè)參數(shù)* 第一個(gè)參數(shù) handler.getClass().getClassLoader() ,我們這里使用handler這個(gè)類的ClassLoader對象來加載我們的代理對象* 第二個(gè)參數(shù)realSubject.getClass().getInterfaces(),我們這里為代理對象提供的接口是真實(shí)對象所實(shí)行的接口,表示我要代理的是該真實(shí)對象,這樣我就能調(diào)用這組接口中的方法了* 第三個(gè)參數(shù)handler, 我們這里將這個(gè)代理對象關(guān)聯(lián)到了上方的 InvocationHandler 這個(gè)對象上*/Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);System.out.println(subject.getClass().getName());subject.rent();subject.hello("world");} }控制臺的輸出:
$Proxy0before rent house Method:public abstract void com.xiaoluo.dynamicproxy.Subject.rent() I want to rent my house after rent housebefore rent house Method:public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String) hello: world after rent house?
我們首先來看看?$Proxy0 這東西,我們看到,這個(gè)東西是由
System.out.println(subject.getClass().getName());?
這條語句打印出來的,那么為什么我們返回的這個(gè)代理對象的類名是這樣的呢?
Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);可能我以為返回的這個(gè)代理對象會是Subject類型的對象,或者是InvocationHandler的對象,結(jié)果卻不是,首先我們解釋一下為什么我們這里可以將其轉(zhuǎn)化為Subject類型的對象?原因就是在newProxyInstance這個(gè)方法的第二個(gè)參數(shù)上,我們給這個(gè)代理對象提供了一組什么接口,那么我這個(gè)代理對象就會實(shí)現(xiàn)了這組接口,這個(gè)時(shí)候我們當(dāng)然可以將這個(gè)代理對象強(qiáng)制類型轉(zhuǎn)化為這組接口中的任意一個(gè),因?yàn)檫@里的接口是Subject類型,所以就可以將其轉(zhuǎn)化為Subject類型了。
同時(shí)我們一定要記住,通過?Proxy.newProxyInstance 創(chuàng)建的代理對象是在jvm運(yùn)行時(shí)動態(tài)生成的一個(gè)對象,它并不是我們的InvocationHandler類型,也不是我們定義的那組接口的類型,而是在運(yùn)行是動態(tài)生成的一個(gè)對象,并且命名方式都是這樣的形式,以$開頭,proxy為中,最后一個(gè)數(shù)字表示對象的標(biāo)號。
接著我們來看看這兩句?
subject.rent(); subject.hello("world");?
這里是通過代理對象來調(diào)用實(shí)現(xiàn)的那種接口中的方法,這個(gè)時(shí)候程序就會跳轉(zhuǎn)到由這個(gè)代理對象關(guān)聯(lián)到的 handler 中的invoke方法去執(zhí)行,而我們的這個(gè) handler 對象又接受了一個(gè) RealSubject類型的參數(shù),表示我要代理的就是這個(gè)真實(shí)對象,所以此時(shí)就會調(diào)用 handler 中的invoke方法去執(zhí)行:
public Object invoke(Object object, Method method, Object[] args)throws Throwable{// 在代理真實(shí)對象前我們可以添加一些自己的操作System.out.println("before rent house");System.out.println("Method:" + method);// 當(dāng)代理對象調(diào)用真實(shí)對象的方法時(shí),其會自動的跳轉(zhuǎn)到代理對象關(guān)聯(lián)的handler對象的invoke方法來進(jìn)行調(diào)用 method.invoke(subject, args);// 在代理真實(shí)對象后我們也可以添加一些自己的操作System.out.println("after rent house");return null;}我們看到,在真正通過代理對象來調(diào)用真實(shí)對象的方法的時(shí)候,我們可以在該方法前后添加自己的一些操作,同時(shí)我們看到我們的這個(gè) method 對象是這樣的:
public abstract void com.xiaoluo.dynamicproxy.Subject.rent()public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)正好就是我們的Subject接口中的兩個(gè)方法,這也就證明了當(dāng)我通過代理對象來調(diào)用方法的時(shí)候,起實(shí)際就是委托由其關(guān)聯(lián)到的 handler 對象的invoke方法中來調(diào)用,并不是自己來真實(shí)調(diào)用,而是通過代理的方式來調(diào)用的。
這就是我們的java動態(tài)代理機(jī)制
?
?
?
?
?
?
?
?
?
?
?
所謂Dynamic Proxy是這樣一種class:它是在運(yùn)行時(shí)生成的class,在生成它時(shí)你必須提供一 組interface給它,然后該class就宣稱它實(shí)現(xiàn)了這些 interface。你當(dāng)然可以把該class的實(shí) 例當(dāng)作這些interface中的任何一個(gè)來用。當(dāng)然啦,這個(gè)Dynamic Proxy其實(shí)就是一個(gè)Proxy, 它不會替你作實(shí)質(zhì)性的工作,在生成它的實(shí)例時(shí)你必須提供一個(gè)handler,由它接管實(shí)際的工 作。
?
java動態(tài)代理類位于java.lang.reflect包下,一般主要涉及到以下兩個(gè)類:
Interface InvocationHandler該接口中僅定義了一個(gè)方法Object:invoke(Object obj,Method method, Object[] args)。在實(shí)際使用時(shí),第一個(gè)參數(shù)obj一般是指代理 類,method是被代理的方法,如上例中的request(),args為該方法的參數(shù)數(shù)組。 這個(gè)抽 象方法在代理類中動態(tài)實(shí)現(xiàn)。
Proxy
該類即為動態(tài)代理類,作用類似于上例中的ProxySubject。
Protected Proxy(InvocationHandler h)
構(gòu)造函數(shù),估計(jì)用于給內(nèi)部的h賦值。
Static Class getProxyClass (ClassLoader loader, Class[] interfaces)
獲得一個(gè) 代理類,其中l(wèi)oader是類裝載器,interfaces是真實(shí)類所擁有的全部接口的數(shù)組。
Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
返回代理類的一個(gè)實(shí)例,返回后的代理類可以當(dāng)作被代理類使用 (可使用被代理類的在Subject接口中聲明過的方法)。
?
在使用動態(tài)代理類時(shí),我們必須實(shí)現(xiàn)InvocationHandler接口,以第一節(jié)中的示例為例:
// 抽象角色(之前是抽象類,此處應(yīng)改為接口): public interface Subject {abstract public void request(); }// 具體角色RealSubject: public class RealSubject implements Subject {public RealSubject() {}public void request() {System.out.println( " From real subject. " );}}// 代理處理器: import java.lang.reflect.Method; import java.lang.reflect.InvocationHandler;public class DynamicSubject implements InvocationHandler {private Object sub;public DynamicSubject() {}public DynamicSubject(Object obj) {sub = obj;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println( " before calling " + method);method.invoke(sub,args);System.out.println( " after calling " + method);return null ;} }?
?
該代理類的內(nèi)部屬性為Object類,實(shí)際使用時(shí)通過該類的構(gòu)造函數(shù)DynamicSubject(Object obj)對其賦值;此外,在該類還實(shí)現(xiàn)了invoke方法,該方法中的
method.invoke(sub,args);?
其實(shí)就是調(diào)用被代理對象的將要被執(zhí)行的方法,方法參數(shù)sub是實(shí)際的被代理對象,args為執(zhí) 行被代理對象相應(yīng)操作所需的參數(shù)。通過動態(tài)代理類,我們可以在調(diào)用之前或之后執(zhí)行一些 相關(guān)操作。
// 客戶端: import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.lang.reflect.Constructor; import java.lang.reflect.Method;public class Client {static public void main(String[] args) throws Throwable {RealSubject rs = new RealSubject(); // 在這里指定被代理類InvocationHandler ds = new DynamicSubject(rs);Class cls = rs.getClass();// 以下是一次性生成代理Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),ds );subject.request();} }// 程序運(yùn)行結(jié)果: before calling public abstract void Subject.request() From real subject. after calling public abstract void Subject.request()?
通過這種方式,被代理的對象(RealSubject)可以在運(yùn)行時(shí)動態(tài)改變,需要控制的接口 (Subject接口)可以在運(yùn)行時(shí)改變,控制的方式(DynamicSubject類)也可以動態(tài)改變,從而實(shí) 現(xiàn)了非常靈活的動態(tài)代理關(guān)系。
?
轉(zhuǎn)載于:https://www.cnblogs.com/hwaggLee/p/4499188.html
總結(jié)
以上是生活随笔為你收集整理的Java基础-JDK动态代理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 代理服务器工作原理是什么?
- 下一篇: 自己看不见进步