The role of proxy, application scenarios, and implementation in Java programs.
代理是程式設計和開發時頻繁使用的技術,可以提高程式靈活性和可擴充套件性。
1 代理作用
- 在不修改原始碼的基礎上,擴充套件和增強實現;
- 程式碼解耦,在代理中通過引數可以判斷真實類,做出不同的響應或呼叫,靈活方便;
- 隱藏部分實現過程和細節。
2 應用場景
ICar介面類,有一個drive方法,傳入speed(速度)行駛,現在已經有Mitsubishi(三菱車)和Bwm(寶馬車)實現了介面,隨著業務發展,可能有更多的車加入;程式穩健執行一段時間後,針對不同的車發現了不同的問題需要處理,比如Mitsubishi車drive前必須檢查和保證冷卻液充足;Bwm車drive前必須檢查和保證機油充足;由於改寫ICar介面可能影響到其它的車;如果有很多的車要處理,更改實現類可能需要花費大量時間,此時可以通過代理類擴充套件和增強。
在代理類(CarProxy)中通過carCheck方法實現對不同汽車檢查,檢查通過後drive汽車,從而實現對原有程式碼增強。
3 代理的實現
3.1 靜態代理
靜態代理的思想:代理類和其它汽車類一樣實現介面類,在重寫方法裡實現擴充套件或增強,並調要汽車類實現。
//寶馬汽車實現類
class Bwm implements ICar {
public void drive(int speed) {
System.out.println("Bwm drive with speed:"+speed);
}
//三菱汽車實現類
class Mitsubishi implements ICar {
public void drive(int speed) {
System.out.println("Mitsubishi drive with speed:"+speed);
}
//汽車代理類
public class CarProxy implements ICar {
private ICar carTarget;
public CarProxy(ICar carTarget){
this.carTarget=carTarget;
}
// 車輛檢查
private boolean carCheck() {
if(this.carTarget instanceof Mitsubishi){ //判斷汽車型別
System.out.println("Mitsubishi 檢查冷卻液完成。");
return true;
}else if(this.carTarget instanceof Bwm){
System.out.println("Bwm 檢查機油完成。");
return true;
}else{
System.out.println("該車不需要檢查。");
return true;
}
}
/** 駕駛
* @param speed 速度
*/
public void drive(int speed) {
if(carCheck()){ //檢查通過後drive
carTarget.drive(speed); //呼叫汽車類
}
}
}
在CarProxy代理類中,增加了對不同汽車的檢查方法(carCheck),當汽車檢查通過後呼叫(drive)方法駕駛汽車。這樣代理類便實現了對三菱和寶馬車的擴充套件,呼叫時傳入ICar實現類物件;
new CarProxy(new Mitsubishi()).drive(80); //drive 三菱車
new CarProxy(new Bwm()).drive(100); //drive 寶馬車
執行輸出;
Mitsubishi 檢查冷卻液完成。
Mitsubishi drive with speed:80
Bwm 檢查機油完成。
Bwm drive with speed:100
3.1.1 缺點
介面類變化會影響實現類和代理類;比如方法修改返回值、引數型別、增加方法,實現類和代理類都需要修改。
3.2 動態代理
Java 提供(java.lang.reflect.InvocationHandler)代理例項介面,代理類實現該介面關聯呼叫處理程式的方法(invoke),當在代理例項呼叫方法時,方法呼叫被編碼(encoded)並分配給其呼叫處理程式的方法。
public class DynamicProxy implements InvocationHandler{
Object carTarget;
//車輛檢查
private boolean carCheck() {
if(this.carTarget instanceof Mitsubishi){
System.out.println("Mitsubishi 檢查冷卻液完成。");
return true;
}else if(this.carTarget instanceof Bwm){
System.out.println("Bwm 檢查機油完成。");
return true;
}else{
System.out.println("該車不需要檢查。");
return true;
}
}
//建立代理類例項
Object crateProxyInstance(Object carTarget) {
this.carTarget = carTarget;
return Proxy.newProxyInstance(carTarget.getClass().getClassLoader(),
carTarget.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(carCheck()){//汽車檢查通過後drive
return method.invoke(carTarget,args);
}
return null;
}
}
除了crateProxyInstance 和 invoke 方法外,對三菱和寶馬車的擴充套件(車輛檢查)與靜態類相同。呼叫時同樣傳入ICar實現類物件。
((ICar)new DynamicProxy().crateProxyInstance(new Mitsubishi())).drive(80);//drive 三菱車
((ICar)new DynamicProxy().crateProxyInstance(new Bwm())).drive(80);//drive 寶馬車
3.3 cglib 代理
除了java自身的代理類,還有第三方代理類,比如(cglib),通過實現方法攔截器(MethodInterceptor),在攔截方法中(intercept)呼叫處理程式的方法。
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CarInterceptor implements MethodInterceptor{
Object carTarget;
//車輛檢查
private boolean carCheck() {
if(this.carTarget instanceof Mitsubishi){
System.out.println("Mitsubishi 檢查冷卻液完成。");
return true;
}else if(this.carTarget instanceof Bwm){
System.out.println("Bwm 檢查機油完成。");
return true;
}else{
System.out.println("該車不需要檢查。");
return true;
}
}
//攔截方法
@Override
public Object intercept(Object carTarget, Method method, Object[] args, MethodProxy proxy) throws Throwable {
this.carTarget=carTarget;
if(carCheck()){//汽車檢查通過後drive
Object obj=proxy.invokeSuper(carTarget, args);
return obj;
}
return null;
}
}
最後呼叫時,首先例項化(Enhancer)物件啟用方法攔截器,設定方法攔截類和Superclass;
Enhancer en=new Enhancer();
en.setCallback(new CarInterceptor());//設定攔截類
//drive 三菱車
en.setSuperclass(new Mitsubishi().getClass());//設定superClass
((ICar) en.create()).drive(80);
//drive 寶馬車
en.setSuperclass(new Bwm().getClass());
((ICar) en.create()).drive(100);
4 總結
靜態代理更像是開發層面的實現,每次修改介面類或實現類都可能需要修改代理類,隨著程式擴充套件,程式碼量和維護量增加;Java 提供的(java.lang.reflect.InvocationHandler)代理例項介面和第三方代理類(cglib)大大彌補了靜態類的不足,也應用在許多開源框架上,比如Spring。
參考文獻
- https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/InvocationHandler.html - Java Interface InvocationHandler
- https://github.com/cglib/cglib/wiki - cglib