設計模式——Proxy代理模式

設計模式——Proxy代理模式

Proxy代理模式

 爲其他對象提供一種代理,以控制這個對象的訪問。代理對象起到中介作用,可以去掉功能服務或增加額外的服務。

幾種常見的代理模式

遠程代理:爲不同地理的對象提供局域網代表對象。

虛擬代理:根據需要將資源消耗很大的對象進行延遲,真正需要的時候進行創建。

保護代理:控制用戶的訪問權限。

智能引用代理:提供對目標對象額外服務。

 

靜態代理

代理和被代理對象在代理之前是確定的,他們都實現相同的接口或者繼承相同的抽象類。代理類可以繼承或聚合被代理類,以便於擴充和刪減功能。(有點像裝飾器模式)

舉慄:

Moveable.java

public interface Moveable {
  
void move();
}

Car.java

接口的實現類。

public class Car implementsMoveable {
  
@Override
  
public void move() {
     
try {
         Thread.sleep(
new Random().nextInt(1000));
        
System.out.println("汽車行駛中...");
     
} catch (InterruptedException e) {
         e.printStackTrace()
;
     
}
   }
}

CarLogProxy.java

記錄日誌代理,實現Moveable接口

public class CarLogProxy implements Moveable {

  
public CarLogProxy(Moveable m) {
     
super();
      this
.m = m;
  
}

  
private Moveable m;

  
@Override
  
public void move() {
      System.
out.println("日誌開始....");
     
m.move();
     
System.out.println("日誌結束....");
  
}

}

CarTimeProxy.java

記錄行駛時間代理,實現Moveable接口。

public class CarTimeProxy implements Moveable {

  
public CarTimeProxy(Moveable m) {
     
super();
      this
.m = m;
  
}

  
private Moveable m;

  
@Override
  
public void move() {
     
long startTime = System.currentTimeMillis();
     
System.out.println("汽車開始行駛....");
     
m.move();
      long
endTime = System.currentTimeMillis();
     
System.out.println("汽車結束行駛....  汽車行駛時間:"
           
+ (endTime - startTime) + "毫秒!");
  
}

}

Test.java

使用聚合的方式、實現同樣的接口來實現功能的擴充。

輸出:

汽車開始行駛....

日誌開始....

汽車行駛中...

日誌結束....

汽車結束行駛....  汽車行駛時間:147毫秒!

public class Test {

  
public static void main(String[] args) {
      Car car =
new Car();
     
CarLogProxy clp = new CarLogProxy(car);
     
CarTimeProxy ctp = new CarTimeProxy(clp);
     
ctp.move();
  
}

}

 

 

動態代理

Java動態代理類位於java.lang.reflect包下,一般主要涉及到以下兩個類:

1.     Interface InvocationHandler:該接口僅定義了一個方法

public object invoke(Object obj, Method method, Object[] args)在代理實例上處理方法調用並返回結果。第一個參數obj一般是指代理類,method是被代理的方法,args爲該方法的參數數組。這個抽象方法在代理類中動態實現。

2.     Proxy:該類即爲動態代理類

static ObjectnewProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)返回代理類的一個實例,返回後的代理類可以當作被代理類使用(可使用被代理類的在接口中聲明過的方法)。

 

動態代理實現步驟:

1.     創建一個實現接口InvocationHandler的類,必須實現invoke方法

2.     創建被代理的類以及接口

3.     調用Proxy的靜態方法newProxyInstance創建一個代理類

4.     通過代理調用方法。

上面的栗子稍作修改:

Moveable.java

這裏再多聲明一個方法fun,用來測試

public interface Moveable {
  
void move();
   void
fun(String s);
}

Car.java

Moveable接口的一個實現類

public class Car implementsMoveable {

  
@Override
  
public void move() {
     
try {
         Thread.sleep(
new Random().nextInt(1000));
        
System.out.println("汽車行駛中...");
     
} catch (InterruptedException e) {
         e.printStackTrace()
;
     
}
   }

  
@Override
  
public void fun(String s) {
      System.
out.println("fun方法+"+s);
  
}

}

TimeHandler.java

實現InvocationHandler接口,實現invoke方法。該方法在代理實例上處理方法調用並返回結果,被代理類的任何一個方法的調用都會被該代理處理,下面使用接口中的兩個方法進行測試。

public class TimeHandler implements InvocationHandler {

  
public TimeHandler(Object target) {
     
super();
      this
.target = target;
  
}

  
private Object target;

  
/*
    *
參數:
    * proxy 
代理對象
    * method 
被代理對象的方法
    * args
方法的參數
    *
    *
返回值:
    * Object 
方法的返回值
    * */
  
@Override
  
public Object invoke(Object proxy, Method method, Object[] args)
        
throws Throwable {
     
long starttime = System.currentTimeMillis();
     
System.out.println("汽車開始行駛....");
      if
(args!=null)
          System.
out.println(method.getName()+args.length);
      else
         
System.out.println(method.getName());
     
method.invoke(target,args);
      long
endtime = System.currentTimeMillis();
     
System.out.println("汽車結束行駛....  汽車行駛時間:"
           
+ (endtime - starttime) + "毫秒!");
      return null;
  
}

}

Test.java

測試類,輸出:

汽車開始行駛....

move

汽車行駛中...

汽車結束行駛....  汽車行駛時間:18毫秒!

汽車開始行駛....

fun1

fun...123

汽車結束行駛....  汽車行駛時間:0毫秒!

從上面的輸出可以看出代理類可以實現並修改被代理類的一些功能,並且每個方法都是通過invoke來通過反射機制執行。方法調用的對象,方法自身的Method對象,方法的參數,這些都可以獲得並進行處理。

 

public class Test {

  
/**
    * JDK
動態代理測試類
   
*/
  
public static void main(String[] args) {
      Car car =
new Car();
     
InvocationHandler h = new TimeHandler(car);
     
Class<?> cls = car.getClass();
     
/**
       * loader 
類加載器
       
* interfaces  實現接口
      
* h InvocationHandler
       */
     
Moveable m = (Moveable)Proxy.newProxyInstance(cls.getClassLoader(),
           
cls.getInterfaces(), h);
     
m.move();
     
m.fun("123");
  
}

}

若是添加多層代理,可以在測試類中繼續編寫,當然這裏要寫一個LogHandler的實現類,這裏就不貼代碼了,和TimeHandler類似。

//多添加一層代理
InvocationHandlerlogHandler = new LogHandler(m);
Moveable m2=(Moveable) Proxy.newProxyInstance(cls.getClassLoader(),
             
cls.getInterfaces(), logHandler);
m2.move();

 

也可以使用cglib實現動態代理,與JDK動態代理區別:

JDK動態代理只能代理實現了接口的類,而cglib是針對類來實現代理的,對指定目標類產生一個子類,通過方法攔截技術攔截所有父類方法的調用(不能對final修飾的類進行代理)。

 

總結

簡單來說,動態代理實現過程就是定義接口,定義代理類,拿到代理類的實例,通過代理類的實例調用被代理類的一些功能。這其中涉及到根據反射機制生成java代碼,編譯成字節碼,加載到內存,調用等。大概理解一下整體思路,對應上面的例子加深理解,必要時可以閱讀相關源碼,查看實現細節。

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