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