代理模式是常用的Java 設(shè)計(jì)模式,它的特征是代理類與委托類有同樣的接口,代理類主要負(fù)責(zé)為委托類預(yù)處理消息、過濾消息、把消息轉(zhuǎn)發(fā)給委托類,以及事后處理消息等。代理類與委托類之間通常會(huì)存在關(guān)聯(lián)關(guān)系,一個(gè)代理類的對象與一個(gè)委托類的對象關(guān)聯(lián),代理類的對象本身并不真正實(shí)現(xiàn)服務(wù),而是通過調(diào)用委托類的對象的相關(guān)方法,來提供特定的服務(wù)。
write log after invoke
隨著Proxy的流行,Sun把它納入到JDK1.3實(shí)現(xiàn)了Java的動(dòng)態(tài)代理。動(dòng)態(tài)代理和普通的代理模式的區(qū)別,就是動(dòng)態(tài)代理中的代理類是由 java.lang.reflect.Proxy類在運(yùn)行期時(shí)根據(jù)接口定義,采用Java反射功能動(dòng)態(tài)生成的。和 java.lang.reflect.InvocationHandler結(jié)合,可以加強(qiáng)現(xiàn)有類的方法實(shí)現(xiàn)。如圖2,圖中的自定義Handler實(shí)現(xiàn) InvocationHandler接口,自定義Handler實(shí)例化時(shí),將實(shí)現(xiàn)類傳入自定義Handler對象。自定義Handler需要實(shí)現(xiàn) invoke方法,該方法可以使用Java反射調(diào)用實(shí)現(xiàn)類的實(shí)現(xiàn)的方法,同時(shí)當(dāng)然可以實(shí)現(xiàn)其他功能,例如在調(diào)用實(shí)現(xiàn)類方法前后加入Log。而Proxy類根據(jù)Handler和需要代理的接口動(dòng)態(tài)生成一個(gè)接口實(shí)現(xiàn)類的對象。當(dāng)用戶調(diào)用這個(gè)動(dòng)態(tài)生成的實(shí)現(xiàn)類時(shí),實(shí)際上是調(diào)用了自定義Handler的 invoke方法。
Proxy類提供了創(chuàng)建動(dòng)態(tài)代理類及其實(shí)例的靜態(tài)方法。
(1)getProxyClass()靜態(tài)方法負(fù)責(zé)創(chuàng)建動(dòng)態(tài)代理類,它的完整定義如下:
public static Class<?> getProxyClass(ClassLoader loader, Class<?>[] interfaces) throws IllegalArgumentException
參數(shù)loader 指定動(dòng)態(tài)代理類的類加載器,參數(shù)interfaces 指定動(dòng)態(tài)代理類需要實(shí)現(xiàn)的所有接口。
(2)newProxyInstance()靜態(tài)方法負(fù)責(zé)創(chuàng)建動(dòng)態(tài)代理類的實(shí)例,它的完整定義如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) throws
IllegalArgumentException
參數(shù)loader 指定動(dòng)態(tài)代理類的類加載器,參數(shù)interfaces 指定動(dòng)態(tài)代理類需要實(shí)現(xiàn)的所有接口,參數(shù)handler 指定與動(dòng)態(tài)代理類關(guān)聯(lián)的 InvocationHandler 對象。
以下兩種方式都創(chuàng)建了實(shí)現(xiàn)Foo接口的動(dòng)態(tài)代理類的實(shí)例:
/**** 方式一 ****/
//創(chuàng)建InvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(...);
//創(chuàng)建動(dòng)態(tài)代理類
Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });
//創(chuàng)建動(dòng)態(tài)代理類的實(shí)例
Foo foo = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler });
/**** 方式二 ****/
//創(chuàng)建InvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(...);
//直接創(chuàng)建動(dòng)態(tài)代理類的實(shí)例
Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[] { Foo.class }, handler);
由Proxy類的靜態(tài)方法創(chuàng)建的動(dòng)態(tài)代理類具有以下特點(diǎn):
動(dòng)態(tài)代理類是public、final和非抽象類型的;
動(dòng)態(tài)代理類繼承了java.lang.reflect.Proxy類;
動(dòng)態(tài)代理類的名字以“$Proxy”開頭;
動(dòng)態(tài)代理類實(shí)現(xiàn)getProxyClass()和newProxyInstance()方法中參數(shù)interfaces指定的所有接口;
Proxy 類的isProxyClass(Class<?> cl)靜態(tài)方法可用來判斷參數(shù)指定的類是否為動(dòng)態(tài)代理類。只有通過Proxy類創(chuàng)建的類才是動(dòng)態(tài)代理類;
動(dòng)態(tài)代理類都具有一個(gè)public 類型的構(gòu)造方法,該構(gòu)造方法有一個(gè)InvocationHandler 類型的參數(shù)。
由Proxy類的靜態(tài)方法創(chuàng)建的動(dòng)態(tài)代理類的實(shí)例具有以下特點(diǎn):
1. 假定變量foo 是一個(gè)動(dòng)態(tài)代理類的實(shí)例,并且這個(gè)動(dòng)態(tài)代理類實(shí)現(xiàn)了Foo 接口,那么“foo instanceof Foo”的值為true。把變量foo強(qiáng)制轉(zhuǎn)換為Foo類型是合法的:
(Foo) foo //合法
2.每個(gè)動(dòng)態(tài)代理類實(shí)例都和一個(gè)InvocationHandler 實(shí)例關(guān)聯(lián)。Proxy 類的getInvocationHandler(Object proxy)靜態(tài)方法返回與參數(shù)proxy指定的代理類實(shí)例所關(guān)聯(lián)的InvocationHandler 對象。
3.假定Foo接口有一個(gè)amethod()方法,那么當(dāng)程序調(diào)用動(dòng)態(tài)代理類實(shí)例foo的amethod()方法時(shí),該方法會(huì)調(diào)用與它關(guān)聯(lián)的InvocationHandler 對象的invoke()方法。
InvocationHandler 接口為方法調(diào)用接口,它聲明了負(fù)責(zé)調(diào)用任意一個(gè)方法的invoke()方法:
Object invoke(Object proxy,Method method,Object[] args) throws Throwable
參數(shù)proxy指定動(dòng)態(tài)代理類實(shí)例,參數(shù)method指定被調(diào)用的方法,參數(shù)args 指定向被調(diào)用方法傳遞的參數(shù),invoke()方法的返回值表示被調(diào)用方法的返回值。
最后看一個(gè)例子,該例子模仿spring 的AOP原理。
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; class Logic{ public void logic(){ Dao dao = Factory.create(); System.out.println("dynamic proxy's name: "+dao.getClass().getName()); dao.insert(); } } class Factory{ static Dao create(){ Dao dao = new JdbcDao(); MyInvocationHandler hand = new MyInvocationHandler(); return (Dao)hand.get(dao); } } interface Dao{ public void update(); public void insert(); } class JdbcDao implements Dao{ public void update(){ System.out.println("in jdbc update"); } public void insert(){ System.out.println("in jdbc insert"); } } class HibernateDao implements Dao{ public void update(){ System.out.println("in hibernate update"); } public void insert(){ System.out.println("in hibernate insert"); } } class MyInvocationHandler implements InvocationHandler { Object o; public Object get(Object o){ System.out.println("in get method of MyInvocationHandler"); this.o = o; return Proxy.newProxyInstance(o.getClass().getClassLoader(),o.getClass().getInterfaces(),this); } public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable { System.out.println("write log before invoke"); Object result = arg1.invoke(o, arg2); System.out.println("write log after invoke"); return result; } } public class Test { public static void main(String[] args) { Logic l = new Logic(); l.logic(); } } |
運(yùn)行結(jié)果:
in get method of MyInvocationHandler
dynamic proxy's name: proxy.$Proxy0
write log before invoke
in jdbc insert
write log after invoke
結(jié)論: JDK的動(dòng)態(tài)代理并不能隨心所欲的代理所有的類。Proxy.newProxyInstance方法的第二個(gè)參數(shù)只能是接口數(shù)組, 也就是Proxy只能代理接口。