Java動(dòng)態(tài)代理之?dāng)r截器的應(yīng)用
由于動(dòng)態(tài)代理一般都比較難理解,程序設(shè)計(jì)者會(huì)設(shè)計(jì)一個(gè)攔截器接口供開(kāi)發(fā)者使用,開(kāi)發(fā)者只要知道攔截器接口的方法、含義和作用即可,無(wú)須知道動(dòng)態(tài)代理是怎么實(shí)現(xiàn)的。用JDK動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)一個(gè)攔截器的邏輯,為此先定義攔截器接口Interceptor,如下所示:
/** * @Auther: haozz * @Date: 2018/5/27 22:15 * @Description:攔截器接口 **/ public interface Interceptor { boolean before(Object proxy, Object target, Method method,Object[] args); void around(Object proxy,Object target,Method method,Object[] args); void after(Object proxy,Object target,Method method,Object[] args); }
這里定義了3個(gè)方法,before、around、after方法,分別給予這些方法如下邏輯定義:
- 3個(gè)方法的參數(shù)為:proxy代理對(duì)象、target真實(shí)對(duì)象、method方法、args運(yùn)行方法參數(shù);
- before方法返回boolean值,它在真實(shí)對(duì)象前調(diào)用。當(dāng)返回為true時(shí),則反射真實(shí)對(duì)象的方法;當(dāng)返回為false時(shí),則調(diào)用around方法;
- 在before方法返回為false的情況下,調(diào)用around方法
- 在反射真實(shí)對(duì)象方法或者around方法執(zhí)行之后,調(diào)用after方法
實(shí)現(xiàn)這個(gè)Interceptor的實(shí)現(xiàn)類(lèi)——MyInterceptor,如下:
/** * @Auther: haozz * @Date: 2018/5/27 22:48 * @Description:MyInterceptor **/ public class MyInterceptor implements Interceptor{ @Override public boolean before(Object proxy, Object target, Method method, Object[] args) { System.out.println("反射方法前邏輯"); return false;//不反射被代理對(duì)象原有方法 } @Override public void around(Object proxy, Object target, Method method, Object[] args) { System.out.println("取代了被代理對(duì)象的方法"); } @Override public void after(Object proxy, Object target, Method method, Object[] args) { System.out.println("反射方法后邏輯"); } }
它實(shí)現(xiàn)了所有Interceptor接口的方法,使用JDK動(dòng)態(tài)代理,就可以去實(shí)現(xiàn)這些方法在適當(dāng)時(shí)的調(diào)用邏輯了。以上一篇(Java設(shè)計(jì)模式之動(dòng)態(tài)代理)中的接口和實(shí)現(xiàn)類(lèi)為例,在JDK動(dòng)態(tài)代理中使用攔截器,如下所示:
/** * @Auther: haozz * @Date: 2018/5/27 22:30 * @Description: **/ public class InterceptorJdkProxy implements InvocationHandler { private Object target;//真實(shí)對(duì)象 private String interceptorClass = null;//攔截器全限定名 public InterceptorJdkProxy(Object target,String interceptorClass){ this.target = target; this.interceptorClass = interceptorClass; } public static Object bind(Object target,String interceptorClass){ //取得代理對(duì)象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InterceptorJdkProxy(target,interceptorClass)); } @Override /** * Description:通過(guò)代理對(duì)象調(diào)用方法,首先進(jìn)入這個(gè)方法 * @auther: haozz * @param: proxy 代理對(duì)象 * @param: method 被調(diào)用方法 * @param: args 方法的參數(shù) * @return: java.lang.Object * @date: 2018/5/27 23:00 **/ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(interceptorClass == null){ //沒(méi)有設(shè)置攔截器則直接反射原有方法 return method.invoke(target,args); } Object result = null; //通過(guò)反射生成攔截器 Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).newInstance(); //調(diào)用前置方法 if(interceptor.before(proxy,target,method,args)){ //反射原有對(duì)象方法 result = method.invoke(target,args); }else{//返回false執(zhí)行around方法 interceptor.around(proxy,target,method,args); } //調(diào)用后置方法 interceptor.after(proxy,target,method,args); return result; } }
這里有兩個(gè)屬性,一個(gè)是target,它是真實(shí)對(duì)象;另一個(gè)是字符串interceptorClass,它是一個(gè)攔截器的全限定名。解釋以下這段代碼的執(zhí)行步驟:
第1步,在bind方法中用JDK動(dòng)態(tài)代理綁定了一個(gè)對(duì)象,然后返回代理對(duì)象;
第2步,如果沒(méi)有設(shè)置攔截器,則直接反射真實(shí)對(duì)象的方法,然后結(jié)束,否則進(jìn)行第3步;
第3步,通過(guò)反射生成攔截器,并準(zhǔn)備使用它;
第4步,調(diào)用攔截器的before方法,如果返回為true,反射原來(lái)的方法;否則運(yùn)行攔截器的around方法;
第5步,調(diào)用攔截器的after方法;
第6步,返回結(jié)果。
- 開(kāi)發(fā)者只要知道攔截器的作用就可以編寫(xiě)攔截器了,編寫(xiě)完后可以設(shè)置攔截器,這樣就完成了任務(wù),所以對(duì)于開(kāi)發(fā)者而言相對(duì)簡(jiǎn)單了
- 設(shè)計(jì)者可能是精通Java的開(kāi)發(fā)人員,他來(lái)完成動(dòng)態(tài)代理的邏輯
- 設(shè)計(jì)者只會(huì)把攔截器接口露給開(kāi)發(fā)者使用,讓動(dòng)態(tài)代理的邏輯在開(kāi)發(fā)者的視野中“消失”
攔截器可以進(jìn)一步簡(jiǎn)化動(dòng)態(tài)代理的使用方法,使程序變得更簡(jiǎn)單,用如下的測(cè)試類(lèi)測(cè)試一下:
public class Mytest { public static void main(String []args){ HelloWorld proxy1 = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(),"com.csdn.blog.interceptor.MyInterceptor"); proxy1.sayHelloWorld(); } }
運(yùn)行這段代碼,得到以下結(jié)果:
反射方法前邏輯
取代了被代理對(duì)象的方法
反射方法后邏輯
顯然,攔截器已經(jīng)生效。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接
相關(guān)文章
Java結(jié)束線(xiàn)程的三種方法及該如何選擇
這篇文章主要介紹了Java結(jié)束線(xiàn)程的三種方法及該如何選擇,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下2021-03-03java?集合工具類(lèi)Collections及Comparable和Comparator排序詳解
這篇文章主要介紹了java集合工具類(lèi)Collections及Comparable和Comparator排序詳解,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-06-06構(gòu)建多模塊的Spring Boot項(xiàng)目步驟全紀(jì)錄
這篇文章主要給大家介紹了關(guān)于如何構(gòu)建多模塊的Spring Boot項(xiàng)目的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用SpringBoot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05Java源碼解析之Gateway請(qǐng)求轉(zhuǎn)發(fā)
今天給大家?guī)?lái)的是關(guān)于Java的相關(guān)知識(shí),文章圍繞著Gateway請(qǐng)求轉(zhuǎn)發(fā)展開(kāi),文中有非常詳細(xì)介紹及代碼示例,需要的朋友可以參考下2021-06-06關(guān)于spring依賴(lài)注入的方式以及優(yōu)缺點(diǎn)
這篇文章主要介紹了關(guān)于spring依賴(lài)注入的方式以及優(yōu)缺點(diǎn),依賴(lài)注入,是IOC的一個(gè)方面,是個(gè)通常的概念,它有多種解釋,這概念是說(shuō)你不用創(chuàng)建對(duì)象,而只需要描述它如何被創(chuàng)建,需要的朋友可以參考下2023-07-07mybatis 字段名自動(dòng)轉(zhuǎn)小寫(xiě)的實(shí)現(xiàn)
這篇文章主要介紹了mybatis 字段名自動(dòng)轉(zhuǎn)小寫(xiě)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03java 解決異常 2 字節(jié)的 UTF-8 序列的字節(jié)2 無(wú)效的問(wèn)題
這篇文章主要介紹了java 解決異常 2 字節(jié)的 UTF-8 序列的字節(jié) 2 無(wú)效的問(wèn)題的相關(guān)資料,需要的朋友可以參考下2016-12-12