Java中的代理模式詳解及實(shí)例代碼
java 代理模式詳解
前言:
在某些情況下,一個(gè)客戶不想或者不能直接引用一個(gè)對(duì)象,此時(shí)可以通過(guò)一個(gè)稱之為“代理”的第三者來(lái)實(shí)現(xiàn)間接引用。代理對(duì)象可以在客戶端和目標(biāo)對(duì)象之間起到 中介的作用,并且可以通過(guò)代理對(duì)象去掉客戶不能看到 的內(nèi)容和服務(wù)或者添加客戶需要的額外服務(wù)。
簡(jiǎn)單來(lái)說(shuō)代理模式就是通過(guò)一個(gè)代理對(duì)象去訪問(wèn)一個(gè)實(shí)際對(duì)象,并且可以像裝飾模式一樣給對(duì)象添加一些功能。
靜態(tài)代理
所謂靜態(tài)代理即在程序運(yùn)行前代理類就已經(jīng)存在,也就是說(shuō)我們編寫(xiě)代碼的時(shí)候就已經(jīng)把代理類的代碼寫(xiě)好了,而動(dòng)態(tài)代理則是在程序運(yùn)行時(shí)自動(dòng)生成代理類。
描述起來(lái)太過(guò)抽象,看一下代碼就明白是怎么回事了
main
public class Main { public static void main(String[] args) { Water water = new Water(); WaterProxy waterProxy = new WaterProxy(water); waterProxy.drink(); } }
接口
//代理類與被代理類共同實(shí)現(xiàn)的接口 public interface Drink { void drink(); }
被代理類
//被代理的類 public class Water implements Drink { @Override public void drink() { System.out.println("drink water"); } }
代理類
//代理類 //與被代理類實(shí)現(xiàn)同一個(gè)接口 public class DrinkProxy implements Drink { private Drink drinkImpl; //通過(guò)構(gòu)造函數(shù)傳入Water對(duì)象 public DrinkProxy(Drink drinkImpl) { this.drinkImpl = drinkImpl; } @Override public void drink() { //在執(zhí)行被代理對(duì)象的方法前做一些事情 System.out.println("before drink"); //執(zhí)行被代理對(duì)象的方法 drinkImpl.drink(); //在執(zhí)行被代理對(duì)象的方法后做一些事 System.out.println("after drink"); } }
執(zhí)行結(jié)果
before drink drink water after drink
動(dòng)態(tài)代理
有時(shí)候我們只想改變代理類所代理的類,但是代理對(duì)象執(zhí)行實(shí)際對(duì)象的方法前后所做的事情是一樣的,正所謂鐵打的代理類,流水的被代理類。而采用靜態(tài)代理就只能代理實(shí)現(xiàn)了同一接口的類,如果要代理任意類則必須寫(xiě)很多重復(fù)的代理類。此時(shí)我們可以采用動(dòng)態(tài)代理,Java已經(jīng)為實(shí)現(xiàn)動(dòng)態(tài)代理提供了一套比較方便的工具。
java.lang.reflect.Proxy類中可以動(dòng)態(tài)生成代理對(duì)象的方法
/** *返回實(shí)現(xiàn)了指定接口的對(duì)象,調(diào)用代理對(duì)象的方法會(huì)調(diào)用 *InvocationHandler的invoke方法 * * @param loader 獲取代理類所使用的類加載器 * @param interfaces 代理類所要實(shí)現(xiàn)的接口 * @param h 實(shí)現(xiàn)了InvocationHandler接口的對(duì)象 * @return 代理對(duì)象 */ public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)
InvocationHandler接口
/** *每個(gè)代理類都有一個(gè)關(guān)聯(lián)的InvocationHandler *當(dāng)代理對(duì)象執(zhí)行一個(gè)方法的時(shí)候會(huì)直接執(zhí)行invoke方法 */ public interface InvocationHandler { /** * @param 調(diào)用該方法的代理對(duì)象 * @param method 代理對(duì)象所調(diào)用的方法 * @param args 調(diào)用的方法的參數(shù) * @return 調(diào)用的方法的返回值 */ public Object invoke(Object proxy, Method method, Object[] args) }
描述總是比較抽象,還是看實(shí)際例子比較好理解
例子
InvocationHandler接口的實(shí)現(xiàn)類
public class CommonInvocationHandler implements InvocationHandler { //被代理的對(duì)象 private Object proxied; public CommonInvocationHandler(Object proxied) { this.proxied = proxied; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在調(diào)用被代理對(duì)象的方法前做一些事情 System.out.println("before doing something"); //調(diào)用被代理對(duì)象的方法 Object result = method.invoke(proxied, args); //在調(diào)用被代理對(duì)象的方法后做一些事情 System.out.println("after doing something");; return result; } }
Main
public class Main { public static void main(String[] args) { //被代理的對(duì)象 Water water = new Water(); //動(dòng)態(tài)獲取代理對(duì)象 Drink waterProxy = (Drink) Proxy.newProxyInstance(water.getClass().getClassLoader(), water.getClass().getInterfaces(), new CommonInvocationHandler(water)); //通過(guò)代理對(duì)象調(diào)用方法 waterProxy.drink(); } }
輸出結(jié)果
before doing something drink water after doing something
也可以不要具體的被代理對(duì)象,但是必須有相應(yīng)的接口(沒(méi)有實(shí)現(xiàn)接口的類可以使用cglib實(shí)現(xiàn)動(dòng)態(tài)代理)才可以動(dòng)態(tài)獲取代理對(duì)象。像最近比較火的Retrofit就直接通過(guò)聲明好的接口使用動(dòng)態(tài)代理進(jìn)行網(wǎng)絡(luò)請(qǐng)求。
例子
簡(jiǎn)單的模擬一下retrofit
POST注解
//Post請(qǐng)求注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface POST { String value() default ""; }
Query注解
//Post請(qǐng)求注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface POST { String value() default ""; }
Service接口
public interface Service { //用POST注解聲明請(qǐng)求的方式和相對(duì)路徑 @POST("/login") //@Query注解聲明請(qǐng)求的參數(shù)名 void login(@Query("username")String username, @Query("password")String password); }
Main
public class Main { public static void main(String[] args) { // 動(dòng)態(tài)獲取Service接口的代理 Service service = (Service) Proxy.newProxyInstance(Service.class.getClassLoader(), new Class[] { Service.class }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 通過(guò)注解獲取請(qǐng)求的相對(duì)路徑 String retativePath = ((POST) method.getAnnotations()[0]).value(); System.out.println("relative path: " + retativePath); // 獲取參數(shù)的注解 Annotation[][] parameterAnnotations = method.getParameterAnnotations(); // 通過(guò)參數(shù)的注解獲取請(qǐng)求參數(shù) for (int i = 0; i < parameterAnnotations.length; i++) { if (parameterAnnotations[i].length != 0) { for (int j = 0; j < parameterAnnotations[i].length; j++) { Query query = (Query) parameterAnnotations[i][j]; System.out.println(query.value() + ": " + args[i].toString()); } } } return null; } }); // 調(diào)用代理對(duì)象的方法 service.login("hello", "world"); } }
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
- java代理模式與動(dòng)態(tài)代理模式詳解
- java實(shí)現(xiàn)http的Post、Get、代理訪問(wèn)請(qǐng)求
- Java中反射動(dòng)態(tài)代理接口的詳解及實(shí)例
- 基于接口實(shí)現(xiàn)java動(dòng)態(tài)代理示例
- 詳解java中動(dòng)態(tài)代理實(shí)現(xiàn)機(jī)制
- java動(dòng)態(tài)代理(jdk與cglib)詳細(xì)解析
- java實(shí)現(xiàn)動(dòng)態(tài)代理方法淺析
- Java動(dòng)態(tài)代理的應(yīng)用詳解
- 詳解java動(dòng)態(tài)代理的2種實(shí)現(xiàn)方式
- 淺談Java注解和動(dòng)態(tài)代理
- Java基礎(chǔ)之代理原理與用法詳解
相關(guān)文章
五種單件模式之Singleton的實(shí)現(xiàn)方法詳解
本篇文章是對(duì)Singleton的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06Spring實(shí)戰(zhàn)之調(diào)用實(shí)例工廠方法創(chuàng)建Bean操作示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之調(diào)用實(shí)例工廠方法創(chuàng)建Bean操作,結(jié)合實(shí)例形式分析了實(shí)例工廠方法創(chuàng)建Bean相關(guān)配置、實(shí)現(xiàn)方法及操作注意事項(xiàng),需要的朋友可以參考下2019-11-11使用mybatis報(bào)Invalid bound statement解決分析
這篇文章主要為大家介紹了使用mybatis報(bào)Invalid bound statement原因解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12Springmvc返回html頁(yè)面問(wèn)題如何解決
這篇文章主要介紹了Springmvc返回html頁(yè)面問(wèn)題如何解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10Spring Cloud Config解決的問(wèn)題和案例
Spring Cloud Config 是 Spring Cloud 套件中的一個(gè)工具,提供了在分布式系統(tǒng)中對(duì)外部化配置的服務(wù)器端和客戶端支持,本文介紹了Spring Cloud Config解決的問(wèn)題和案例,文中有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下2024-07-07Java分支結(jié)構(gòu)和循環(huán)結(jié)構(gòu)原理與用法詳解
這篇文章主要介紹了Java分支結(jié)構(gòu)和循環(huán)結(jié)構(gòu)原理與用法,結(jié)合實(shí)例形式分析了java分支結(jié)構(gòu)、循環(huán)結(jié)構(gòu)、跳轉(zhuǎn)語(yǔ)句等相關(guān)概念、原理、使用技巧與操作注意事項(xiàng),需要的朋友可以參考下2020-02-02