動態代理詳解

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

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文件,是在運行時生成的,你也不用去關心它是什麼類型的,你只須要知道它實現了哪些接口便可。ide

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()方法。this

  想象中的類:spa

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()方法。代理

4 InvocationHandler的invoke()方法
  InvocationHandler的invoke()方法的參數有三個:
    Object proxy:代理對象,也就是Proxy.newProxyInstance()方法返回的對象,一般咱們用不上它;
    Method method:表示當前被調用方法的反射對象,例如mi.fun1(),那麼method就是fun1()方法的反射對象;
    Object[] args:表示當前被調用方法的參數,固然mi.fun1()這個調用是沒有參數的,因此args是一個零長數組。
  最後要說的是invoke()方法的返回值爲Object類型,它表示當前被調用的方法的返回值,固然mi.fun1()方法是沒有返回值的,因此invoke()返回的就必須是null了。code

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("當前調用的方法是:" + method.getName());
      return null;
    }
  };
  MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);
  mi.fun1();
  mi.fun2();
}
//當前調用的方法是:fun1
//當前調用的方法是:fun2

5 動態代理的用途
  動態代理的用途與裝飾模式很類似,就是爲了對某個對象進行加強。全部使用裝飾者模式的案例均可以使用動態代理來替換。
  下面咱們用一個例子來講明動態代理的用途!
  咱們來寫一個Waiter接口,它只有一個serve()方法。MyWaiter是Waiter接口的實現類:對象

public interface Waiter {
  public void serve();
}
public class MyWaiter implements Waiter {
  public void serve() {
    System.out.println("服務...");
  }
}

  如今咱們要對MyWaiter對象進行加強,要讓它在服務以前以及服務以後添加禮貌用語,即在服務以前說「您好!」,在服務以後說:「很高興爲您服務!」。

public class MainApp1 {
  public static void main(String[] args) {
    ClassLoader loader = MainApp1.class.getClassLoader();
    Class[] cs = {Waiter.class};
    Waiter target = new MyWaiter();
    MyInvocationHandler h = new MyInvocationHandler(target);
    Waiter waiter = (Waiter)Proxy.newProxyInstance(loader, cs, h);
    waiter.serve();
  }
}
class MyInvocationHandler implements InvocationHandler {
  public Waiter target;
  public MyInvocationHandler(Waiter target) {
    this.target = target;
  }
  public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
    System.out.println("您好!");
    Object result = method.invoke(target, args);
    System.out.println("很高興爲您服務!");
    return result;
  }
}

 實例(AOP):

 1 import org.junit.Test;
 2 /*
 3  * 目標是讓目標對象和加強均可以切換!
 4  */
 5 public class Demo3 {
 6     @Test
 7     public void fun1() {
 8         ProxyFactory factory = new ProxyFactory();//建立工廠
 9         factory.setTargetObject(new ManWaiter());//設置目標對象
10         factory.setBeforeAdvice(new BeforeAdvice() {//設置前置加強
11             public void before() {
12                 System.out.println("您好很差!");
13             }
14         });
15         
16         factory.setAfterAdvice(new AfterAdvice() {//設置後置加強
17             public void after() {
18                 System.out.println("再見不見!");
19             }
20         });
21         Waiter waiter = (Waiter)factory.createProxy();
22         waiter.shouQian();
23     }
24 }
Demo3
 1 import java.lang.reflect.InvocationHandler;
 2 import java.lang.reflect.Method;
 3 import java.lang.reflect.Proxy;
 4 
 5 /**
 6  * 它用來生成代理對象
 7  * 它須要全部的參數
 8  * * 目標對象
 9  * * 加強
10  * @author cxf
11  */
12 /**
13  * 1. 建立代理工廠
14  * 2. 給工廠設置三樣東西:
15  *   * 目標對象:setTargetObject(xxx);
16  *   * 前置加強:setBeforeAdvice(該接口的實現)
17  *   * 後置加強:setAfterAdvice(該接口的實現)
18  * 3. 調用createProxy()獲得代理對象
19  *   * 執行代理對象方法時:
20  *   > 執行BeforeAdvice的before()
21  *   > 目標對象的目標方法
22  *   > 執行AfterAdvice的after()
23  * @author cxf
24  *
25  */
26 public class ProxyFactory {
27     private Object targetObject;//目標對象
28     private BeforeAdvice beforeAdvice;//前置加強
29     private AfterAdvice afterAdvice;//後置加強
30     /**
31      * 用來生成代理對象
32      * @return
33      */
34     public Object createProxy() {
35         /*
36          * 1. 給出三大參數
37          */
38         ClassLoader loader = this.getClass().getClassLoader();
39         Class[] interfaces = targetObject.getClass().getInterfaces();
40         InvocationHandler h = new InvocationHandler() {
41             public Object invoke(Object proxy, Method method, Object[] args)
42                     throws Throwable {
43                 /*
44                  * 在調用代理對象的方法時會執行這裏的內容
45                  */
46                 // 執行前置加強
47                 if(beforeAdvice != null) {
48                     beforeAdvice.before();
49                 }
50                 
51                 Object result = method.invoke(targetObject, args);//執行目標對象的目標方法
52                 // 執行後置加強
53                 if(afterAdvice != null) {
54                     afterAdvice.after();
55                 }
56                 
57                 // 返回目標對象的返回值
58                 return result;
59             }
60         };
61         /*
62          * 2. 獲得代理對象
63          */
64         Object proxyObject = Proxy.newProxyInstance(loader, interfaces, h);
65         return proxyObject;
66     }
67     
68     
69     public Object getTargetObject() {
70         return targetObject;
71     }
72     public void setTargetObject(Object targetObject) {
73         this.targetObject = targetObject;
74     }
75     public BeforeAdvice getBeforeAdvice() {
76         return beforeAdvice;
77     }
78     public void setBeforeAdvice(BeforeAdvice beforeAdvice) {
79         this.beforeAdvice = beforeAdvice;
80     }
81     public AfterAdvice getAfterAdvice() {
82         return afterAdvice;
83     }
84     public void setAfterAdvice(AfterAdvice afterAdvice) {
85         this.afterAdvice = afterAdvice;
86     }
87 }
ProxyFactory
1 public class ManWaiter implements Waiter {
2     public void serve() {
3         System.out.println("服務中...");
4     }
5     
6     public void shouQian() {
7         System.out.println("混蛋,給我錢!");
8     }
9 }
ManWaiter
1 // 服務員
2 public interface Waiter {
3     // 服務
4     public void serve();
5     public void shouQian();
6 }
Waiter
1 public interface AfterAdvice {
2     public void after();
3 }
AfterAdvice
1 /**
2  * 前置加強
3  * @author cxf
4  *
5  */
6 public interface BeforeAdvice {
7     public void before();
8 }
BeforeAdvice
相關文章
相關標籤/搜索