《讀_Head_First_有感》_“代理模式_完結”
阿新 • • 發佈:2018-11-05
轉載自我的朋友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 作者
。設計模式的學習層面結束了