Java中的代理模式詳解及實(shí)例代碼
java 代理模式詳解
前言:
在某些情況下,一個(gè)客戶不想或者不能直接引用一個(gè)對(duì)象,此時(shí)可以通過一個(gè)稱之為“代理”的第三者來實(shí)現(xiàn)間接引用。代理對(duì)象可以在客戶端和目標(biāo)對(duì)象之間起到 中介的作用,并且可以通過代理對(duì)象去掉客戶不能看到 的內(nèi)容和服務(wù)或者添加客戶需要的額外服務(wù)。
簡(jiǎn)單來說代理模式就是通過一個(gè)代理對(duì)象去訪問一個(gè)實(shí)際對(duì)象,并且可以像裝飾模式一樣給對(duì)象添加一些功能。
靜態(tài)代理
所謂靜態(tài)代理即在程序運(yùn)行前代理類就已經(jīng)存在,也就是說我們編寫代碼的時(shí)候就已經(jīng)把代理類的代碼寫好了,而動(dòng)態(tài)代理則是在程序運(yùn)行時(shí)自動(dòng)生成代理類。
描述起來太過抽象,看一下代碼就明白是怎么回事了
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;
//通過構(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)了同一接口的類,如果要代理任意類則必須寫很多重復(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));
//通過代理對(duì)象調(diào)用方法
waterProxy.drink();
}
}
輸出結(jié)果
before doing something drink water after doing something
也可以不要具體的被代理對(duì)象,但是必須有相應(yīng)的接口(沒有實(shí)現(xiàn)接口的類可以使用cglib實(shí)現(xiàn)動(dòng)態(tài)代理)才可以動(dòng)態(tài)獲取代理對(duì)象。像最近比較火的Retrofit就直接通過聲明好的接口使用動(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 {
// 通過注解獲取請(qǐng)求的相對(duì)路徑
String retativePath = ((POST) method.getAnnotations()[0]).value();
System.out.println("relative path: " + retativePath);
// 獲取參數(shù)的注解
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
// 通過參數(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、代理訪問請(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)文章
IntelliJ IDEA彈出“IntelliJ IDEA License Activation”的處理方法
這篇文章主要介紹了IntelliJ IDEA彈出“IntelliJ IDEA License Activation”的處理方法,本文給出解決方法,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
SpringBoot2.0整合SpringCloud Finchley @hystrixcommand注解找不到解決方案
這篇文章主要介紹了SpringBoot2.0整合SpringCloud Finchley @hystrixcommand注解找不到解決方案,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-08-08
Java使用正則表達(dá)式實(shí)現(xiàn)找出數(shù)字功能示例
這篇文章主要介紹了Java使用正則表達(dá)式實(shí)現(xiàn)找出數(shù)字功能,結(jié)合實(shí)例形式分析了Java針對(duì)數(shù)字的匹配查找及非數(shù)字替換操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-03-03
SpringBoot?緩存預(yù)熱的實(shí)現(xiàn)
本文主要介紹了SpringBoot?緩存預(yù)熱的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2007-11-11
SpringBoot深入分析運(yùn)行原理與功能實(shí)現(xiàn)
我們發(fā)現(xiàn)springBoot程序開發(fā)比spring程序編寫起來容易的多。配置簡(jiǎn)潔,依賴關(guān)系簡(jiǎn)單,啟動(dòng)運(yùn)行容易。那么結(jié)下了我們我們就要思考一下入門程序中的這些功能是怎么實(shí)現(xiàn)的2022-09-09
Java ScheduledExecutorService的具體使用
ScheduledExecutorService有線程池的特性,也可以實(shí)現(xiàn)任務(wù)循環(huán)執(zhí)行,本文主要介紹了Java ScheduledExecutorService的具體使用,具有一定的參考價(jià)值,感興趣的可以了解一下2023-05-05
idea創(chuàng)建spring?boot項(xiàng)目時(shí)javaversion只能選擇17和21解決辦法
這篇文章主要給大家介紹了關(guān)于idea創(chuàng)建spring?boot項(xiàng)目時(shí)javaversion只能選擇17和21的解決辦法,文中通過代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-01-01
Spring與Mybatis相結(jié)合實(shí)現(xiàn)多數(shù)據(jù)源切換功能
這篇文章主要介紹了Spring與Mybatis相結(jié)合實(shí)現(xiàn)多數(shù)據(jù)源切換功能的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06

