java中的代理模式

代理模式的原理

使用一個代理將對象包裝起來, 而後用該代理對象取代原始對象。任何對原始對象的調用都要經過代理。代理對象決定是否以及什麼時候將方法調用轉到原始對象上。java

簡單例子:api

小強意外成爲了一個網紅明星,此時他請了一個專業的經紀人進行各類事務的管理,小強的演出活動啊都得通過經紀人的贊成。此時小強是被代理對象,經紀人是代理對象。數組

此時快樂大本營想請小強去參加節目,須要找到小強的經紀人,小強的經紀人而後進行排擋,找小強去完成唱歌,才藝展現等動做。ide

靜態代理

代理對象和被代理對象(目標對象)實現同一接口。優勢是不用修改目標對象的源碼,擴展其功能。測試

特色

  • 在編譯期間,代理類和被代理對象(目標對象)的類都肯定下來了,不利於程序的擴展
  • 每個代理類只能爲一個接口服務,這樣一來程序開發中必然產生過多的代理。

舉例

建立一個接口,這個接口是代理工廠(代理類)和Nike工廠(被代理類)的公共接口,this

經過代理工廠的方法調用同時也調用了Nike工廠的同名方法,完成不修改被代理類的源碼而擴展其功能。.net

共同接口代理

interface ClothFactory{
    void produceCloth();
}

代理類code

// 代理類
class ProxyClothFactory implements ClothFactory {
    private ClothFactory factory;//用被代理類對象進行實例化

    public ProxyClothFactory(ClothFactory factory) {
        this.factory = factory;
    }

    @Override
    public void produceCloth() {
        System.out.println("代理工廠作一些準備工做");
        factory.produceCloth();
        System.out.println("代理工廠作一些後續的工做");
    }
}

被代理類對象

// 被代理類
class NikeClothFactory implements ClothFactory{

    @Override
    public void produceCloth() {
        System.out.println("nike工廠生產運動服");
    }
}

測試代碼

public class StaticProxyTest {
    public static void main(String[] args) {

        // 建立被代理類對象
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        // 建立代理類對象
        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nikeClothFactory);
        proxyClothFactory.produceCloth();
    }
}

輸出結果

代理工廠作一些準備工做
nike工廠生產運動服
代理工廠作一些後續的工做

動態代理

動態代理是指客戶經過代理類來調用其它對象的方法,而且是在程序運行時根據須要動態建立目標類的代理對象。

特色

代理對象不須要實現接口,可是要求被代理對象(目標對象)必須實現接口,不然不能使用動態代理。

相關API

Proxy:專門完成代理的操做類,是全部動態代理類的父類。經過此類爲一個或多個接口動態地生成實現類。

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)直接建立一個動態代理對象

參數詳解:

  • ClassLoader loader:和被代理對象使用相同的類加載器
  • Class<?>[] interfaces:和被代理對象具備相同的行爲,實現相同的接口
  • InvocationHandler h:獲得InvocationHandler接口的實現類實例

實現動態代理

實現動態代理,要解決的問題

  1. 如何根據加載到內存中的被代理類,動態建立一個代理類及其對象;
  2. 當經過代理類的對象調用方法時,如何動態的去調用被代理類中的同名方法

具體步驟以下:

  1. 建立被代理類和接口

    接口:Human接口

    //共同接口
    interface Human{
        String getBelief();
        void eat(String food);
    }

    被代理類:SuperMan類

    // 被代理類
    class SuperMan implements Human{
    
        @Override
        public String getBelief(){
            return "我能飛";
        }
        @Override
        public void eat(String food){
            System.out.println("我喜歡吃"+food);
        }
    }
  2. 建立一個用來生產代理類的工廠,該工廠有一個靜態方法getProxyInstance能夠返回一個代理類的對象,該方法需傳入被代理類對象。

    class ProxyFactory{
        // 調用此方法,返回一個代理類的對象。須要傳入被代理類對象
        public static Object getProxyInstance(Object obj){
    
            MyInvocationHandler handler = new MyInvocationHandler();
            handler.bind(obj);
            // 哪一個類加載的?被代理類
            // 被代理類實現了哪一個接口?獲得被代理類實現的所有接口
            // 獲得InvocationHandler接口的實現類實例
            return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
        }
    }
  3. 建立一個實現接口InvocationHandler的類,實現inoke方法,以完成代理的具體操做。當咱們經過代理類的對象調用方法a時,就會自動的調用以下方法invoke(),將被代理類要執行的方法a的功能聲明在invoke()中。

    class MyInvocationHandler implements InvocationHandler{
        private Object obj;//須要使用被代理類的對象進行賦值
        public void bind(Object obj){
            this.obj = obj;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // obj:被代理類對象
            // method:即爲代理類對象調用的方法,此方法做爲被代理類對象要調用的方法
            // args: 方法調用時所須要的參數
            Object returnValue = method.invoke(obj,args);
            //該返回值做爲當前類中的invoke()的返回值
            return returnValue;
        }
    }
  4. 測試類

    public class ProxyTest {
        public static void main(String[] args) {
            SuperMan superMan = new SuperMan();//建立一個被代理類對象
            // 動態建立代理類
            Human proxyInstance = (Human)ProxyFactory.getProxyInstance(superMan);
    
            String belief = proxyInstance.getBelief();
            System.out.println(belief);
            proxyInstance.eat("肉夾饃");
        }
    }
  5. 輸出結果

