1. 程式人生 > >代理模式

代理模式

implement ndt 內部 cti rri 但是 llb void client

概述:

代理模式,提供了對目標對象另外的訪問方式。簡單講在不改變目標對象的提前下,為其添加額外功能以供其他對象使用。而對於開發人員來講,其實就是不改變原有的代碼,對相應功能進行擴展,比如限制對原有代碼的訪問權限,記錄原有代碼的執行時間,對運行過的代碼寫日誌.....

代理模式有靜態代理和動態代理。其關鍵點是,代理對象與目標對象、代理對象是對目標對象的擴展,並調用目標對象。

代理模式的角色:

抽象對象:聲明了目標對象和代理對象的共同接口,這樣一來在任何可以使用目標對象的地方都可以使用代理對象。

目標對象:定義了代理對象所代表的目標對象。

代理對象:代理對象內部含有目標對象的引用,從而可以在任何時候操作目標對象。

一、靜態代理

靜態代理有聚合和繼承兩種實現方式。下面我們以“一只程序猿在寫代碼為例子”,分別為其記錄日誌和時間。

1、繼承實現方式:

//抽象對象:
public interface People {
    public void code();
}
//目標對象
public class Programmer implements People {
    @Override
    public void code() {
        System.out.println("我在寫代碼");
    }
}
//繼承代理  
public class TimeProxy extends
Programmer { public void proxy(){ System.out.println("time Strat"); super.code(); System.out.println("time end"); } }

此時我們對程序猿的代理進行執行:

public class Client {
    public static void main(String[] args) {
        TimeProxy timpProxty = new TimeProxy();    
        timpProxty.proxy();    
    }
}

輸出結果:

技術分享

這裏代理模式(繼承)就實現了,但這裏有一個問題如果我要先記錄對這只程序猿的行為的時間,然後為他記錄日誌。此時我們的代碼可能是

public class LogAndTimeProxy extends  Programmer {
    public void logAndTimeProxy(){
        System.out.println("time start");
        System.out.println("log start");
        super.code();
    }    
}

但是如果需要先記錄日誌再記錄時間,那此時就要修改原有的代理或者新增代理類,這種做法代碼的實現不靈活。我們應該要做到分別定義一個時間代理,一個日誌代理,不管時間、日誌誰先誰後,都不需要去修改原有的代理或許添加新代理類

2、聚合實現方式:

//抽象對象:
public interface People {
    public void code();
}
//目標對象
public class Programmer implements People {
    @Override
    public void code() {
        System.out.println("我在寫代碼");
    }
}
//時間代理類(聚合)
public class TimeProxy implements People {
    private People people;
    
    public TimeProxy(People people){
        this.people=people;
    }

    @Override
    public void code() {
        System.out.println("time start");
        people.code();
        System.out.println("time end");
    }
}
//日誌代理類(聚合)
public class LogProxy  implements People {
    private People people;
    
    public LogProxy(People people){
        this.people=people;
    }
    
    @Override
    public void code() {
        System.out.println("logging start");
        people.code();
        System.out.println("logging end");
    }
}
//如果我們要先記錄時間,後記錄日誌實現方式
public class Client {
    public static void main(String[] args) {
        People p = new Programmer();
        TimeProxy timpProxty = new TimeProxy(p);
        LogProxy logProxty = new LogProxy(timpProxty);
        logProxty.code();    
    }
}

執行結果:

技術分享

如果要實現新記錄日誌再記錄時間

public class Client {
    public static void main(String[] args) {
        People p = new Programmer();
        LogProxy logProxty = new LogProxy(p);    
        TimeProxy timpProxty = new TimeProxy(logProxty);
        timpProxty.code();    
    }
}

結果:

技術分享

這樣就可以不用修改代理類的情況下,更改代理類了。

但這裏有一個問題就是每個代理類要繼承一個接口或者實現一個類。如果一個項目比較大的話,那就需要添加很多的類,一旦接口增加方法,目標對象與代理對象都要維護。工作量會很大。

二、動態代理

動態代理有兩種實現方法,分別是jdk代理和Cglib代理。

Jdk代理主要實現Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)。其中ClassLoader 表示當前目標對象使用的類加載器,Class<?>表示目標對象實現的接口的類型,InvocationHandler 表示事件處理,當執行目標對象是會觸發該方法。

這裏同樣使用“一只程序猿在寫代碼,休息中...”為例子

//抽象對象:
public interface DynamicPeople {
    public void codeing();
    public void doOtherThing(String things);
}
//目標對象
public class DynamicProgrammer implements DynamicPeople {
    @Override
    public void codeing() {
        System.out.println("我在寫代碼...");
    }
    @Override
    public void doOtherThing(String things) {
        System.out.println(things+"...");
    }
}
//動態代理類
public class JdkDynamicProxy {
    public Object object;
    
