1. 程式人生 > >《讀_Head_First_有感》_“代理模式_完結”

《讀_Head_First_有感》_“代理模式_完結”

 

轉載自我的朋友tjhuey

前言:
今天是設計模式我想說的最後模式————代理模式代理模式我認為是最重要的模式了,常用於開源框架底層封裝,如Spring,Mybatis,HDFS,RPC,xxl-job 等;主要是aop的思想,業務中常用日誌切面,介面訪問量,併發量控制,簡單的說是攔截相關請求做自己想做的事等,今天簡單寫個例子,並分享一下相關原始碼

今日主題

代理模式:概述:為其他物件提供一種代理可以控制這個物件的訪問

場景:

假設某些操作處理,需要其他事物執行,並在執行時動態的去擴充套件,可以採用。
在這裡,我簡單寫了個demo,模擬某些操作需要代理,其中計算操作代理,計算操作前後需要處理一些事情。

程式碼如下

'設計模式'程式碼

package com.huey.designpattern.dynamicproxy;

/**
* ┏┓   ┏┓
* ┏┛┻━━━┛┻┓
* ┃       ┃  
* ┃   ━   ┃
* ┃ ┳┛ ┗┳ ┃
* ┃       ┃
* ┃   ┻   ┃
* ┃       ┃
* ┗━┓   ┏━┛
* ┃   ┃ 神獸保佑        
* ┃   ┃ 程式碼無BUG!
* ┃   ┗━━━┓
* ┃       ┣┓
* ┃       ┏┛
* ┗┓┓┏━┳┓┏┛
* ┃┫┫ ┃┫┫
* ┗┻┛ ┗┻┛
 * @author huey
 * @Description : 代理操作
 * @Date Created in 2018/10/21 20:23
*/
public interface ProxyOperation<T> {

    /**
     * @author huey
     * @Description : 其他物件初始化
     * @Date Created in 2018/10/21 20:27
     */
    void init();
    /**
     * @author huey
     * @Description : 鉤子 是否計算了
     * @Date Created in 2018/10/21 20:25
     */
    boolean isCalc(boolean flag);

    /**
     * @author huey
     * @Description : 計算後,其他物件後置處理
     * @Date Created in 2018/10/21 20:27
     */
    void postProcessor();
}
package com.huey.designpattern.dynamicproxy;

/**
* ┏┓   ┏┓
* ┏┛┻━━━┛┻┓
* ┃       ┃  
* ┃   ━   ┃
* ┃ ┳┛ ┗┳ ┃
* ┃       ┃
* ┃   ┻   ┃
* ┃       ┃
* ┗━┓   ┏━┛
* ┃   ┃ 神獸保佑        
* ┃   ┃ 程式碼無BUG!
* ┃   ┗━━━┓
* ┃       ┣┓
* ┃       ┏┛
* ┗┓┓┏━┳┓┏┛
* ┃┫┫ ┃┫┫
* ┗┻┛ ┗┻┛
 * @author huey
 * @Description : 業務:計算
 * 實際目標物件介面
 * @Date Created in 2018/10/21 20:18
*/
public interface ProxyCalcOperation extends ProxyOperation {

     void calc();
}
package com.huey.designpattern.dynamicproxy;

import lombok.extern.slf4j.Slf4j;

/**
 * ┏┓   ┏┓
 * ┏┛┻━━━┛┻┓
 * ┃       ┃
 * ┃   ━   ┃
 * ┃ ┳┛ ┗┳ ┃
 * ┃       ┃
 * ┃   ┻   ┃
 * ┃       ┃
 * ┗━┓   ┏━┛
 * ┃   ┃ 神獸保佑
 * ┃   ┃ 程式碼無BUG!
 * ┃   ┗━━━┓
 * ┃       ┣┓
 * ┃       ┏┛
 * ┗┓┓┏━┳┓┏┛
 * ┃┫┫ ┃┫┫
 * ┗┻┛ ┗┻┛
 *
 * @author huey
 * @Description : 業務預設計算操作目標物件
 * @Date Created in 2018/10/21 20:21
 */
@Slf4j
public class ProxyCalcOperationDefault implements ProxyCalcOperation {

    @Override
    public void calc() {
        log.info("ProxyCalcOperationDefault --> {}","calc");
    }

    /**
     * @author huey
     * @Description : 其他物件初始化
     * @Date Created in 2018/10/21 20:27
     */
    @Override
    public void init() {
        log.info("ProxyCalcOperationDefault --> {}","init");
    }

