java 靜態代理和動態代理

1、動態代理(Dynamic Proxy)
  代理分爲靜態代理和動態代理,靜態代理是在編譯時就將接口、實現類、代理類一股腦兒全部手動完成,但如果我們需要很多的代理,每一個都這麼手動的去創建實屬浪費時間,而且會有大量的重複代碼,此時我們就可以採用動態代理,動態代理可以在程序運行期間根據需要動態的創建代理類及其實例,來完成具體的功能。
  其實方法直接調用就可以完成功能,爲什麼還要加個代理呢?
  原因是採用代理模式可以有效的將具體的實現與調用方進行解耦,通過面向接口進行編碼完全將具體的實現隱藏在內部。
2、代理實現的一般模式
  其實代理的一般模式就是靜態代理的實現模式:首先創建一個接口(JDK代理都是面向接口的),然後創建具體實現類來實現這個接口,在創建一個代理類同樣實現這個接口,不同指出在於,具體實現類的方法中需要將接口中定義的方法的業務邏輯功能實現,而代理類中的方法只要調用具體類中的對應方法即可,這樣我們在需要使用接口中的某個方法的功能時直接調用代理類的方法即可,將具體的實現類隱藏在底層。
  第一步:定義總接口Iuser.java
  

package ceshi1;
public interface Iuser {
     void eat(String s);
}

 第二步:創建具體實現類UserImpl.java
 

package ceshi1;
public class UserImpl implements Iuser {
  @Override
  public void eat(String s) {
    System.out.println("我要吃"+s);
  }
}

 第三步:創建代理類UserProxy.java
 

package ceshi1;
public class UserProxy implements Iuser {
  private Iuser user = new UserImpl();
  @Override
  public void eat(String s) {
    System.out.println("靜態代理前置內容");
    user.eat(s);
    System.out.println("靜態代理後置內容");
  }
}

第四步:創建測試類ProxyTest.java

package ceshi1;
public class ProxyTest {
  public static void main(String[] args) {    
    UserProxy proxy = new UserProxy();
    proxy.eat("蘋果");
  }
}

  運行結果:
  

靜態代理前置內容
我要吃蘋果
靜態代理後置內容

3、動態代理的實現
  動態代理的思維模式與之前的一般模式是一樣的,也是面向接口進行編碼,創建代理類將具體類隱藏解耦,不同之處在於代理類的創建時機不同,動態代理需要在運行時因需實時創建。
  第一步:定義總接口Iuser.java
  

package ceshi1;
 public interface Iuser {
   void eat(String s);
 }

第二步:創建具體實現類UserImpl.java

package ceshi1;
public class UserImpl implements Iuser {
  @Override
  public void eat(String s) {
    System.out.println("我要吃"+s);
  }
}

 第三步:創建實現InvocationHandler接口的代理類
 

package ceshi1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxy implements InvocationHandler {
  private Object object;//用於接收具體實現類的實例對象
  //使用帶參數的構造器來傳遞具體實現類的對象
  public DynamicProxy(Object obj){
    this.object = obj;
  }
  @Override
  public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
    System.out.println("前置內容");
    method.invoke(object, args);
    System.out.println("後置內容");
    return null;
  }
}

 第四步:創建測試類ProxyTest.java
 

package ceshi1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class ProxyTest {
  public static void main(String[] args) {
    Iuser user = new UserImpl();
    InvocationHandler h = new DynamicProxy(user);
    Iuser proxy = (Iuser) Proxy.newProxyInstance(Iuser.class.getClassLoader(), new Class[]{Iuser.class}, h);
    proxy.eat("蘋果");
  }
}

 運行結果爲:
 

動態代理前置內容
我要吃蘋果
動態代理後置內容

4、通過上面的動態代理實例我們來仔細分析研究一下動態代理的實現過程
(1)首先我要說的就是接口,爲什麼JDK的動態代理是基本接口實現的呢?
  因爲通過使用接口指向實現類的實例的多態實現方式,可以有效的將具體的實現與調用之間解耦,便於後期修改與維護。
再具體的說就是我們在代理類中創建一個私有成員變量(private修飾),使用接口來指向實現類的對象(純種的多態體現,向上轉型的體現),然後在該代理類中的方法中使用這個創建的實例來調用實現類中的相應方法來完成業務邏輯功能。
這麼說起來,我之前說的“將具體實現類完全隱藏”就不怎麼正確了,可以改成,將具體實現類的細節向調用方完全隱藏(調用方調用的是代理類中的方法,而不是實現類中的方法)。
  這就是面向接口編程,利用java的多態特性,實現程序代碼的解耦。
