Java程式中的代理作用和應用場景及實現

堅持╅信念★發表於2021-04-03

The role of proxy, application scenarios, and implementation in Java programs.

代理是程式設計和開發時頻繁使用的技術,可以提高程式靈活性和可擴充套件性。

1 代理作用

  1. 在不修改原始碼的基礎上,擴充套件和增強實現;
  2. 程式碼解耦,在代理中通過引數可以判斷真實類,做出不同的響應或呼叫,靈活方便;
  3. 隱藏部分實現過程和細節。

2 應用場景

java_proxy

ICar介面類,有一個drive方法,傳入speed(速度)行駛,現在已經有Mitsubishi(三菱車)和Bwm(寶馬車)實現了介面,隨著業務發展,可能有更多的車加入;程式穩健執行一段時間後,針對不同的車發現了不同的問題需要處理,比如Mitsubishi車drive前必須檢查和保證冷卻液充足;Bwm車drive前必須檢查和保證機油充足;由於改寫ICar介面可能影響到其它的車;如果有很多的車要處理,更改實現類可能需要花費大量時間,此時可以通過代理類擴充套件和增強。

java_proxy

在代理類(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。

參考文獻

相關文章