設計模式——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代碼,編譯成字節碼,加載到內存,調用等。大概理解一下整體思路,對應上面的例子加深理解,必要時可以閱讀相關源碼,查看實現細節。