(2)創建代理類的過程
  如果你瞭解靜態代理,那麼你會發現動態代理的實現其實與靜態代理類似,都需要創建代理類,但是不同之處也很明顯,創建方式不同!
  不同之處體現在靜態代理我們知根知底,我們知道要對哪個接口、哪個實現類來創建代理類,所以我們在編譯前就直接實現與實現類相同的接口,直接在實現的方法中調用實現類中的相應(同名)方法即可;而動態代理不同,我們不知道它什麼時候創建,也不知道要創建針對哪個接口、實現類的代理類(因爲它是在運行時因需實時創建的)。
  雖然二者創建時機不同,創建方式也不相同,但是原理是相同的,不同之處僅僅是:靜態代理可以直接編碼創建,而動態代理是利用反射機制來抽象出代理類的創建過程。
  讓我們來分析一下之前的代碼來驗證一下上面的說辭:
    第一點:靜態代理需要實現與實現類相同的接口,而動態代理需要實現的是固定的Java提供的內置接口(一種專門提供來創建動態代理的接口)InvocationHandler接口,因爲java在接口中提供了一個可以被自動調用的方法invoke,這個之後再說。
    第二點:private Object object;
        public UserProxy(Object obj){this.object = obj;}
  這幾行代碼與靜態代理之中在代理類中定義的接口指向具體實現類的實例的代碼異曲同工,通過這個構造器可以創建代理類的實例,創建的同時還能將具體實現類的實例與之綁定(object指的就是實現類的實例,這個實例需要在測試類中創建並作爲參數來創建代理類的實例),實現了靜態代理類中private Iuser user = new UserImpl();一行代碼的作用相近,這裏爲什麼不是相同,而是相近呢,主要就是因爲靜態代理的那句代碼中包含的實現類的實例的創建,而動態代理中實現類的創建需要在測試類中完成,所以此處是相近。
    第三點:invoke(Object proxy, Method method, Object[] args)方法,該方法是InvocationHandler接口中定義的唯一方法,該方法在調用指定的具體方法時會自動調用。其參數爲:代理實例、調用的方法、方法的參數列表
  在這個方法中我們定義了幾乎和靜態代理相同的內容,僅僅是在方法的調用上不同,不同的原因與之前分析的一樣(創建時機的不同,創建的方式的不同,即反射),Method類是反射機制中一個重要的類,用於封裝方法,該類中有一個方法那就是invoke(Object object,Object…args)方法,其參數分別表示:所調用方法所屬的類的對象和方法的參數列表,這裏的參數列表正是從測試類中傳遞到代理類中的invoke方法三個參數中最後一個參數(調用方法的參數列表)中,在傳遞到method的invoke方法中的第二個參數中的(此處有點囉嗦)。
    第四點:測試類中的異同
  靜態代理中我們測試類中直接創建代理類的對象,使用代理類的對象來調用其方法即可,若是別的接口(這裏指的是別的調用方)要調用Iuser的方法,也可以使用此法
動態代理中要複雜的多,首先我們要將之前提到的實現類的實例創建(補充完整),然後利用這個實例作爲參數,調用代理來的帶參構造器來創建“代理類實例對象”,這裏加引號的原因是因爲它並不是真正的代理類的實例對象,而是創建真正代理類實例的一個參數,這個實現了InvocationHandler接口的類嚴格意義上來說並不是代理類,我們可以將其看作是創建代理類的必備中間環節,這是一個調用處理器,也就是處理方法調用的一個類,不是真正意義上的代理類,可以這麼說:創建一個方法調用處理器實例。
  下面纔是真正的代理類實例的創建,之前創建的”代理類實例對象“僅僅是一個參數
    Iuser proxy = (Iuser) Proxy.newProxyInstance(Iuser.class.getClassLoader(), new Class[]{Iuser.class}, h);
  這裏使用了動態代理所依賴的第二個重要類Proxy,此處使用了其靜態方法來創建一個代理實例,其參數分別是:類加載器(可爲父類的類加載器)、接口數組、方法調用處理器實例
  這裏同樣使用了多態,使用接口指向代理類的實例,最後會用該實例來進行具體方法的調用即可。
  

文章轉載自http://www.cnblogs.com/V1haoge/p/5860749.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章