    /**
     * @param flag
     * @author huey
     * @Description : 鉤子 是否計算了
     * @Date Created in 2018/10/21 20:25
     */
    @Override
    public boolean isCalc(boolean flag) {
        return false;
    }

    /**
     * @author huey
     * @Description : 計算後,其他物件後置處理
     * @Date Created in 2018/10/21 20:27
     */
    @Override
    public void postProcessor() {
        log.info("ProxyCalcOperationDefault --> {}","postProcessor");
    }
}
package com.huey.designpattern.dynamicproxy;

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

/**
 * ┏┓   ┏┓
 * ┏┛┻━━━┛┻┓
 * ┃       ┃
 * ┃   ━   ┃
 * ┃ ┳┛ ┗┳ ┃
 * ┃       ┃
 * ┃   ┻   ┃
 * ┃       ┃
 * ┗━┓   ┏━┛
 * ┃   ┃ 神獸保佑
 * ┃   ┃ 程式碼無BUG!
 * ┃   ┗━━━┓
 * ┃       ┣┓
 * ┃       ┏┛
 * ┗┓┓┏━┳┓┏┛
 * ┃┫┫ ┃┫┫
 * ┗┻┛ ┗┻┛
 *
 * @author huey
 * @Description : 代理簡單工具類
 * @Date Created in 2018/10/21 20:33
 */
public class ProxyOperationUtil {

    public static ProxyCalcOperation getProxy(ProxyOperation proxyOperation) {

        ClassLoader loader = proxyOperation.getClass().getClassLoader();

        Class<?>[] interfaces = proxyOperation.getClass().getInterfaces();

        //代理類執行的程式碼,lamda,聯想aop
        InvocationHandler h = (proxy, method, args) -> {
            //前置擴充套件,非業務程式碼,模擬其他方法棧呼叫
            proxyOperation.init();
            //目標方法
            Object result = method.invoke(proxyOperation, args);

            //後置擴充套件,非業務程式碼,模擬其他方法棧呼叫
            proxyOperation.postProcessor();
            return result;
        };
        ProxyCalcOperation newProxyInstance = (ProxyCalcOperation) Proxy.newProxyInstance(loader, interfaces, h);
        return newProxyInstance;
    }
}
package com.huey.designpattern.dynamicproxy;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

@Slf4j
public class ProxyOperationTest {

    /**
     * @author huey
     * @Description : 普通
     * @Date Created in 2018/10/21 20:52
     */

    @Test
    public void test1() {
        ProxyCalcOperation proxyCalcOperation = new ProxyCalcOperationDefault();
        proxyCalcOperation.calc();
    }

    /**
     * @author huey
     * @Description : 代理
     * @Date Created in 2018/10/21 20:52
     */
    @Test
    public void test2() {
        ProxyCalcOperation proxyCalcOperation = new ProxyCalcOperationDefault();
        // 從原始碼中得知,設定這個值,可以把生成的代理類,輸出出來。
        ProxyCalcOperation proxy = ProxyOperationUtil.getProxy(proxyCalcOperation);
        proxy.calc();
    }

    /**
     * @author huey
     * @Description : 生成後 反編譯下 檢視
     * @Date Created in 2018/10/21 22:13
     */
    @Test
    public void test3() {
        //生成的jdk代理類
        byte[] bytes = ProxyGenerator.generateProxyClass("$ProxyTl", new Class[]{ProxyCalcOperation.class});
        try {
            FileOutputStream fileOutputStream = new FileOutputStream("E:\\company\\$ProxyTl.class");
            fileOutputStream.write(bytes);
            fileOutputStream.flush();
            fileOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}

聯想

spring中的代理模式是什麼樣的呢?簡單列出了4個圖,jdk和cglib兩種方式,預設jdk,UT3中的代理類反編譯後可看預設繼承Proxy實現目標介面,為啥jdk代理必須是介面形式呢,其實很簡單,因為java是單繼承。如果哪天再總結spring的小編再跟蹤下

spring_jdk_proxy.png

 

spring_cglib_proxy_1.png

spring_cglib_proxy_2.png

 

is_cglib_default.png

jdk代理原始碼配圖

1.png

2.png

3.png

4.png

5.png

6.png

7.png

8.png

總結

設計模式【六大原則和23種模式】是個學不完的東西,還是要看自己想到哪種程度,讀原始碼,瞭解框架,非常的敏感,過癮
最後,謝謝讀者們的欣賞,也希望指出其中的不足,小編還會繼續邊學習邊總結
謝謝HeadFirst 作者。設計模式的學習層面結束了