咱們發現,只須要知道接口和被代理類,就能實現建立一個代理類。把上面靜態代理部分的例子拿過來

NikeClothFactory nike = new NikeClothFactory();
ClothFactory proxyInstance1 = (ClothFactory) ProxyFactory.getProxyInstance(nike);
proxyInstance1.produceCloth(); //nike工廠生產運動服

動態代理實現兩種方式

動態代理

特色:字節碼隨用隨建立,隨用隨加載;
做用:不修改源碼的基礎上對方法加強。

基於接口的動態代理

提供者: JDK 官方的 Proxy 類。
要求:被代理類最少實現一個接口。

實現步驟

  1. 建立一個接口,該接口能夠理解成對生產廠家的規範

    /**
     * 對生產廠家要求的接口
     */
    public interface IProducer {
    
        /**
         * 銷售
         * @param money
         */
        public void saleProduct(float money);
    
        /**
         * 售後
         * @param money
         */
        public void afterService(float money);
    }

    代理類:是一個生產者,它實現了接口,具備接口中的方法,也就是說符合生產廠家的要求,產品銷售和售後是ok的。

    /**
     * 一個生產者
     */
    public class Producer implements IProducer{
        /**
         * 銷售
         * @param money
         */
        public void saleProduct(float money){
            System.out.println("銷售產品,並拿到錢:"+money);
        }
        /**
         * 售後
         * @param money
         */
        public void afterService(float money){
            System.out.println("提供售後服務,並拿到錢:"+money);
        }
    }
  2. 模擬一個消費者,若是該消費者若是沒有經過代理商去買電腦,而是直接去到廠家處購買。但現實中,不多有人這樣購買。通常都是廠家把產品交給代理商進行銷售以及提供售後服務,顧客只需跟代理商進行交易,而不直接與廠家進行聯繫。

    沒有代理以前

    Producer producer1 = new Producer();
    producer.saleProduct(10000f);//銷售產品,並拿到錢:10000.0

    有了代理,代理要從中提取20%的手續費。那麼生產者只能獲得80%的錢

    public class Client {
        public static void main(String[] args) {
            final Producer producer = new Producer();
            IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                    producer.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            //提供加強的代碼
                            Object returnValue = null;
    
                            //1.獲取方法執行的參數
                            Float money = (Float)args[0];
                            //2.判斷當前方法是否是銷售
                            if("saleProduct".equals(method.getName())) {
                                returnValue = method.invoke(producer, money*0.8f);
                            }
                            return returnValue;
                        }
                    });
            proxyProducer.saleProduct(10000f);
        }
    }

    涉及的參數:

    Proxy類

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
    ClassLoader:類加載器 代理誰寫誰的類加載器,它是用於加載代理對象字節碼的。和被代理對象使用相同的類加載器。固定寫法。
    Class[]:字節碼數組,它是用於讓代理對象和被代理對象有相同方法。固定寫法。
    InvocationHandler:用於提供加強的代碼
    它是讓咱們寫如何代理。咱們通常都是些一個該接口的實現類,一般狀況下都是匿名內部類,但不是必須的。此接口的實現類都是誰用誰寫。

    InvocationHandler接口

    public Object invoke(Object proxy, Method method, Object[] args) 
    做用:執行被代理對象的任何接口方法都會通過該方法
    方法參數的含義
    proxy   代理對象的引用
    method  當前執行的方法
    args    當前執行方法所需的參數

基於子類的動態代理

提供者:第三方的 cglib。
要求:被代理類不能是最終類(用 final 修飾的類)。

涉及的類:Enhancer

如何建立代理對象:使用Enhancer類中的create(Class,Callback)方法

create方法的參數:

Class:字節碼,它是用於指定被代理對象的字節碼。

Callback:如何代理,即就是用於提供加強的代碼。咱們通常都是寫一個該接口的實現類,一般狀況下都是匿名內部類,但不是必須的。此接口的實現類都是誰用誰寫。咱們通常寫的都是該接口的子接口實現類:MethodInterceptor

仍是消費者買產品的例子,只不過不讓它實現接口。

舉例:

生產者

/**
 * 一個生產者
 */
public class Producer {

    /**
     * 銷售
     * @param money
     */
    public void saleProduct(float money){
        System.out.println("銷售產品,並拿到錢:"+money);
    }

    /**
     * 售後
     * @param money
     */
    public void afterService(float money){
        System.out.println("提供售後服務,並拿到錢:"+money);
    }
}

消費者

/**
 * 模擬一個消費者
 */
public class Client {

    public static void main(String[] args) {
        final Producer producer = new Producer();
        Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * 執行被代理對象的任何方法都會通過該方法
             * @param proxy
             * @param method
             * @param args
             *    以上三個參數和基於接口的動態代理中invoke方法的參數是同樣的
             * @param methodProxy :當前執行方法的代理對象
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //提供加強的代碼
                Object returnValue = null;

                //1.獲取方法執行的參數
                Float money = (Float)args[0];
                //2.判斷當前方法是否是銷售
                if("saleProduct".equals(method.getName())) {
                    returnValue = method.invoke(producer, money*0.8f);
                }
                return returnValue;
            }
        });
        cglibProducer.saleProduct(12000f);
    }
}

參考連接:

http://www.javashuo.com/article/p-dwzqgqkp-a.html

相關文章
相關標籤/搜索