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