Java Proxy動態代理

百聯達發表於2016-12-06
一:背景

Spring主要有兩大思想,一個是IoC,另一個就是AOP.而AOP的原理就是java的動態代理機制.

二:概念

動態代理其實就是java.lang.reflect.Proxy類動態的根據您指定的所有介面生成一個class BYTE,該class會繼承Proxy類,並實現所有你指定的介面(您在引數中傳入的介面陣列);然後再利用您指定的classloader將 class BYTE載入進系統,最後生成這樣一個類的物件,並初始化該物件的一些值,如invocationHandler,以即所有的介面對應的Method成員。 初始化之後將物件返回給呼叫的客戶端

三:解析

在java的動態代理機制中,有兩個重要的類或介面,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這一個類和介面是實現我們動態代理所必須用到的。

InvocationHandler:
每一個動態代理類都必須要實現InvocationHandler這個介面,並且每個代理類的例項都關聯到了一個Handler,當我們透過代理物件呼叫一個方法的時候,這個方法的呼叫就會被轉發為由InvocationHandler這個介面的 invoke 方法來進行呼叫。我們來看看InvocationHandler這個介面的唯一一個方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我們看到這個方法一共接受三個引數,那麼這三個引數分別代表什麼呢?

proxy:  指代我們所代理的那個真實物件
method:  指代的是我們所要呼叫真實物件的某個方法的Method物件
args:  指代的是呼叫真實物件某個方法時接受的引數

Proxy:

Proxy這個類的作用就是用來動態建立一個代理物件的類,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException
這個方法的作用就是得到一個動態的代理物件,其接收三個引數,我們來看看這三個引數所代表的含義:
loader:  一個ClassLoader物件,定義了由哪個ClassLoader物件來對生成的代理物件進行載入
interfaces:  一個Interface物件的陣列,表示的是我將要給我需要代理的物件提供一組什麼介面,如果我提供了一組介面給它,那麼這個代理物件就宣稱實現了該介面(多型),這樣我就能呼叫這組介面中的方法了
h: 一個InvocationHandler物件,表示的是當我這個動態代理物件在呼叫方法的時候,會關聯到哪一個InvocationHandler物件上


四:案例

業務介面:

點選(此處)摺疊或開啟

  1. public interface BusinessProcessor {
  2.     public void processBusiness();
  3. }
業務實現:

點選(此處)摺疊或開啟

  1. public class BusinessProcessorImpl implements BusinessProcessor{

  2.     /* (non-Javadoc)
  3.      * @see com.enjoylink.gmap.mongodb.demo.BusinessProcessor#processBusiness()
  4.      */
  5.     @Override
  6.     public void processBusiness() {
  7.         
  8.         System.out.println("===========處理業務============");
  9.     }

  10. }
業務代理:

點選(此處)摺疊或開啟

  1. public class BusinessProcessorHandler implements InvocationHandler {

  2.     private Object target = null;

  3.     BusinessProcessorHandler(Object target) {
  4.         this.target = target;
  5.     }

  6.     /*
  7.      * (non-Javadoc)
  8.      *
  9.      * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
  10.      * java.lang.reflect.Method, java.lang.Object[])
  11.      */
  12.     @Override
  13.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  14.         
  15.         System.out.println("=========業務處理------前的操作=========");
  16.         Object result=method.invoke(target, args);
  17.         System.out.println("=========業務處理------後的操作=========");
  18.         return result;
  19.     }

  20. }
測試:

