在Spring中,嚴格的來講不僅僅使用JDK的動態代理,還會使用CGLIB動態代理,可是它們大同小異,咱們這裏講JDK的動態代理,有了它的理解CGLIB也不難掌握,讀者能夠閱讀其餘的文檔。java
首先,讓咱們先了解什麼是動態代理,假設讀者是一個軟件工程師,而你的公司是家軟件公司,我是一個客戶,我須要你公司提供軟件的專業服務。顯然我是找到大家公司的商務,而不是你去討論個人要求。那麼商務就等同於你的一個代理,我也會認爲商務等於大家公司,而不會去管你這個軟件工程師如何工做的。bash
代理的實際就是在真實服務對象(軟件工程師)以前加多一個佔位或者叫作代理對象(商務),這個佔位(商務)能夠根據調用者(客戶)的要求去控制真實服務對象的訪問(軟件工程師)。有了這個比喻是否是好理解不少??ide
那麼代理模式的好處在於什麼?首先佔位(商務)能夠在真實服務對象以前以後作一些服務,同時根據須要選擇是否須要啓用真實服務對象,也會加入一些規則,好比商務也能夠根據客戶和公司的規則來提供額外的服務,這時可能就連真實服務對象(軟件工程師)都不會啓用。測試
好,上面的東西貌似很神奇,咱們用一張圖來表達代理的含義。this
好,這裏的代理對象就是咱們以前談到的佔位,它代理了咱們看到的真實對象,能夠在真實對象以前以後,甚至是代替真實對象提供服務。代理
動態代理有好幾種,Spring使用了CGLIG和JDK動態代理。在JDK動態代理中,要求必須提供接口,而CGLIB是不須要的,咱們這裏只談論JDK動態代理,在大部分的狀況下,筆者建議你使用JDK動態代理,由於JDK動態代理的速度要比CGLIB要快,在Spring中一個有切面的Bean若是有接口聲明,Spring就會用JDK動態代理代理它,否者啓用CGLIB。code
好了論述講了一大截,咱們開始講JDK動態代理,首先咱們須要提供一個簡單的接口:對象
public interface HelloService { public void sayHello(String name); }
跟着是實現類:接口
public class HelloServiceImpl implements HelloService { @Override public void sayHello(String name) { System.err.println("hello " + name); } }
咱們跟着就是要生成代理對象(proxy),分紅兩步: 成代理對象要創建代理對象(proxy)和真實對象(HelloServiceImpl)的代理關係。 實現代理方法 在JDK動態代理中須要實現接口:java.lang.reflect.InvocationHandler。文檔
public class HelloProxy implements InvocationHandler { private Object target; /** * 生成代理對象,並和真實服務對象綁定. * @param target 真實服務對線下 * @return 代理對象 */ public Object bind(Object target) { this.target = target; //生成代理對象,並綁定. Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); //指明代理類,this表明用當前類對象,那麼就要求其實現InvocationHandler接口 return proxy; } /** * 當生成代理對象時,第三個指定使用HelloProxy進行代理時,代理對象調用的方法就會進入這個方法。 * @param proxy ——代理對象 * @param method -- 被調用的方法 * @param args -- 方法參數 * @return 代理方法返回。 * @throws Throwable -- 異常處理 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.err.println("反射真實對象方法前"); Object obj = method.invoke(target, args);//至關於sayHello方法調用. System.err.println("反射真實對象方法後"); return obj; } }
首先聲明瞭一個類的屬性,它的做用是保存真實服務對象。
而後用bind方法綁定代理對象和真實對象,是經過這樣去綁定的:
Object proxy = Proxy.newProxyInstance( target.getClass().getClassLoader(), //類的加載器 target.getClass().getInterfaces(), //對象的接口,明確代理對象掛在哪些接口下 this );//指明代理類,this表明用當前類對象,那麼就要求其實現InvocationHandler接口
這樣聲明就會進入當前類invoke方法,它有三個參數:
Object proxy ——當前代理對象
Method method —— 當前調度的方法
Object[] args -- 方法參數 而後咱們經過反射調度真實對象的方法,不懂的能夠看到第一篇的論述(連接描述)
Object obj = method.invoke(target, args);//至關於sayHello方法調用. 讓咱們測試一下這段代碼:
public class Chapter1Main { public static void main(String[] args) { HelloProxy helloProxy = new HelloProxy(); //由於使用了接口HelloService綁定了代理對象,因此能夠用HelloService做爲代理對象的聲明. HelloService proxy = (HelloService) helloProxy.bind(new HelloServiceImpl()); proxy.sayHello("張三");//此時使用代理對象運行方法進入HelloProxy的invoke方法裏 } }
這和時候,咱們運行一下能夠獲得下面的打印
反射真實對象方法前 hello 張三 反射真實對象方法後
好了,咱們看到的HelloService實際已是一個代理對象了,而不是咱們普通人看到的HelloServiceImpl這個真實對象,在Spring中不要被它迷糊了哦。