設計模式-代理模式(07)

 結構型模式

  大概學習完了五種建立型模式,分別是單例模式,工廠方法模式,抽象方法模式,建造者模式,原型模式.下面要學習的是七種結構型模式,結構型模式(Structural Pattern)描述如何將類或者對象結合在一塊兒造成更大的結構.結構型模式的目的是經過組合類或對象產生更大的結構以適應更高層次的邏輯需求,它包括:代理模式,裝飾模式,適配器模式,組合模式,橋樑模式,外觀模式,享元模式。html

代理模式

定義

  代理模式(Proxy Pattern)也叫委託模式,是一種使用率很是高的模式。英文原話是:Provide a surrogate or placeholder for another object to control access to it.意思是:爲其餘對象提供一種代理以控制對這個對象的訪問java

  代理模式是一項基本的設計技巧,許多模式如狀態模式、策略模式、訪問者模式本質上也採用了代理模式。git

角色分類

  1.抽象主題(Subject)角色:該角色是真實主題和代理主題的共同接口,以便在任何可使用真實主題的地方均可以使用代理主題。github

  2.代理主題(Proxy Subject)角色:也叫爲委託類、代理類,該角色負責控制類對真實主題的引用,負責在須要的時候建立或刪除真實主題對象,而且在真實主題角色處理完畢先後作預處理和藹後處理工做。spring

  3.真實主題(Real Subject)角色:該角色也叫被委託角色或被代理角色,是業務邏輯的具體執行者。編程

/**
 * 主題角色 
 * 真實和代理的共同接口,能夠在任何使用真實主題的地方使用代理主題.
 */
public interface Subject {
    //定義一個請求方法
    public void request();
}

/**
 * 真實主題角色
 * 被代理角色,是業務邏輯的執行者 
 */
public class RealSubject implements Subject{
    @Override
    public void request() {
        //業務邏輯處理
        System.out.println("真實主題角色處理中...");
    }
}

/**
 * 代理主題角色
 * 在須要的時候建立或刪除真實對象,而且在真實主題角色處理完畢先後作預處理和藹後處理工做.
 */
public class ProxySubject implements Subject{
    private Subject subject;
    
    //實例化的時候把須要代理的對象傳進來
    public ProxySubject(Subject subject) {
        this.subject = subject;
    }

    //代理後的方法執行內容
    @Override
    public void request() {
        this.beforeRequest();
        subject.request();
        this.afterRequest();
    }
    
    //請求前的操做
    public void beforeRequest(){
        //預處理
        System.out.println(subject.getClass()+"被代理了,在它執行以前要執行預處理方法");
    }
    //請求後的操做
    public void afterRequest(){
        //善後處理
        System.out.println(subject.getClass()+"被代理了,在它執行以後要執行善後方法");
    }
}

//調用
public class ProxyDemo {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        System.out.println("未代理前執行request請求:------------------------");
        subject.request();
        System.out.println("代理後執行request請求:--------------------------");
        subject = new ProxySubject(subject);
        subject.request();
    }
}

一個代理主題類能夠代理多個真實主題,具體代理哪一個真實主題由高層的應用模塊決定,代理的真實主題能夠經過代理類的構造函數傳遞。框架

代理模式的優勢 

  1.職責清晰,真實的角色只須要實現實際的業務邏輯,而不用關心其餘非本職的事務,附帶的結果就是編程簡潔清晰。dom

  2.高擴展性,具體主題角色隨需求不一樣可能有不少種,但只要實現了接口,代理類就徹底能夠在不作任何修改的狀況下代理各類真實主題角色。ide

  3.智能化,代理類能夠在運行時才肯定須要代理的真實主題,這是一種強大的功能。函數

代理模式的使用場景

  代理模式的應用很是普遍。大到一個系統框架、企業平臺,小到事務處理、代碼片斷,隨處可見代理模式的使用。例如,Java RMI的遠程調用就是一種代理模式的應用,如今流行的AOP也能夠經過代理模式實現.

