神祕代理-Proxy

前言:java

  代理模式做爲常見的設計模式之一,在項目開發中不可或缺。本文就嘗試着揭開代理的神祕面紗,也歡迎各路人批評指正!設計模式

1.如何實現代理:

【假設有個關於汽車移動(move)的計時需求】
設計:Moveable接口,一個Car的實現類;兩個代理CarTimer,TimeHandler.UML圖以下:框架


1)繼承dom

 1 package com.gdufe.proxy;
 2 
 3 import java.util.Random;
 4 
 5 public class CarTimer extends Car {
 6     
 7     @Override
 8     public void move() {
 9         long start=System.currentTimeMillis();
10         super.move();        //調用父類的move()方法
11         try{
12             Thread.sleep(new Random().nextInt(10000));
13         }catch(Exception e){
14             e.printStackTrace();
15         }
16         long end=System.currentTimeMillis();
17         System.out.println("I'm time machine.Time for moving:"+(end-start));
18     }
19 }
View Code


2)組合
ide

 1 package com.gdufe.proxy;
 2 
 3 import java.util.Random;
 4 
 5 public class TimeHandler implements Moveable {
 6     private Moveable m;
 7     public TimeHandler(Moveable m) {
 8         this.m = m;
 9     }
10     @Override
11     public void move() {
12         long start=System.currentTimeMillis();
13         m.move();
14         try{
15             Thread.sleep(new Random().nextInt(10000));
16         }catch(Exception e){
17             e.printStackTrace();
18         }
19         long end=System.currentTimeMillis();
20         System.out.println("I'm time machine.Time for moving:"+(end-start));
21     }
22 
23 }
View Code

 

客戶端代碼:工具

 1 package com.gdufe.proxy;
 2 
 3 public class Client {
 4     public static void main(String[] args) {
 5         System.out.println("繼承實現代理:");
 6         new CarTimer().move();
 7         System.out.println("組合實現代理:");
 8         new TimeHandler(new Car()).move();
 9     }
10 }

 


輸出結果:
測試

繼承實現代理:
Car moving...
I'm time machine.Time for moving:7080
組合實現代理:
Car moving...
I'm time machine.Time for moving:5169

 

分析:從上述例子實現當中,咱們第一感受天然是分不出兩種代理的實現方式孰優孰劣。且繼續往下面看。this

2.靈活代理-接口切換

【假設如今特殊需求不肯定:「汽車移動以前先往左仍是先往右」】
很明顯,咱們此時若使用繼承的方式實現代理,則後續很不容易維護,並且會造成臃腫的繼承鏈;但使用接口的方式咱們發現僅須要兩個代理類:TurnLeft,TurnRight。並且,無論後續需求如何都只須要作簡單的調整。UML圖以下:spa

----------設計

TurnLeft.java

 1 package com.gdufe.proxy;
 2 
 3 public class TurnLeft implements Moveable {
 4     private Moveable m;
 5     public TurnLeft(Moveable m) {
 6         this.m = m;
 7     }
 8     @Override
 9     public void move() {
10         System.out.println("turn left...");
11         m.move();
12     }
13 
14 }
View Code

TurnRight.java

 1 package com.gdufe.proxy;
 2 
 3 public class TurnRight implements Moveable {
 4 
 5     private Moveable m;
 6     public TurnRight(Moveable m) {
 7         this.m = m;
 8     }
 9     @Override
10     public void move() {
11         System.out.println("turn right");
12         m.move();
13     }
14 
15 }
View Code

客戶端代碼:

 1 package com.gdufe.proxy;
 2 
 3 public class Client0 {
 4 
 5     /**
 6      * @param args
 7      */
 8     public static void main(String[] args) {
 9         System.out.println("Turn right,then left before moving:");
10         test1();
11         
12         System.out.println("Turn left,then right before moving:");
13         test2();
14     }
15     //對接口的實現內外包裝
16     private static void test1() {
17         Car car  = new Car();
18         Moveable m1 = new TurnLeft(car);
19         Moveable m2 = new TurnRight(m1);
20         m2.move();
21     }
22     public static void test2(){
23         Car car = new Car();
24         Moveable m1 = new TurnRight(car);
25         Moveable m2 = new TurnLeft(m1);
26         m2.move();
27     }
28 
29 }

 

輸出結果:

Turn right,then left before moving:
turn right
turn left...
Car moving...
Turn left,then right before moving:
turn left...
turn right
Car moving...

 

======================== 

3.動態代理:

