1. 程式人生 > >Java動態代理--Proxy.newProxyInstance3

Java動態代理--Proxy.newProxyInstance3

 

1 學習動態代理的目的
  動態代理技術都是在框架中使用,例如:Struts1、Struts2、Spring和Hibernate中都使用了動態代理技術。如果你不想自己寫個框架,那麼你基本上是用上不動態代理技術的。學習動態代理技術的目的是為了更好的理解框架內部的原理,也就是說是為了將來學習框架打基礎!動態代理技術有點難度!而且明白了動態代理技術可能一時也想不到他適合在什麼情況下使用它。這些東西都會在學習框架時漸漸明白。

2 執行時實現指定的介面
  想實現某個介面,你需要寫一個類,然後在類名字的後面給出“implements”XXX介面。這才是實現某個介面:

public interface MyInterface {
  void fun1();
  void fun2();
}

public class MyInterfaceImpl implements MyInterface {
  public void fun1() {
    System.out.println("fun1()");
  }
  public void fun2() {
    System.out.println("fun2()");
  }
}

  上面的程式碼對我們來說沒有什麼新鮮感,我們要說的是動態代理技術可以通過一個方法呼叫就可以生成一個對指定介面的實現類物件。

Class[] cs = {MyInterface.class};
MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);

  上面程式碼中,Proxy類的靜態方法newProxyInstance()方法生成了一個物件,這個物件實現了cs陣列中指定的介面。沒錯,返回值mi是MyInterface介面的實現類。你不要問這個類是哪個類,你只需要知道mi是MyInterface介面的實現類就可以了。你現在也不用去管loader和h這兩個引數是什麼東東,你只需要知道,Proxy類的靜態方法newProxyInstance()方法返回的方法是實現了指定介面的實現類物件,甚至你都沒有看見實現類的程式碼。


  動態代理就是在執行時生成一個類,這個類會實現你指定的一組介面,而這個類沒有.java檔案,是在執行時生成的,你也不用去關心它是什麼型別的,你只需要知道它實現了哪些介面即可。

3 newProxyInstance()方法的引數
  Proxy類的newInstance()方法有三個引數:
    ClassLoader loader:它是類載入器型別,你不用去理睬它,你只需要知道怎麼可以獲得它就可以了:MyInterface.class.getClassLoader()就可以獲取到ClassLoader物件,沒錯,只要你有一個Class物件就可以獲取到ClassLoader物件;
    Class[] interfaces:指定newProxyInstance()方法返回的物件要實現哪些介面,沒錯,可以指定多個介面,例如上面例子只我們只指定了一個介面:Class[] cs = {MyInterface.class};


    InvocationHandler h:它是最重要的一個引數!它是一個介面!它的名字叫呼叫處理器!你想一想,上面例子中mi物件是MyInterface介面的實現類物件,那麼它一定是可以呼叫fun1()和fun2()方法了,難道你不想呼叫一下fun1()和fun2()方法麼,它會執行些什麼東東呢?其實無論你呼叫代理物件的什麼方法,它都是在呼叫InvocationHandler的invoke()方法!

public static void main(String[] args) {
  Class[] cs = {MyInterface.class};
  ClassLoader loader = MyInterface.class.getClassLoader();
  InvocationHandler h = new InvocationHandler() {
    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
      System.out.println("無論你呼叫代理物件的什麼方法,其實都是在呼叫invoke()...");
      return null;
    }
  };
  MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);
  mi.fun1();
  mi.fun2();
}

InvocationHandler介面只有一個方法,即invoke()方法!它是對代理物件所有方法的唯一實現。也就是說,無論你呼叫代理物件上的哪個方法,其實都是在呼叫InvocationHandler的invoke()方法。

這個動態代理生成的想象中的類類似這種:

class X implements MyInterface {
  private InvocationHandler h;
  public X(InvocationHandler h) {
    this.h = h;
  }
  public void fun1() {
    h.invoke();
  }
  public void fun2() {
    h.invoke();
  }
}

注意,X類是我們用來理解代理物件與InvocationHandler之間的關係的,但它是不存在的類。是我們想象出來的!也就是說,它是用來說明,無論你呼叫代理物件的哪個方法,最終呼叫的都是呼叫處理器的invoke()方法。