本文連接:https://blog.csdn.net/zcc_0015/article/details/22695647
1、動態代理與靜態代理的區別。
(1)Proxy類的代碼被固定下來,不會由於業務的逐漸龐大而龐大;
(2)能夠實現AOP編程,這是靜態代理沒法實現的;
(3)解耦,若是用在web業務下,能夠實現數據層和業務層的分離。
(4)動態代理的優點就是實現無侵入式的代碼擴展。
靜態代理這個模式自己有個大問題,若是類方法數量愈來愈多的時候,代理類的代碼量是十分龐大的。因此引入動態代理來解決此類問題
2、動態代理java
Java中動態代理的實現,關鍵就是這兩個東西:Proxy、InvocationHandler,下面從InvocationHandler接口中的invoke方法入手,簡單說明一下Java如何實現動態代理的。
首先,invoke方法的完整形式以下: 程序員
Java代碼 web
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { method.invoke(obj, args); return null; }
首先猜想一下,method是調用的方法,即須要執行的方法;args是方法的參數;proxy,這個參數是什麼?以上invoke()方法的實現便是比較標準的形式,咱們看到,這裏並無用到proxy參數。查看JDK文檔中Proxy的說明,以下:
Java代碼
A method invocation on a proxy instance through one of its proxy interfaces will be dispatched to the invoke method of the instance's invocation handler, passing the proxy instance,a java.lang.reflect.Method object identifying the method that was invoked, and an array of type Object containing the arguments. 編程
由此能夠知道以上的猜想是正確的,同時也知道,proxy參數傳遞的便是代理類的實例。 ide
爲了方便說明,這裏寫一個簡單的例子來實現動態代理。 this
Java代碼 spa
//抽象角色(動態代理只能代理接口) public interface Subject { public void request(); } Java代碼 //真實角色:實現了Subject的request()方法 public class RealSubject implements Subject{ public void request(){ System.out.println("From real subject."); } } Java代碼 //實現了InvocationHandler public class DynamicSubject implements InvocationHandler { private Object obj;//這是動態代理的好處,被封裝的對象是Object類型,接受任意類型的對象 public DynamicSubject() { } public DynamicSubject(Object obj) { this.obj = obj; } //這個方法不是咱們顯示的去調用 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before calling " + method); method.invoke(obj, args); System.out.println("after calling " + method); return null; } } Java代碼 //客戶端:生成代理實例,並調用了request()方法 public class Client { public static void main(String[] args) throws Throwable{ // TODO Auto-generated method stub Subject rs=new RealSubject();//這裏指定被代理類 InvocationHandler ds=new DynamicSubject(rs); Class<?> cls=rs.getClass(); //如下是一次性生成代理 Subject subject=(Subject) Proxy.newProxyInstance( cls.getClassLoader(),cls.getInterfaces(), ds); //這裏能夠經過運行結果證實subject是Proxy的一個實例,這個實例實現了Subject接口 System.out.println(subject instanceof Proxy); //這裏能夠看出subject的Class類是$Proxy0,這個$Proxy0類繼承了Proxy,實現了Subject接口 System.out.println("subject的Class類是:"+subject.getClass().toString()); System.out.print("subject中的屬性有:"); Field[] field=subject.getClass().getDeclaredFields(); for(Field f:field){ System.out.print(f.getName()+", "); } System.out.print("\n"+"subject中的方法有:"); Method[] method=subject.getClass().getDeclaredMethods(); for(Method m:method){ System.out.print(m.getName()+", "); } System.out.println("\n"+"subject的父類是:"+subject.getClass().getSuperclass()); System.out.print("\n"+"subject實現的接口是:"); Class<?>[] interfaces=subject.getClass().getInterfaces(); for(Class<?> i:interfaces){ System.out.print(i.getName()+", "); } System.out.println("\n\n"+"運行結果爲:"); subject.request(); } } Xml代碼 運行結果以下:此處省略了包名,***代替 true subject的Class類是:class $Proxy0 subject中的屬性有:m1, m3, m0, m2, subject中的方法有:request, hashCode, equals, toString, subject的父類是:class java.lang.reflect.Proxy subject實現的接口是:cn.edu.ustc.dynamicproxy.Subject, 運行結果爲: before calling public abstract void ***.Subject.request() From real subject. after calling public abstract void ***.Subject.request()
PS:這個結果的信息很是重要,至少對我來講。由於我在動態代理犯暈的根源就在於將上面的subject.request()理解錯了,至少是被表面所迷惑,沒有發現這個subject和Proxy之間的聯繫,一度糾結於最後調用的這個request()是怎麼和invoke()聯繫上的,而invoke又是怎麼知道request存在的。其實上面的true和class $Proxy0就能解決不少的疑問,再加上下面將要說的$Proxy0的源碼,徹底能夠解決動態代理的疑惑了。 .net
從以上代碼和結果能夠看出,咱們並無顯示的調用invoke()方法,可是這個方法確實執行了。下面就整個的過程進行分析一下: 代理
從Client中的代碼看,能夠從newProxyInstance這個方法做爲突破口,咱們先來看一下Proxy類中newProxyInstance方法的源代碼:
Java代碼 code
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); /* * Invoke its constructor with the designated invocation handler. */ try { /* * Proxy源碼開始有這樣的定義: * private final static Class[] constructorParams = { InvocationHandler.class }; * cons便是形參爲InvocationHandler類型的構造方法 */ Constructor cons = cl.getConstructor(constructorParams); return (Object) cons.newInstance(new Object[] { h }); } 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()); } }
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)作了如下幾件事.
(1)根據參數loader和interfaces調用方法 getProxyClass(loader, interfaces)建立代理類$Proxy0.$Proxy0類 實現了interfaces的接口,並繼承了Proxy類.
(2)實例化$Proxy0並在構造方法中把DynamicSubject傳過去,接着$Proxy0調用父類Proxy的構造器,爲h賦值,以下:
Java代碼
class Proxy{ InvocationHandler h=null; protected Proxy(InvocationHandler h) { this.h = h; } ... } 來看一下這個繼承了Proxy的$Proxy0的源代碼: Java代碼 public final class $Proxy0 extends Proxy implements Subject { private static Method m1; private static Method m0; private static Method m3; private static Method m2; static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m3 = Class.forName("***.RealSubject").getMethod("request", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); } catch (NoSuchMethodException nosuchmethodexception) { throw new NoSuchMethodError(nosuchmethodexception.getMessage()); } catch (ClassNotFoundException classnotfoundexception) { throw new NoClassDefFoundError(classnotfoundexception.getMessage()); } } //static public $Proxy0(InvocationHandler invocationhandler) { super(invocationhandler); } @Override public final boolean equals(Object obj) { try { return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue(); } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } @Override public final int hashCode() { try { return ((Integer) super.h.invoke(this, m0, null)).intValue(); } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final void request() { try { super.h.invoke(this, m3, null); return; } catch (Error e) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } @Override public final String toString() { try { return (String) super.h.invoke(this, m2, null); } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } }
接着把獲得的$Proxy0實例強制轉換成Subject,並將引用賦給subject。當執行subject.request()方法時,就調用了$Proxy0類中的request()方法,進而調用父類Proxy中的h的invoke()方法.即InvocationHandler.invoke()。
PS:一、須要說明的一點是,Proxy類中getProxyClass方法返回的是Proxy的Class類。之因此說明,是由於我一開始犯了個低級錯誤,覺得返回的是「被代理類的Class類」- -!推薦看一下getProxyClass的源碼,很長=。=
二、從$Proxy0的源碼能夠看出,動態代理類不只代理了顯示定義的接口中的方法,並且還代理了java的根類Object中的繼承而來的equals()、hashcode()、toString()這三個方法,而且僅此三個方法。
Q:到如今爲止,還有一個疑問,invoke方法中的第一個參數是Proxy的實例(準確說,最終用到的是$Proxy0的實例),可是有什麼用呢?或者說,程序內是怎樣顯示出做用的? A:就本人目前的水平看來,這個proxy參數並無什麼做用,在整個動態代理機制中,並無用到InvocationHandler中invoke方法的proxy參數。而傳入的這個參數實際是代理類的一個實例。我想多是爲了讓程序員在invoke方法中使用反射來獲取關於代理類的一些信息吧。