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