摘要:場景描述病從口入這句成語告訴我們注意飲食健康,小六同學(xué)想吃蘋果,在吃蘋果之前需要清洗一下蘋果和洗一下手,吃完蘋果后,需要洗一下手保持個(gè)人衛(wèi)生十分鐘后。。。動(dòng)態(tài)代理小六委托管家來代理洗食物和洗手,小六屬于委托對(duì)象,管家屬于代理對(duì)象。
前言
為了更好的理解代理模式,首先根據(jù)生活中實(shí)際場景進(jìn)行模擬,讓我們?cè)谏钪腥ンw驗(yàn)設(shè)計(jì)思想的美妙。
場景描述“病從口入”這句成語告訴我們注意飲食健康,小六同學(xué)想吃蘋果,在吃蘋果之前需要清洗一下蘋果和洗一下手,吃完蘋果后,需要洗一下手保持個(gè)人衛(wèi)生;十分鐘后。。。小六同學(xué)又想吃一個(gè)大鴨梨,清洗鴨梨--洗手--吃鴨梨--吃完洗手。
代碼模擬蘋果和鴨梨都屬于食物,創(chuàng)建一個(gè)食物接口
public interface Foods { void eatApple(); void eatpear(); }
小六同學(xué)吃蘋果和鴨梨的動(dòng)作,相當(dāng)于實(shí)現(xiàn)類
public class People implements Foods { @Override public void eatApple() { System.out.println("eat apple"); } @Override public void eatpear() { System.out.println("eat pear"); } }
小六同學(xué)謹(jǐn)記“病從口入”這句成語,所以在吃食物之前需要清洗食物洗手,吃完食物后需要洗手。so easy~~直接在People實(shí)現(xiàn)類上加上這兩個(gè)動(dòng)作就可以,但是小五同學(xué)說我吃蘋果和鴨梨之前只洗手不洗食物,為了實(shí)現(xiàn)小五這個(gè)動(dòng)作需要重新寫接口,重寫實(shí)現(xiàn)類。那可不可以在不改變實(shí)現(xiàn)類的前提下實(shí)現(xiàn)呢,答案是肯定的,那就用到靜態(tài)代理來實(shí)現(xiàn)。
public static void main(String []args){ People people = new Perople(); System.out.println("吃前:洗食物洗手"); people.eatApple(); System.out.println("吃后:洗手"); System.out.println("吃前:洗食物洗手"); people.eatpear(); System.out.println("吃后:洗手"); }小六變胖了
小六同學(xué)最近變胖了,原因是越來越能吃了,一天需要吃蘋果、鴨梨、香蕉、櫻桃、橘子、橙子。。。等一百種水果才能吃飽!雖然飲食控制不住,但小六同學(xué)還是每次吃食物之前都洗食物洗手,吃完食物后洗手的好習(xí)慣,隨之食量的增大,每次都需要洗食物洗手,費(fèi)力費(fèi)時(shí)間,小六心生一計(jì),不如干脆找個(gè)管家,每次想吃東西時(shí)只需喊一聲,管家?guī)兔ο词澄锵词郑约褐回?fù)責(zé)吃,棒極了。
動(dòng)態(tài)代理的兩種實(shí)現(xiàn)方式Java 實(shí)現(xiàn)動(dòng)態(tài)代理有兩種方式,一種是 Java 自帶的 JDK 動(dòng)態(tài)代理,還有一種是使用字節(jié)碼增強(qiáng)技術(shù)實(shí)現(xiàn)的 CGLIB 庫動(dòng)態(tài)代理。
兩種方法同時(shí)存在,各有優(yōu)劣。jdk動(dòng)態(tài)代理是由Java內(nèi)部的反射機(jī)制來實(shí)現(xiàn)的,cglib動(dòng)態(tài)代理底層則是借助asm來實(shí)現(xiàn)的。總的來說,反射機(jī)制在生成類的過程中比較高效,而asm在生成類之后的相關(guān)執(zhí)行過程中比較高效(可以通過將asm生成的類進(jìn)行緩存,這樣解決asm生成類過程低效問題)。還有一點(diǎn)必須注意:jdk動(dòng)態(tài)代理的應(yīng)用前提,必須是目標(biāo)類基于統(tǒng)一的接口。如果沒有上述前提,jdk動(dòng)態(tài)代理不能應(yīng)用。由此可以看出,jdk動(dòng)態(tài)代理有一定的局限性,cglib這種第三方類庫實(shí)現(xiàn)的動(dòng)態(tài)代理應(yīng)用更加廣泛,且在效率上更有優(yōu)勢。
JDK動(dòng)態(tài)代理小六委托管家來代理洗食物和洗手,小六屬于委托對(duì)象,管家屬于代理對(duì)象。
JDK動(dòng)態(tài)代理主要兩個(gè)相關(guān)類:
Proxy(java.lang.reflect包下的),主要負(fù)責(zé)管理和創(chuàng)建代理類的工作。
InvocationHandler 接口,只擁有一個(gè)invoke方法,主要負(fù)責(zé)方法調(diào)用部分,是動(dòng)態(tài)代理中我們需要實(shí)現(xiàn)的方法
每一個(gè)代理實(shí)例都必須要實(shí)現(xiàn)InvocationHandler這個(gè)接口,當(dāng)我們通過代理對(duì)象調(diào)用一個(gè)方法的時(shí)候,這個(gè)方法的調(diào)用就會(huì)被轉(zhuǎn)發(fā)為由InvocationHandler這個(gè)接口的 invoke 方法來進(jìn)行調(diào)用,所以要想對(duì)方法(吃食物)加強(qiáng)(洗食物洗手)就需要在invoke方法中實(shí)現(xiàn)。
public class FoodsHandler implements InvocationHandler{ private Object object;//委托對(duì)象(小六同學(xué)) public FoodsHandler(Object object){ this.object = object; } /*invoke方法的三個(gè)參數(shù): proxy: 指代我們所代理的那個(gè)真實(shí)對(duì)象 method: 指代的是我們所要調(diào)用真實(shí)對(duì)象的某個(gè)方法的Method對(duì)象 args: 指代的是調(diào)用真實(shí)對(duì)象某個(gè)方法時(shí)接受的參數(shù) */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ System.out.println("吃前:洗食物洗手"); //當(dāng)代理對(duì)象調(diào)用真實(shí)對(duì)象的方法時(shí),會(huì)自動(dòng)跳轉(zhuǎn)到代理對(duì)象關(guān)聯(lián)的handler對(duì)象的invoke方法來進(jìn)行調(diào)用 Object result = method.invoke(object, args); System.out.println("吃后:洗手"); return result; } }
public static void main(String []args){ //委托對(duì)象(小六同學(xué)) People people = new People(); //我們要代理哪個(gè)真實(shí)對(duì)象,就將該對(duì)象傳進(jìn)去,最后是通過該真實(shí)對(duì)象來調(diào)用其方法的 FoodsHandler inter = new FoodsHandler(people); //加上這句將會(huì)產(chǎn)生一個(gè)$Proxy0.class文件,這個(gè)文件即為動(dòng)態(tài)生成的代理類文件 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); //獲取代理類實(shí)例foods Foods foods = (Foods)(Proxy.newProxyInstance(Foods.class.getClassLoader(), new Class[] {Foods.class}, fh)); //通過代理類對(duì)象調(diào)用代理類方法,實(shí)際上會(huì)轉(zhuǎn)到invoke方法調(diào)用 foods.eatApple(); foods.eatpear(); } /*newProxyInstance方法三個(gè)參數(shù)的解釋如下 public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException loader:一個(gè)ClassLoader對(duì)象,定義了由哪個(gè)ClassLoader對(duì)象來對(duì)生成的代理對(duì)象進(jìn)行加載 interfaces:一個(gè)Interface對(duì)象的數(shù)組,表示的是我將要給我需要代理的對(duì)象提供一組什么接口,如果我提供了一組接口給它,那么這個(gè)代理對(duì)象就宣稱實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了 h:一個(gè)InvocationHandler對(duì)象,表示的是當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上 */Cglib動(dòng)態(tài)代理
上面的 JDK 動(dòng)態(tài)代理需要定義一個(gè)接口,實(shí)現(xiàn)類實(shí)現(xiàn)接口中的方法,如果實(shí)現(xiàn)類不能實(shí)現(xiàn)接口時(shí),我們就需要 CGLIB 動(dòng)態(tài)代理。
使用 CGLIB 動(dòng)態(tài)代理之前需要導(dǎo)入相關(guān) jar 包,可以多帶帶導(dǎo)入 cglib-.jar 包和 asm-.jar 包,也可以只導(dǎo)入一個(gè) cglib-nodep-.jar 包(包含了 asm)。下載鏈接
public class People { public void eatApple() { System.out.println("eat apple"); } public void eatpear() { System.out.println("eat pear"); } }
public class PeopleCglib implements MethodInterceptor { @Override // object 代表要增強(qiáng)的對(duì)象,method代表要攔截的方法,objects 代表方法中的參數(shù),methodProxy 代表對(duì)方法的代理 public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable{ System.out.println("吃前:洗食物洗手"); methodProxy.invokeSuper(object,objects); System.out.println("吃后:洗手"); return object; } }
public class Main { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); // 增強(qiáng)類對(duì)象 enhancer.setSuperclass(People.class); // 設(shè)置要增強(qiáng)的類(People) PeopleCglib peopleCglib = new PeopleCglib(); enhancer.setCallback(peopleCglib); // 設(shè)置要增強(qiáng)的方法(peopleCglib) People people = (People) enhancer.create(); // 生成增強(qiáng)過的子類對(duì)象 people.eatApple(); // 調(diào)用方法實(shí)際為增強(qiáng)過的方法 people.eatApple(); } }
輸出結(jié)果
吃前:洗食物洗手 eat apple 吃后:洗手 吃前:洗食物洗手 eat apple 吃后:洗手
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/75363.html
摘要:設(shè)計(jì)模式之代理模式今天學(xué)到的動(dòng)態(tài)代理實(shí)現(xiàn),對(duì)代理這個(gè)概念很模糊,看了一篇文章發(fā)現(xiàn)這是一種設(shè)計(jì)模式,于是學(xué)習(xí)記錄一下。簡介代理模式是一種對(duì)象結(jié)構(gòu)型的模式,主要為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。下面依次講解著三種代理。 設(shè)計(jì)模式之代理模式 今天學(xué)到Spring的動(dòng)態(tài)代理實(shí)現(xiàn)AOP,對(duì)代理這個(gè)概念很模糊,看了一篇文章發(fā)現(xiàn)這是一種設(shè)計(jì)模式,于是學(xué)習(xí)記錄一下。 簡介 代理模式是一種對(duì)...
時(shí)間:2017年08月28日星期一說明:本文部分內(nèi)容均來自慕課網(wǎng)。@慕課網(wǎng):http://www.imooc.com教學(xué)源碼:https://github.com/zccodere/s...學(xué)習(xí)源碼:https://github.com/zccodere/s... 第一章:代理模式 1-1 概念介紹 學(xué)習(xí)本課程基礎(chǔ) 面向?qū)ο蟮脑O(shè)計(jì)思維 了解多態(tài)的概念 了解反射機(jī)制 課程目標(biāo) 代理模式基本概念及分類...
摘要:網(wǎng)上關(guān)于的動(dòng)態(tài)代理,和這些概念有講解得非常高深的文章。現(xiàn)在咱們通過一個(gè)最簡單的例子認(rèn)識(shí)什么是。創(chuàng)建一個(gè)簡單的類,實(shí)現(xiàn)這個(gè)接口。看看用如何優(yōu)雅實(shí)現(xiàn)吧希望這個(gè)例子能讓大家對(duì)的動(dòng)態(tài)代理之有了最基本的了解。 網(wǎng)上關(guān)于Java的動(dòng)態(tài)代理,Proxy和InvocationHandler這些概念有講解得非常高深的文章。其實(shí)這些概念沒有那么復(fù)雜。現(xiàn)在咱們通過一個(gè)最簡單的例子認(rèn)識(shí)什么是Invocatio...
摘要:網(wǎng)上關(guān)于的動(dòng)態(tài)代理,和這些概念有講解得非常高深的文章。現(xiàn)在咱們通過一個(gè)最簡單的例子認(rèn)識(shí)什么是。創(chuàng)建一個(gè)簡單的類,實(shí)現(xiàn)這個(gè)接口。看看用如何優(yōu)雅實(shí)現(xiàn)吧希望這個(gè)例子能讓大家對(duì)的動(dòng)態(tài)代理之有了最基本的了解。 網(wǎng)上關(guān)于Java的動(dòng)態(tài)代理,Proxy和InvocationHandler這些概念有講解得非常高深的文章。其實(shí)這些概念沒有那么復(fù)雜。現(xiàn)在咱們通過一個(gè)最簡單的例子認(rèn)識(shí)什么是Invocatio...
摘要:受知乎文章和設(shè)計(jì)模式之禪的啟發(fā),我也來搞一篇腦洞小開的文章由標(biāo)題可知,這篇文章是寫給我女朋友看的。于是這就讓經(jīng)紀(jì)人對(duì)粉絲說只有萬,我才會(huì)寫代碼。 前言 只有光頭才能變強(qiáng) 回顧前面: ThreadLocal就是這么簡單 多線程三分鐘就可以入個(gè)門了! 多線程基礎(chǔ)必要知識(shí)點(diǎn)!看了學(xué)習(xí)多線程事半功倍 Java鎖機(jī)制了解一下 AQS簡簡單單過一遍 Lock鎖子類了解一下 線程池你真不來了解一下...
閱讀 870·2021-09-02 09:55
閱讀 1495·2019-12-27 12:02
閱讀 1684·2019-08-30 14:24
閱讀 1138·2019-08-30 14:18
閱讀 2750·2019-08-29 13:57
閱讀 2193·2019-08-26 11:51
閱讀 1364·2019-08-26 10:37
閱讀 763·2019-08-23 16:09