點選(此處)摺疊或開啟

  1. public class BusinessProcessorTest {

  2.     public static void main(String[] args) {
  3.         BusinessProcessorImpl bpImpl = new BusinessProcessorImpl();
  4.         BusinessProcessorHandler handler = new BusinessProcessorHandler(bpImpl);

  5.         BusinessProcessor bp = (BusinessProcessor) Proxy.newProxyInstance(bpImpl.getClass().getClassLoader(),
  6.                 bpImpl.getClass().getInterfaces(), handler);

  7.         bp.processBusiness();
  8.         System.out.println(bp.getClass().getName());

  9.         Class clz = bp.getClass();
  10.         printClassDefinition(clz);

  11.     }

  12.     public static String getModifier(int modifier) {
  13.         String result = "";
  14.         switch (modifier) {
  15.             case Modifier.PRIVATE:
  16.                 result = "private";
  17.             case Modifier.PUBLIC:
  18.                 result = "public";
  19.             case Modifier.PROTECTED:
  20.                 result = "protected";
  21.             case Modifier.ABSTRACT:
  22.                 result = "abstract";
  23.             case Modifier.FINAL:
  24.                 result = "final";
  25.             case Modifier.NATIVE:
  26.                 result = "native";
  27.             case Modifier.STATIC:
  28.                 result = "static";
  29.             case Modifier.SYNCHRONIZED:
  30.                 result = "synchronized";
  31.             case Modifier.STRICT:
  32.                 result = "strict";
  33.             case Modifier.TRANSIENT:
  34.                 result = "transient";
  35.             case Modifier.VOLATILE:
  36.                 result = "volatile";
  37.             case Modifier.INTERFACE:
  38.                 result = "interface";
  39.         }
  40.         return result;
  41.     }

  42.     public static void printClassDefinition(Class clz) {

  43.         String clzModifier = getModifier(clz.getModifiers());
  44.         if (clzModifier != null && !clzModifier.equals("")) {
  45.             clzModifier = clzModifier + " ";
  46.         }
  47.         String superClz = clz.getSuperclass().getName();
  48.         if (superClz != null && !superClz.equals("")) {
  49.             superClz = "extends " + superClz;
  50.         }

  51.         Class[] interfaces = clz.getInterfaces();

  52.         String inters = "";
  53.         for (int i = 0; i < interfaces.length; i++) {
  54.             if (i == 0) {
  55.                 inters += "implements ";
  56.             }
  57.             inters += interfaces[i].getName();
  58.         }

  59.         System.out.println(clzModifier + clz.getName() + " " + superClz + " " + inters);
  60.         System.out.println("{");

  61.         Field[] fields = clz.getDeclaredFields();
  62.         for (int i = 0; i < fields.length; i++) {
  63.             String modifier = getModifier(fields[i].getModifiers());
  64.             if (modifier != null && !modifier.equals("")) {
  65.                 modifier = modifier + " ";
  66.             }
  67.             String fieldName = fields[i].getName();
  68.             String fieldType = fields[i].getType().getName();
  69.             System.out.println(" " + modifier + fieldType + " " + fieldName + ";");
  70.         }

  71.         System.out.println();

  72.         Method[] methods = clz.getDeclaredMethods();
  73.         for (int i = 0; i < methods.length; i++) {
  74.             Method method = methods[i];

  75.             String modifier = getModifier(method.getModifiers());
  76.             if (modifier != null && !modifier.equals("")) {
  77.                 modifier = modifier + " ";
  78.             }

  79.             String methodName = method.getName();

  80.             Class returnClz = method.getReturnType();
  81.             String retrunType = returnClz.getName();

  82.             Class[] clzs = method.getParameterTypes();
  83.             String paraList = "(";
  84.             for (int j = 0; j < clzs.length; j++) {
  85.                 paraList += clzs[j].getName();
  86.                 if (j != clzs.length - 1) {
  87.                     paraList += ", ";
  88.                 }
  89.             }
  90.             paraList += ")";

  91.             clzs = method.getExceptionTypes();
  92.             String exceptions = "";
  93.             for (int j = 0; j < clzs.length; j++) {
  94.                 if (j == 0) {
  95.                     exceptions += "throws ";
  96.                 }

  97.                 exceptions += clzs[j].getName();

  98.                 if (j != clzs.length - 1) {
  99.                     exceptions += ", ";
  100.                 }
  101.             }

  102.             exceptions += ";";

  103.             String methodPrototype = modifier + retrunType + " " + methodName + paraList + exceptions;

  104.             System.out.println(" " + methodPrototype);

  105.         }
  106.         System.out.println("}");
  107.     }

  108. }
測試結果:

點選(此處)摺疊或開啟

  1. =========業務處理------前的操作=========
  2. ===========處理業務============
  3. =========業務處理------後的操作=========
  4. com.sun.proxy.$Proxy0
  5. com.sun.proxy.$Proxy0 extends java.lang.reflect.Proxy implements com.enjoylink.gmap.mongodb.demo.BusinessProcessor
  6. {
  7.     java.lang.reflect.Method m1;
  8.     java.lang.reflect.Method m2;
  9.     java.lang.reflect.Method m3;
  10.     java.lang.reflect.Method m0;

  11.     boolean equals(java.lang.Object);
  12.     java.lang.String toString();
  13.     int hashCode();
  14.     void processBusiness();
  15. }

Proxy.newProxyInstance方法會做如下幾件事:

 

1,根據傳入的第二個引數interfaces動態生成一個類,實現interfaces中的介面,該例中即BusinessProcessor介面的processBusiness方法。並且繼承了Proxy類,重寫了hashcode,toString,equals等三個方法。具體實現可參看 ProxyGenerator.generateProxyClass(...); 該例中生成了$Proxy0

 

2,透過傳入的第一個引數classloder將剛生成的類載入到jvm中。即將$Proxy0load

 

3,利用第三個引數,呼叫$Proxy0$Proxy0(InvocationHandler)建構函式 建立$Proxy0的物件,並且用interfaces引數遍歷其所有介面的方法,並生成Method物件初始化物件的幾個Method成員變數

 

4,將$Proxy0的例項返回給客戶端。

我們再看客戶端怎麼調就清楚了。

 

1,客戶端拿到的是$Proxy0的例項物件,由於$Proxy0繼承了BusinessProcessor,因此轉化為BusinessProcessor沒任何問題。

 

BusinessProcessor bp = (BusinessProcessor)Proxy.newProxyInstance(....);

 

2bp.processBusiness()

 

實際上呼叫的是$Proxy0.processBusiness();那麼$Proxy0.processBusiness()的實現就是透過InvocationHandler去呼叫invoke方法。

五:總結

代理模式最大的特點就是代理類和實際業務類實現同一個介面(或繼承同一父類),代理物件持有一個實際物件的引用,外部呼叫時操作的是代理物件,而在代理物件的內部實現中又會去呼叫實際物件的操作
 Java動態代理其實內部也是透過Java反射機制來實現的,即已知的一個物件,然後在執行時動態呼叫其方法,這樣在呼叫前後作一些相應的處理.

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28624388/viewspace-2129828/,如需轉載,請註明出處,否則將追究法律責任。

相關文章