其餘代理模式

上面的代理模式叫作靜態代理模式,動態地理能夠經過繼承和聚合實現,靜態代理能夠作到在不修改目標對象的功能前提下,對目標功能擴展。可是由於靜態代理代理對象須要與目標對象實現同樣的接口,因此會有不少代理類,類太多.同時,一旦接口增長方法,目標對象與代理對象都要維護.要解決這個問題可使用動態代理模式

  動態代理模式

  1.代理對象,不須要實現接口
  2.代理對象的生成,是利用JDK的API,動態的在內存中構建代理對象(須要咱們指定建立代理對象/目標對象實現的接口的類型)
  3.動態代理也叫作:JDK代理,接口代理.

java.lang.reflect.Proxy

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

  ClassLoader loader:指定當前目標對象使用的類加載器,獲取加載器的方法是固定的  clazz.getClassLoader()

  Class<?>[] interfaces:被代理對象實現的接口的類型,使用泛型方式確認類型  clazz.getInterfaces()

  InvocationHandler h:事件處理,執行目標對象的方法時,會觸發事件處理器的方法,會把當前執行目標對象的方法做爲參數傳入

 

/**
 * 車類接口
 */
public interface Moveable {
    public void run();
}

/**
 * 車類接口實現類
 */
public class Car implements Moveable {
    @Override
    public void run() {
        try {
            Thread.sleep(new Random().nextInt(3*1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("car is running...");
    }
}

/**
 * 代理以後的處理方法-處理時間記錄
 */
public class TimeInvocationHandler implements InvocationHandler {
    private Object object;
    public TimeInvocationHandler(Object object) {
        this.object = object;
    }
    /* 
     * 用時代理
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("開始計算時間");
        long startTime = System.currentTimeMillis();
        method.invoke(object, args);
        long endTime = System.currentTimeMillis();
        System.out.println("計算時間完畢,共用時"+(endTime-startTime)+"毫秒");
        return null;
    }

}

/**
 * 代理後的處理方法-日誌記錄
 */
public class LogInvocationHandler implements InvocationHandler {
    private Object object;
    public LogInvocationHandler(Object object) {
        this.object = object;
    }
    /* 
     * 日誌代理
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("日誌開始記錄"+method.getName());
        method.invoke(object, args);
        System.out.println("日誌記錄完畢");
        return null;
    }
}

        //運行
    public static void main(String[] args) {
        Car car = new Car();
        Class<?> clazz = car.getClass();
        //日誌代理
        InvocationHandler logHandler = new LogInvocationHandler(car);
        Moveable mv = (Moveable)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), logHandler);
        //用時代理
        InvocationHandler timeHandler = new TimeInvocationHandler(mv);
        Moveable mv2 = (Moveable)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), timeHandler);
        //運行run方法
        mv2.run();
    }

 

運行結果:

/**
 * 建立動態代理對象
 * 動態代理不須要實現接口,可是須要指定接口類型
 */
public class DynamicProxy {
    private Object target;
    public DynamicProxy(Object target) {
        this.target = target;
    }
    
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(RealSubject.class.getClassLoader(), RealSubject.class.getInterfaces(), 
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        beforeRequest();
                        Object returnValue = method.invoke(target, args);  //目標對象方法
                        afterRequest();
                        return returnValue;
                    }
                });
    }
    
    //請求前的操做
    public void beforeRequest(){
        //預處理
        System.out.println(target.getClass()+"被動態代理了,在它執行以前要執行動態代理加入的預處理方法");
    }
    //請求後的操做
    public void afterRequest(){
        //善後處理
        System.out.println(target.getClass()+"被動態代理了,在它執行以後要執行動態代理加入的善後方法");
    }
}

public class DynamicProxyDemo {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        System.out.println(subject.toString()+"執行方法subject.request()結果爲");
        subject.request();
        System.out.println("--------------------------------------");
        subject = (Subject) new DynamicProxy(subject).getProxyInstance();
        System.out.println("代理類"+subject.toString()+"執行方法request()方法結果爲");
        subject.request();
    }
}

執行後發現結果居然是這個

這是由於System.out.println("代理類"+subject.toString()+"執行方法request()方法結果爲");這行執行了toString()方法,toString()方法一樣被代理了.這裏有個疑問就是代理類的地址和被代理類的地址同樣,執行request()方法結果卻不同,若是哪位牛人知道的話,歡迎評論.

