設計模式之代理模式

代理模式

  • 作用:在不改變原有程式碼的基礎上,增加或擴展額外的功能
  • 靜態代理
//1,被代理類需要實現的介面,代理類也要實現該介面,該介面對用戶使用
interface FlightMachine{
    void fly();
}
//2,被代理類實現介面
class FlightMachineImpl implements FlightMachine{

    @Override
    public void fly() {
        System.out.println("我可以飛");
    }
}
//3,代理類實現介面
class FlightProxy implements FlightMachine{
    //4,代理類引用被代理對象
    private FlightMachine flightMachine;

    //5,寫有參構造方法得到被代理對象實例
    public FlightProxy(FlightMachine flightMachine){
        super();
        this.flightMachine=flightMachine;
    }

    @Override
    public void fly() {
        //增加額外的功能
        System.out.println("我可以發射導彈");
        flightMachine.fly();
        //增加額外的功能
        System.out.println("我可以航拍");
    }
}
public class TestProxy {
    public static void main(String[] args) {
        FlightMachine flightMachine = new FlightMachineImpl();
        FlightMachine flightProxy = new FlightProxy(flightMachine);
        flightProxy.fly();
    }
}

console:
我可以發射導彈
我可以飛
我可以航拍

小結:代理類和被代理類都需要實現同一個介面,且代理類預先設定,造成服務單一,要是其他類需要代理則需要更多代理類,不易擴展,缺點比較明顯,適用場景不廣。

  • 動態代理
    • JDK代理
      • 使用JDK自帶的代理類,製造代理工廠
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//1,被代理類實現的介面
interface FlightMachine{
    void fly();
}
//2,被代理類需要至少實現的一個介面
class FlightMachineImpl implements FlightMachine{
    @Override
    public void fly() {
        System.out.println("我可以飛");
    }
}
//3,創建動態代理的工廠類,用來動態生成代理類對象
class FlightProxyFactory{
    //4,需要引用被代理的對象
    private Object target;

    //5,寫有參構造方法得到代理目標實例
    public FlightProxyFactory(Object target) {
        this.target = target;
    }

    //6,提供一個獲取代理類對象的方法
    public Object getProxyInstance(){
        //用JDK自帶的reflect包的Proxy創建代理類實例
        /*
        @Proxy.newProxyInstance(Param1,Param2,Param3){ }
        參數1:類載入器,一般一個項目只有一個類載入器,所以選取比較方便獲取的類來getClassLoader()就行
        參數2:代理目標所實現的所有介面,通過.getClass().getInterfaces()獲得
        參數3:方法調用處理器
         */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), new InvocationHandler() {
                    /*
                    invoke(Param1,Param2,Param3){  }
                    參數1:代理類對象
                    參數2:被代理類(代理目標)需要執行的方法
                    參數3:執行方法需要的參數
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //增加額外的功能
                        System.out.println("我可以發射導彈");
                        //通過反射來調用代理目標方法
                        Object procedure = method.invoke(target, args);
                        //增加額外的功能
                        System.out.println("我可以航拍");
                        return procedure;
                    }
                });
    }

}

public class TestProxy {
    public static void main(String[] args) {
        FlightMachine flightMachine = new FlightMachineImpl();
        FlightProxyFactory flightProxyFactory = new FlightProxyFactory(flightMachine);
        Object proxyInstance = flightProxyFactory.getProxyInstance();
        if (proxyInstance instanceof FlightMachine){
            FlightMachine machineProxy = (FlightMachine) proxyInstance;
            machineProxy.fly();
        }
    }
}
console:
我可以發射導彈
我可以飛
我可以航拍

小結:建立代理工廠,根據需要的類動態生成代理類,雖然代理目標不固定,但是需要實現至少一個介面,不是所有的類都實現了介面或者繼承了某個類,所以還不夠好,但是JDK代理在反射生成代理類方面是作用較好,和其他動態代理方式配合使用效果更好。

  • CGLIB代理
    • 使用第三方代理包,cglib和asm兩個包需要引入,核心介面是MethodInterceptor,核心類是Enhancer,給被代理類動態生成一個子類來進行代理(子類對父類來說就相當於擴展了功能)
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;


//1,被代理類
class FlightMachineImpl {
    public void fly() {
        System.out.println("我可以飛");
    }
}
//2,創建cglib動態代理的工廠類,用來動態生成代理類、代理類對象,並實現MethodInterceptor介面
class CglibProxyInterceptor implements MethodInterceptor {
    //3,引用代理目標的對象
    private Object target;

    //4,寫有參構造方法得到代理目標實例
    public CglibProxyInterceptor(Object target) {
        this.target = target;
    }

    //5,提供一個獲取代理類對象的方法
    public Object getProxyInstance(){
        //6,用cglib包的Enhancer創建代理類,且該代理類需要繼承代理目標成為其子類
        Enhancer enhancer = new Enhancer();
        //7,將代理目標設置為代理類的父類
        enhancer.setSuperclass(target.getClass());
        //8,設置回調函數
        enhancer.setCallback(this);
        //9,創建代理類對象
        return enhancer.create();
    }
    //6,實現intercept方法
    /*
        @intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy){ }
        參數1 o:被代理的對象
        參數2 method:代理對象的方法,這裡是FlightMachineImpl的fly()方法
        參數3 objects:方法的參數
        參數4 methodProxy:方法代理,這裡代理的是cglib代理的fly()方法
         */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("我可以發射導彈");
        Object result = method.invoke(target, objects);
        System.out.println("我可以航拍");
        //methodProxy的invokeSuper可以實現相同的效果
//        Object result = methodProxy.invokeSuper(o, objects);
        return result;
    }
}

public class TestProxy {
    public static void main(String[] args) {
        CglibProxyInterceptor cglibProxyInterceptor = new CglibProxyInterceptor(new FlightMachineImpl());
        Object proxyInstance = cglibProxyInterceptor.getProxyInstance();
        if (proxyInstance instanceof FlightMachineImpl){
            FlightMachineImpl flightMachine = (FlightMachineImpl) proxyInstance;
            flightMachine.fly();
        }
    }
}
console:
我可以發射導彈
我可以飛
我可以航拍

小結:使用cglib代理,代理目標不用實現介面或繼承類,只需根據該被代理類生成子類作為其代理類完成代理,和JDK代理一起在spring aop中使用廣泛。


至此,若有紕漏,望各位不吝賜教