JDK動態代理

在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中不要被它迷糊了哦。  

相關文章
相關標籤/搜索