  Cglib代理

  靜態代理和動態代理模式都是要求目標對象是實現一個接口的目標對象,可是有時候目標對象只是一個單獨的對象,並無實現任何的接口,這個時候就可使用以目標對象子類的方式類實現代理,這種方法就叫作:Cglib代理  

Cglib代理,也叫做子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展.

  1. JDK的動態代理有一個限制,就是使用動態代理的對象必須實現一個或多個接口,若是想代理沒有實現接口的類,就可使用Cglib實現.
  2. Cglib是一個強大的高性能的代碼生成包,它能夠在運行期擴展java類與實現java接口.它普遍的被許多AOP的框架使用,例如Spring AOP和synaop,爲他們提供方法的interception(攔截)
  3. Cglib包的底層是經過使用一個小而塊的字節碼處理框架ASM來轉換字節碼並生成新的類.不鼓勵直接使用ASM,由於它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉.

實現

1.引入cglib的jar文件,Spring的核心包中已經包括了Cglib功能,因此直接引入spring-core-4.2.3.RELEASE.jar便可.
2.引入功能包後,就能夠在內存中動態構建子類
3.代理的類不能爲final,不然報錯
4.目標對象的方法若是爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法.

public class Train {
    public void move(){
        System.out.println("火車正在行駛...");
    }
}

public class CglibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    
    public Object getProxy(Class clazz){
        //設置父類
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        //建立子類
        return enhancer.create();
    }
    
    /**
     * 攔截全部目標類方法的調用
     * obj  目標類的實例
     * m   目標方法的反射對象
     * args  方法的參數
     * proxy 代理類的實例
     */
    @Override
    public Object intercept(Object obj, Method m, Object[] args,
            MethodProxy proxy) throws Throwable {
        System.out.println("開始記錄到日誌...");
        proxy.invokeSuper(obj, args);
        System.out.println("日誌記錄完畢.");
        return null;
    }

}

    /**
     * 測試
     */
    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
        Train t = (Train)proxy.getProxy(Train.class);
        t.move();
    }

運行結果:

public class ProxyFactory implements MethodInterceptor {
    //維護目標對象
    private Object target;
    public ProxyFactory(Object target) {
        this.target = target;
    }
    //給目標對象建立一個代理對象
    public Object getProxyInstance(){
        //1.工具類
        Enhancer enhancer = new Enhancer();
        //2.設置父類
        enhancer.setSuperclass(target.getClass());
        //3.設置調用函數
        enhancer.setCallback(this);
        //4.建立子類(代理對象)
        return enhancer.create();
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {
        beforeRequest();
        //執行目標對象的方法
        Object returnvalue = method.invoke(target, args);
        afterRequest();
        return returnvalue;
    }
    
    //請求前的操做
    public void beforeRequest(){
        //預處理
        System.out.println(target.getClass()+"被代理了,在它執行以前要執行預處理方法");
    }
    //請求後的操做
    public void afterRequest(){
        //善後處理
        System.out.println(target.getClass()+"被代理了,在它執行以後要執行善後方法");
    }
}

/*
 * 目標對象
 * 沒有實現任何接口
 */
public class RealSubject {
    public void request() {
        System.out.println("----被代理對象請求方法!----");
    }
}

public class CglibDemo {
    public static void main(String[] args) {
        RealSubject target = new RealSubject();
        RealSubject proxy = (RealSubject) new ProxyFactory(target).getProxyInstance();
        proxy.request();
    }
}

源碼

參考文章:Java的三種代理模式

相關文章
相關標籤/搜索