java動態代理原理

java的動態代理學習
此前總是在hadoop的學習中聽說這個名詞,但是總是不知道其所以然。今天有空來好好的研究研究。
所有的編程代碼學習都離不開示例,尤其是自己編寫代碼。下面是一個示例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 *
 * 理解理解java的代理機制(proxy) <br>
 * 1.代理機制的現實版演繹: <br>
 * 就拿買火車票來說,我、黃牛、火車代售站;這裏有三個人,我是需要買票的客戶,我可以直接在“火車代售站”買票,也可以找“黃牛”買票,也就是說
 * “黃牛”可以代理火車代售站。即我和黃牛交易與我和火車代售站交易一樣,黃牛代理了火車代售站:都有一個賣票的窗口 <br>
 * 2.java的代理機制 <br>
 * B類持有A類的引用,調用A類就相當於當用B類(好繞口)
 *
 * @author fangshun.liao
 * @version 2015年1月12日 下午2:41:52
 */
public class ProxyTest {

 public static void main(String args[]) {
  Real real = new Real();
  RealInterface proxy = (RealInterface) Proxy.newProxyInstance(real.getClass()
    .getClassLoader(), new Class[]{RealInterface.class}, new InvocationReal(real));
  proxy.print();
 }
}

interface RealInterface{
 public void print();
}

class Real implements RealInterface{
 public void print() {
  System.out.println("do Real");
 }
}

class InvocationReal implements InvocationHandler {
 private Real real;

 public InvocationReal( Real real) { 
  this.real = real;
 }

 @Override
 public Object invoke(Object proxy, Method method, Object[] args)
   throws Throwable {
  System.out.println("proxy invocation starting...");
  method.invoke(real, args);
  System.out.println(method.getName());
  System.out.println("proxy invocation ended");
  return proxy;
 }
}
上面的代碼是一個非常簡單的示例,就是將本來new好的對象再用Proxy包裝起來:
RealInterface proxy = (RealInterface) Proxy.newProxyInstance(real.getClass() .getClassLoader(), new Class[]{RealInterface.class}, new InvocationReal(real));
這個方法看起來好怪啊,classLoader都出來了,且第二個必須是interface才行,第三個也不能爲空,且又引用了real。當看到這時會發現,這樣子寫程序真是腦子有毛病呢!明明就有個real了,乾脆直接調用real就好了,還要繞來繞去幹什麼?是的,如果就光這個例子,是不能夠說明什麼問題的,且反而感覺憋手蹩腳的。不妨先不考慮動態代理到底有什麼用,而是猜猜它的實現方式即執行方式:
1.參數說明及用途猜想
    classLoader:其可以解析出java字節碼(.class)文件,那麼如果我們能夠生成java的字節碼,我們就可以用此加載出一個類;更進一步:在知道所有方法名時就可以自動生成一個類。這裏猜測newProxyInstance方法會自己創造一個類,這個類實現了interfaces。這樣解釋有點說的通此方法返回值必須是interfaces中的一個,而不能是類(如 Real類)。
   InvocationHandler:方法調用的句柄,也就是proxy調用方法後最終真正實現的地方。且看其實現方式:
class InvocationReal implements InvocationHandler {
 private Real real;//其實這個InvocationReal 和Real可以沒有任何關係
 public InvocationReal( Real real) { 
  this.real = real;
 }

 @Override
 public Object invoke(Object proxy, Method method, Object[] args)//proxy爲方法所依賴的代理,method即調用的方法實體類,args就是參數列表
   throws Throwable {
  System.out.println("proxy invocation starting...");
  method.invoke(real, args);//這裏真正執行方法
  System.out.println(method.getName());
  System.out.println("proxy invocation ended");
  return proxy;
 }
}


2.proxy.print()的調用過程猜想
  上面獲得了一個proxy,下面就可以來調用方法了:proxy.print();
   要想知道此方法最終是怎麼以一個進行過程,可以debug跟蹤。會發現其是直接跳入到了InvocationReal .invoke(...)方法中的。這裏如果再和上面的猜測結合,這裏我們可以自己動手寫一個將就的動態代理的實現了,下面將我自己要實現的方法的僞代碼給出:
Proxy.newProxyInstance(real.getClass()  .getClassLoader(), new Class[]{RealInterface.class}, new InvocationReal(real)):
     1.Object [] its = getAllInterfaces();//獲取所有的接口
     2. Object  cl = generateClassFromInterfaces(its,InvocationHandler);//根據interfaces生成一個類,這個類實現了所有的接口
     3. generateClassFromInterfaces(its): 
             ==>每一個方法的調用就是:
try {
   Method method = this.getClass().getMethod("print", new Class[]{});
   Object args[] = new Object[1];
   args[0] = null;//因爲此處沒有輸入參數   
   InvocationReal.invoke(this,method,args);
  } catch (NoSuchMethodException e) {
   e.printStackTrace();
  } catch (SecurityException e) {
   e.printStackTrace();
  }


實驗後發現proxy的名字就是com.xxx.xxx.xxx_test.$Proxy0,看來猜測是猜對了。

原理猜測出來,那麼什麼地方用到了呢?這個代理其實在遠程過程調用(RPC)見的最多,不妨思考思考,在newProxyInstance時我們只要接口就行了,具體怎麼實現的都可以不用管,然後在invocationHandler的invoke中我們可以做非常多的事情,如將method序列化發送出去,然後等待遠處返回結果等等。即我們自己寫一個invocationHandler就行,至於invoke中怎麼寫就隨便你了。這樣看來是不是感覺非常牛叉?
其實代理模式就是A持有B的引用,調用A的方法全部由B真正執行。






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