1. 程式人生 > >設計模式(4)代理模式

設計模式(4)代理模式

談及設計模式裡面的代理模式,不得不說,這個模式在網上有很多的應用場景。例如說,Spring裡面的代理機制,dubbo的動態代理模式生成proxy過程等等。
代理模式的概念:
代理模式是指使用代理物件來執行某一個物件所要執行的相應方法。

我們常說的代理模式主要劃分為了兩種型別:
靜態代理
所謂的靜態代理我的認識就是一種靈活性較差的代理設計。其基本的設計結構圖如下所示:
在這裡插入圖片描述
每次做代理的時候,都需要實現原來的介面方法,然後在建構函式中將相應的代理物件給注入進去。這樣子做起來顯得有點繁瑣。如果說我們不去實現它相應的介面的話,那麼這樣所帶來的效果就不是叫做代理了。(因為只能實現介面中的部分功能,並不夠完善)。
例如說以下案例程式碼:

/**
 * 作者:idea
 * 日期:2018/5/30
 * 描述:
 */
public interface Subject {
    public void request();
}

/**
 * 作者:idea
 * 日期:2018/5/30
 * 描述:代理物件
 */
public class ProxySubject implements Subject{

    public Subject subject;

    public ProxySubject(){
        this.subject=new RealSubject();
    }

    public void before(){
        System.out.println("this is before");
    }


    public void after(){
        System.out.println("this is after");
    }

    @Override
    public void request() {
        this.before();
        this.subject.request();
        this.after();
    }
}

/**
 * 作者:idea
 * 日期:2018/5/30
 * 描述:
 */
public class RealSubject implements Subject{


    @Override
    public void request() {
        System.out.println("請求-------");
    }

}

/**
 * 作者:idea
 * 日期:2018/5/30
 * 描述:
 */
public class Test {
    public static void main(String[] args) {
        Subject subject=new ProxySubject();
        subject.request(); //使用了代理物件之後,相應的請求會被橫向攔截
    }
}

動態代理
關於動態代理部分而言,我們常說的幾種型別分別就是cglib代理,還有jdk代理了。
舉個簡單的jdk代理模式程式碼案例:

/**
 * @author idea
 * @data 2018/11/11
 */
public interface Decode {

    String decode(String content);

}

/**
 * @author idea
 * @data 2018/11/11
 */
public class DecodeImpl implements Decode{


    @Override
    public String decode(String content){
        return content+"decode";
    }

}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 代理工廠
 *
 * @author idea
 * @data 2018/11/11
 */
public class InvocationFactory implements InvocationHandler {


    private Object target;

    public InvocationFactory(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理執行");
        Object value = method.invoke(target, args);
        return value;
    }

    public static void main(String[] args) {
        InvocationHandler invocationHandler = new InvocationFactory(new DecodeImpl());
        Decode decode = (Decode) Proxy.newProxyInstance(DecodeImpl.class.getClassLoader(), DecodeImpl.class.getInterfaces(), invocationHandler);
        System.out.println(decode.decode("asd"));
    }
}

通過上述的一個簡單案例可以先大致的瞭解什麼是jdk代理,以及jdk代理的基本實現步驟。使用jdk來進行代理的時候,要求被代理物件有相應的介面和介面實現類。在代理Proxy建立的時候需要獲取到相應目標物件的類載入器,以及介面,還有InvocationHandler的例項。在InvocationHandler裡面,對於invoke部分可以引用相應的物件控制代碼,然後進行方法的擴充套件實現,從而實現代理的效果。

cglib的代理機制程式碼案例:

/**
 * @author idea
 * @data 2018/11/11
 */
public class Car {

    public void test(){
        System.out.println("this is test");
    }
}


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author idea
 * @data 2018/11/11
 */
public class CarCglib implements MethodInterceptor {

    private Object target;

    public Object getTarget(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        //設定它的代理物件
        enhancer.setSuperclass(this.target.getClass());
        //回撥物件
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib執行代理");
        Object value = methodProxy.invokeSuper(o, objects);
        return value;
    }

    public static void main(String[] args) {
        Car car=new Car();
        CarCglib carCglib=new CarCglib();
        Car car2= (Car) carCglib.getTarget(car);
        car2.test();
    }
}

對於cglib而言,在執行代理的時候,可以動態地調整相應的返回值:

 public Object getTarget2(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        //設定它的代理物件
        enhancer.setSuperclass(this.target.getClass());
        //回撥物件
        enhancer.setCallback(new FixedValue() {
            @Override
            public Object loadObject() throws Exception {
                return "使用fixedvalue可以替換掉方法的返回值";
            }
        });
        return enhancer.create();
    }


    public Object getTarget3(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        //設定它的代理物件
        enhancer.setSuperclass(this.target.getClass());
        //回撥物件
        enhancer.setCallback(new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
                    return "Hello cglib!";
                } else {
                    return method.invoke(o, objects);
                }
            }
        });
        return enhancer.create();
    }

    public Object getTarget4(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        //設定它的代理物件
        enhancer.setSuperclass(this.target.getClass());
        //回撥物件
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
                    return "Hello cglib!";
                } else {
                    return methodProxy.invokeSuper(o, objects);
                }
            }
        });
        return enhancer.create();
    }

這裡面儘量少用InvocationHandler來充當回撥物件,這樣容易產生死迴圈現象的發生。

來看看下邊的一個比較圖:

cglib動態代理 jdk動態代理
cglib主要是通過和目標物件使用相同的一個父類,然後來實現相應的代理機制。這種機制並不要求目標物件要實現介面 Jdk的動態代理,是使用反射技術獲得類的載入器並且建立例項,根據類執行的方法在執行方法的前後傳送通知。

代理模式的應用場景也是蠻多的,例如說Spring裡面的AOP,Dubbo裡面的RPC呼叫,都有使用到代理模式的這種思想,因此感覺這塊的知識的也是蠻重要的。