要想了解Java動(dòng)態(tài)代理,首先要了解什么叫做代理,熟悉設(shè)計(jì)模式的朋友一定知道在Gof總結(jié)的23種設(shè)計(jì)模式中,有一種叫做代理(Proxy)的對(duì)象結(jié)構(gòu)型模式,動(dòng)態(tài)代理中的代理,指的就是這種設(shè)計(jì)模式。
在我看來(lái)所謂的代理模式,和23種設(shè)計(jì)模式中的“裝飾模式”是一個(gè)東西。23種設(shè)計(jì)模式中將它們作為兩種模式,網(wǎng)上也有些文章講這兩種模式的異同,從細(xì)節(jié)來(lái)看,確實(shí)可以人為地區(qū)分這兩種模式,但是抽象到一定高度后,我認(rèn)為這兩種模式是完全一樣的。因此學(xué)會(huì)了代理模式,也就同時(shí)掌握了裝飾模式。
代理模式
代理模式簡(jiǎn)單來(lái)說(shuō),就是對(duì)一個(gè)對(duì)象進(jìn)行包裝,包裝后生成的對(duì)象具有和原對(duì)象一樣的方法列表,但是每個(gè)方法都可以是被包裝過(guò)的。
靜態(tài)代理
讓我們先來(lái)看一段代碼:
package common;public class Test { static interface Subject{ void sayHi(); void sayHello(); } static class SubjectImpl implements Subject{ @Override public void sayHi() { System.out.println("hi"); } @Override public void sayHello() { System.out.println("hello"); } } static class SubjectImplProxy implements Subject{ private Subject target; public SubjectImplProxy(Subject target) { this.target=target; } @Override public void sayHi() { System.out.print("say:"); target.sayHi(); } @Override public void sayHello() { System.out.print("say:"); target.sayHello(); } } public static void main(String[] args) { Subject subject=new SubjectImpl(); Subject subjectProxy=new SubjectImplProxy(subject); subjectProxy.sayHi(); subjectProxy.sayHello(); } }
這段代碼中首先定義了一個(gè)Subject接口,接口中有兩個(gè)方法。
然后定義了SubjectImpl類實(shí)現(xiàn)Subject接口并實(shí)現(xiàn)其中的兩個(gè)方法,到這里肯定是沒(méi)問(wèn)題的。
現(xiàn)在再定義一個(gè)SubjuectImplProxy類,也實(shí)現(xiàn)Subject接口。這個(gè)SubjectImplProxy類的作用是包裝SubjectImpl類的實(shí)例,它的內(nèi)部定義一個(gè)變量target來(lái)保存一個(gè)SubjectImpl的實(shí)例。SubjectImplProxy也實(shí)現(xiàn)了接口規(guī)定的兩個(gè)方法,并且在它的實(shí)現(xiàn)版本中,都調(diào)用了SubjectImpl的實(shí)現(xiàn),但是又添加了自己的處理邏輯。
相信這段代碼不難理解,它通過(guò)對(duì)SubjectImpl進(jìn)行包裝,達(dá)到了給輸出內(nèi)容添加前綴的功能。這種代理方式叫做靜態(tài)代理。
動(dòng)態(tài)代理
從上面的演示中我們不難看出靜態(tài)代理的缺點(diǎn):我們對(duì)SubjectImpl的兩個(gè)方法,是進(jìn)行的相同的包裝,但是卻要在SubjectImplProxy里把相同的包裝邏輯寫兩次,而且以后如果Subject接口再添加新的方法,SubjectImplProxy也必須要添加新的實(shí)現(xiàn),盡管SubjectImplProxy對(duì)所有方法的包裝可能都是一樣的。
下面我把上面例子的靜態(tài)代理改成動(dòng)態(tài)代理,我們來(lái)看一下區(qū)別:
package common;import java.lang.invoke.MethodHandle;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class Test { static interface Subject{ void sayHi(); void sayHello(); } static class SubjectImpl implements Subject{ @Override public void sayHi() { System.out.println("hi"); } @Override public void sayHello() { System.out.println("hello"); } } static class ProxyInvocationHandler implements InvocationHandler{ private Subject target; public ProxyInvocationHandler(Subject target) { this.target=target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.print("say:"); return method.invoke(target, args); } } public static void main(String[] args) { Subject subject=new SubjectImpl(); Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject)); subjectProxy.sayHi(); subjectProxy.sayHello(); } }
只看main方法的話,只有第二行和之前的靜態(tài)代理不同,同樣是生成一個(gè)subjectProxy代理對(duì)象,只是生成的代碼不同了。靜態(tài)代理是直接new 一個(gè)SubjectImplProxy的實(shí)例,而動(dòng)態(tài)代理則調(diào)用了java.lang.reflect.Proxy.newProxyInstance()方法,我們來(lái)看一下這個(gè)方法的源碼:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null) { throw new NullPointerException(); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass(loader, interfaces); //獲取代理類的Class /* * Invoke its constructor with the designated invocation handler. */ try { Constructor cons = cl.getConstructor(constructorParams); //constructorParams是寫死的:{ InvocationHandler.class },上邊返回的代理類Class一定是extends Proxy的,而Proxy有一個(gè)參數(shù)為InvocationHandler的構(gòu)造函數(shù) return cons.newInstance(new Object[] { h }); //這里通過(guò)構(gòu)造函數(shù)將我們自己定義的InvocationHandler的子類傳到代理類的實(shí)例里,當(dāng)我們調(diào)用代理類的任何方法時(shí),實(shí)際上都會(huì)調(diào)用我們定義的InvocationHandler子類重寫的invoke()函數(shù) } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { throw new InternalError(e.toString()); } }
上面的 Class<?> cl = getProxyClass(loader, interfaces); 調(diào)用的getProxyClass方法:
Class<?><?> (interfaces.length > 65535 IllegalArgumentException("interface limit exceeded"<?> proxyClass = = Set<Class<?>> interfaceSet = HashSet<> ( i = 0; i < interfaces.length; i++=<?> interfaceClass = = Class.forName(interfaceName, (interfaceClass != + " is not visible from class loader" (! + " is not an interface" "repeated interface: " +=<String> key =<List<String>, Object>= (cache == = HashMap<> = (value = (Class<?> (proxyClass != (value ==