其實,無論是繼承仍是組合的方式,咱們上面實現的都僅僅是「靜態代理」,也是咱們平時用的比較多的。如今,咱們開始聊聊Java的神器---「動態代理」。
【假設如今須要實現一個「萬能」的日誌工具,即無論對任何類的任何方法均可以動態對其進行日誌操做】
例如:上面的例子中,請思考如何實如今汽車移動以前進行日誌操做?
常規的靜態代理方式固然能夠實現,下面咱們就利用Java中的Proxy類進行實現。UML圖以下:

 

添加關鍵類:LogHandler.java

 1 package com.gdufe.proxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 
 6 public class LogHandler implements InvocationHandler {
 7     
 8     //被代理對象
 9     private Object proxied;
10     
11     public LogHandler(Object proxied) {
12         this.proxied = proxied;
13     }
14     @Override
15     public Object invoke(Object proxy, Method method, Object[] args)
16             throws Throwable {
17         System.out.println("I'm log machine.");
18         return method.invoke(proxied);
19     }
20 
21 }

 

客戶端代碼:

 1 package com.gdufe.proxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Proxy;
 5 
 6 public class Client1 {
 7 
 8     /**
 9      * @param args
10      */
11     public static void main(String[] args) {
12         
13         //單首創建Moveable接口對象
14         Moveable m=null;
15         m = new Car();
16         m = new TimeHandler(m);
17         
18         //初始化LogHandler實現的InvacationHandler接口
19         InvocationHandler h =  new LogHandler(m);
20         m = (Moveable) Proxy.newProxyInstance(
21                 Moveable.class.getClassLoader(),
22                 new Class[] {Moveable.class},h);
23         
24         //m在動態代理處理完後,move()方法已被修改
25         m.move();
26     }
27 
28 }

 

輸出結果:

I'm log machine.
Car moving...
I'm time machine.Time for moving:110

 

分析:

  上述的實現代碼對於剛接觸Java的朋友來講估計比較費解。要理解其過程,至少對java的反射機制有必定的理解。看穿了的話,其實動態代理的關鍵環節,就在newProxyInstance()操做上。代碼實現的關鍵步驟:

Step1:初始化被代理的對象(如上圖中的TimeHandler);

Step2:建立一個實現了InvocationHandler接口的類,在invoke()方法進行你但願執行的代理操做(如上圖的LogHandler);

Step3:將經過Proxy拿到的新對象賦給最終要實現的接口,最後調用該接口方法(如代碼中的「m.move()」)。

---------------------------------------------------------------------

有朋友可能犯迷糊了,動態代理的內部實現過程呢?Proxy是怎樣一步一步識別到Car的呢?

請看圖:

 

注意:$Proxy類是虛擬存在的,在Java API中是找不到的。也就是說,它只存在於中間過程。因此,爲方便你們理解就加上了。)

  
  那麼,到底動態代理適用於什麼情形呢?從上面汽車的簡單日誌實例也許還難以看出。下面咱們再引入一個測試。
【假設如今增長一個司機(Driver)類,其實現了Speakable接口;很明顯他跟汽車沒有很直接的關聯,那麼如今咱們利用動態代理的方式將上面的LogHandler加到司機的speak()方法上】
----------
客戶端代碼:

 1 package com.gdufe.proxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Proxy;
 5 
 6 public class Client2 {
 7 
 8     /**
 9      * @param args
10      */
11     public static void main(String[] args) {
12         Moveable m=null;
13         m = new Car();
14 
15         Speakable s =null;
16         s = new Driver();
17         
18         InvocationHandler h = null;
19         h=new LogHandler(m);
20         m = (Moveable) Proxy.newProxyInstance(Moveable.class.getClassLoader(),new Class[] {Moveable.class},h);
21         m.move();
22         
23         //司機被代理
24         h = new LogHandler(s);
25         s = (Speakable)Proxy.newProxyInstance(Speakable.class.getClassLoader(),new Class[]{Speakable.class}, h);
26         s.speak();
27         }
28 
29 }

 

輸出結果:

I'm log machine.
Car moving...
I'm log machine.
Driver speak...

  在汽車move()跟司機speak()以前,都自動實現LogHandler操做!

-----------------------------------------------------

後語:

經過上述例子,總結動態代理優勢:
·適用任何類的任何方法;
·使用靈活,可隨時將代理工具類加入或抽出。

  動態代理是Java語言的精華所在,不少的開發框架都是基於其內部原理。本人目前對動態代理的理解也僅限於此,歡迎對Java有深度學識的朋友拍磚,謝謝~

相關文章
相關標籤/搜索