Java靜態(tài)代理和動(dòng)態(tài)代理詳解
Java 靜態(tài)代理和動(dòng)態(tài)代理
- 代理模式主要有靜態(tài)代理和動(dòng)態(tài)代理兩種實(shí)現(xiàn)方式
靜態(tài)代理
- 指程序在運(yùn)行之前所需要的代理類就已經(jīng)被創(chuàng)建好了
- 通常情況下代理類、目標(biāo)類都需要實(shí)現(xiàn)同一個(gè)接口
- 代理類內(nèi)部維護(hù)了目標(biāo)類的引用,在不改變目標(biāo)類的情況下通過(guò)代理類增加功能來(lái)擴(kuò)展目標(biāo)類的邏輯功能,真正的業(yè)務(wù)邏輯還是由目標(biāo)類來(lái)實(shí)現(xiàn),代理類只是調(diào)用目標(biāo)類的相關(guān)方法,符合開閉原則
- 一般需要為每個(gè)目標(biāo)類都創(chuàng)建代理類和接口,可能導(dǎo)致類的數(shù)量大大增加
- 通常情況下接口一旦修改,代理類和目標(biāo)類都需要修改,代碼耦合度較高
示例
接口
public interface ILogin {
void doLogin();
}
目標(biāo)類
public class UserLogin implements ILogin {
@Override
public void doLogin(){
System.out.print("用戶登錄邏輯");
}
}
代理類
public class UserLoginProxy implements ILogin {
private UserLogin mUserLogin;
public UserLoginProxy() {
mUserLogin = new UserLogin();
}
@Override
public void doLogin(){
//...
System.out.print("登錄前邏輯");
//真正的業(yè)務(wù)邏輯還是由目標(biāo)類來(lái)實(shí)現(xiàn)
mUserLogin.doLogin();
System.out.print("登錄后邏輯");
//...
}
}
使用
public static void main(String[] args){
//
ILogin iLogin = new UserLoginProxy();
iLogin.doLogin();
}
動(dòng)態(tài)代理
- 動(dòng)態(tài)代理指在程序運(yùn)行時(shí)通過(guò)反射機(jī)制等技術(shù)動(dòng)態(tài)創(chuàng)建出代理對(duì)象
- 基于接口的動(dòng)態(tài)代理,通常通過(guò) java.lang.reflect.Proxy 類和 java.lang.reflect.InvocationHandler 接口實(shí)現(xiàn)動(dòng)態(tài)代理
- 基于類的動(dòng)態(tài)代理,比如使用 CGLIB(Code Generation Library,一個(gè)強(qiáng)大的 Java 字節(jié)碼生成庫(kù))
- InvocationHandler 調(diào)用處理器,是一個(gè)接口,有個(gè) invoke 方法
- 通過(guò)使用 Proxy#newProxyInstance 來(lái)創(chuàng)建指定接口的代理實(shí)現(xiàn)類,當(dāng)調(diào)用代理對(duì)象任何方法時(shí)都會(huì)調(diào)用 InvocationHandler#invoke 方法,可以在這個(gè)方法中拿到傳入的參數(shù),注解等
- 只能實(shí)現(xiàn)基于接口的動(dòng)態(tài)代理,因?yàn)?Java 是單繼承的,它在動(dòng)態(tài)生成 $ProxyX 代理類的時(shí)候已經(jīng)繼承 Proxy 類了
- 可以減少類的數(shù)量,降低工作量,靈活性高,可以在運(yùn)行時(shí)動(dòng)態(tài)地為不同的委托類創(chuàng)建代理對(duì)象
- 減少對(duì)業(yè)務(wù)接口的依賴,降低耦合,便于后期維護(hù),可以用于實(shí)現(xiàn) AOP 面向切面編程,比如在某個(gè)邏輯功能前后添加日志記錄、監(jiān)控性能和權(quán)限檢查控制等操作
- 由于因?yàn)槭褂昧朔瓷?,所以?huì)在運(yùn)行時(shí)會(huì)消耗一定的性能
private InvocationHandler mInvocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//target 是目標(biāo)類對(duì)象
Object result = method.invoke(target,args);
//
return result;
}
};
//方式1
//創(chuàng)建代理類的 Class 對(duì)象,參數(shù) (ClassLoader 類加載器,Class<?>)
Class<?> proxyClass = Proxy.getProxyClass(Toast.class.getClassLoader(), Toast.class);
//通過(guò)反射實(shí)例化代理對(duì)象
Toast toastProxy = (Toast) proxyClass.getConstructor(InvocationHandler.class).newInstance(mInvocationHandler);
//方式2,是方法1的簡(jiǎn)化版,newProxyInstance 內(nèi)部代碼邏輯和方法1基本一致
//直接創(chuàng)建代理對(duì)象,參數(shù) (ClassLoader 類加載器,Class<?>[],InvocationHandler)
Toast toastProxy2 = (Toast) Proxy.newProxyInstance(Toast.class.getClassLoader(),new Class<?>[]{Toast.class}, mInvocationHandler);
示例
接口
public interface ICalculator {
int add(int a, int b);
int minus(int a, int b);
}
目標(biāo)類
public class Calculator implements ICalculator {
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public int minus(int a, int b) {
return a - b;
}
}
動(dòng)態(tài)代理的處理類
public class CalculatorInvocationHandler implements InvocationHandler {
private Object target;
public CalculatorInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在方法調(diào)用前可以添加一些邏輯
Object result = method.invoke(target, args);
// 在方法調(diào)用后也可以添加一些邏輯
return result;
}
}
使用
//目標(biāo)類
ICalculator calculator = new Calculator();
//代理類
ICalculator proxyCalculator = (ICalculator) Proxy.newProxyInstance(
calculator.getClass().getClassLoader(),
calculator.getClass().getInterfaces(),
new CalculatorInvocationHandler(calculator)
);
到此這篇關(guān)于Java靜態(tài)代理和動(dòng)態(tài)代理詳解的文章就介紹到這了,更多相關(guān)Java靜態(tài)和動(dòng)態(tài)代理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot中使用JDBC Templet的方法教程
這篇文章主要給大家介紹了關(guān)于在Spring Boot中使用JDBC Templet的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-03-03
springboot+vue3無(wú)感知刷新token實(shí)戰(zhàn)教程
本文介紹了基于Spring Boot和Vue3的無(wú)感知刷新Token的實(shí)現(xiàn),包括后端token構(gòu)造和刷新邏輯,以及前端的請(qǐng)求處理和緩存機(jī)制2025-03-03
kafka?消息隊(duì)列中點(diǎn)對(duì)點(diǎn)與發(fā)布訂閱的區(qū)別說(shuō)明
這篇文章主要介紹了kafka?消息隊(duì)列中點(diǎn)對(duì)點(diǎn)與發(fā)布訂閱的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
Springboot實(shí)現(xiàn)密碼的加密解密
這篇文章主要為大家詳細(xì)介紹了Springboot實(shí)現(xiàn)密碼的加密解密,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11
SpringSecurity動(dòng)態(tài)加載用戶角色權(quán)限實(shí)現(xiàn)登錄及鑒權(quán)功能
這篇文章主要介紹了SpringSecurity動(dòng)態(tài)加載用戶角色權(quán)限實(shí)現(xiàn)登錄及鑒權(quán)功能,很多朋友感覺(jué)這個(gè)功能很難,今天小編通過(guò)實(shí)例代碼給大家講解,需要的朋友可以參考下2019-11-11