    public JdkDynamicProxy(Object object){
        this.object=object;
    }

    //獲取一個代理
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(
                object.getClass().getClassLoader(), 
                object.getClass().getInterfaces(), 
                new InvocationHandler(){
                    @Override
                 //這裏proxy表示代理目標,methos表示方法,args表示參數
                    public Object invoke(Object proxy, Method method,
                            Object[] args) throws Throwable {
                        System.out.println("寫日誌");
                        //執行對應的方法
                        Object result =method.invoke(object, args);
                        return result;
                    }                
                });
    }
}

調用程序猿的方法,看看代理效果:

public class DynamicClient {
    public static void main(String[] args) {
        DynamicPeople programmer = new DynamicProgrammer();
        DynamicPeople proxy = (DynamicPeople) new JdkDynamicProxy(programmer).getProxyInstance();
        
        proxy.codeing();
        System.out.println("*************");
        proxy.doOtherThing("休息中");
    }
}

執行結果

技術分享

可見動態代理已經成功了。但這時我們需要實現上面所說的“分別定義一個時間代理,一個日誌代理,不管時間、日誌誰先誰後,都不需要去修改原有的代理或許添加新代理類”。這些我們需要改下newProxyInstance的InvocationHandler。我們分別定義時間處理類DynamicTimeHandler和日誌處理類DynamicLogHandler,兩者都繼承InvocationHandler,代碼如下:

public class DynamicTimeHandler implements InvocationHandler {
    //目標對象
    private Object object;
    public DynamicTimeHandler(Object object){
        this.object=object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {    
        System.out.println("開始時間");
        Object result = method.invoke(object, args);
        return result;
    }
}
public class DynamicLogHandler implements InvocationHandler {
    private Object object;
    public DynamicLogHandler(Object object){
        this.object=object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("開始日誌");
        Object result = method.invoke(object, args);
        return result;
    }
}

修改代理類:

public class JdkDynamicProxy {
    public Object object;
    public InvocationHandler h;
    
    public JdkDynamicProxy(Object object,InvocationHandler h){
        this.object=object;
        this.h = h ;
    }

    //獲取一個代理
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(
                object.getClass().getClassLoader(), 
                object.getClass().getInterfaces(), 
                h);
    }
}

調用程序猿的方法,看看代理效果

public class DynamicClient {
    public static void main(String[] args) {
        DynamicPeople programmer = new DynamicProgrammer();
        InvocationHandler time = new DynamicTimeHandler(programmer);
        DynamicPeople timeProxy = (DynamicPeople) new JdkDynamicProxy(programmer,time).getProxyInstance();
        
        InvocationHandler log  = new DynamicLogHandler(timeProxy);
        DynamicPeople logProxy = (DynamicPeople) new JdkDynamicProxy(programmer,log).getProxyInstance();
        
        logProxy.codeing();
        System.out.println("*************");
        logProxy.doOtherThing("休息中");    
    }
}

執行結果:

技術分享

這樣就實現了上面所說的效果了。但jdk的實現限制,就是目標對象必須實現一個接口。這個缺陷,我們用Cglib代理是可以解決的(Cglib在Spring的核心包中)。

這裏我們以“開車去看美女”為例子

//定義一個目標對象汽車類
public class Car {
    public void running(){
        System.out.println("開著汽車...");
    }
    public void doSomething(String things){
        System.out.println("開著汽車"+things);
    }
}
//寫代理方法
public class CglibDynamicProxy implements MethodInterceptor{
    public Object b;
    
    public CglibDynamicProxy(Object b){
        this.b=b;
    }
    
    public Object getProxyInstance(){
        //1.工具類
        Enhancer en = new Enhancer();
        //2.設置父類
        en.setSuperclass(b.getClass());
        //3.設置回調函數
        en.setCallback(this);
        //4.創建子類(代理對象)
        return en.create();

    }

    @Override
    public Object intercept(Object arg0, Method method, Object[] arg2,
            MethodProxy arg3) throws Throwable {
            System.out.println("開始代理...");
            //執行目標對象的方法
            Object returnValue = method.invoke(b, arg2);
            return returnValue;
    }
}

調用方法,看一下執行效果

public class DynamicClient {
    public static void main(String[] args) {
        Car car = new Car();
        Car newCar= (Car) new CglibDynamicProxy(car).getProxyInstance();
        newCar.doSomething("看美女");
        newCar.running();
    }    
}

執行結果

技術分享

對於如何“分別定義一個時間代理,一個日誌代理,不管時間、日誌誰先誰後,都不需要去修改原有的代理或許添加新代理類”還沒研究,等以後補上,O(∩_∩)O

代理模式