代理模式:JAVA靜態(tài)代理和動態(tài)代理的實例和實現(xiàn)詳解
前言
代理模式,我們這里結(jié)合JAVA的靜態(tài)代理和動態(tài)代理來說明,類比Spring AOP面向切面編程:增強(qiáng)消息,也是代理模式。
而我們的靜態(tài)代理和動態(tài)代理,與(service)接口和(serviceImpl)接口實現(xiàn)類有什么區(qū)別呢?靜態(tài)代理的概念與其在理解上類似,可以說靜態(tài)代理是實現(xiàn)類的增強(qiáng)消息。而且,靜態(tài)代理針對的是所以實現(xiàn)接口的實現(xiàn)類(通過向上轉(zhuǎn)型實現(xiàn))。
靜態(tài)代理
實現(xiàn)簡述
本質(zhì)上是一接口一代理,對該接口的所有實現(xiàn)類進(jìn)行"增強(qiáng)"(額外)操作:
如下例:human接口有兩個實現(xiàn)類:man和woman,我們需要在每個human接口被實現(xiàn)的時候說他是god創(chuàng)造的(增強(qiáng)操作)。那么我們針對human接口手動實現(xiàn)一個GodProxy靜態(tài)代理類。那么我們通過這個代理執(zhí)行human接口的實現(xiàn)對象的方法時就可以引入增強(qiáng)操作。
創(chuàng)建human接口
public interface Human {
public void sex();
}
創(chuàng)建接口實現(xiàn)類
public class Man implements Human{
@Override
public void sex() {
System.out.println( "this is Man" );
}
}
public class Women implements Human{
@Override
public void sex() {
System.out.println( "this is Women" );
}
}
創(chuàng)建針對接口實現(xiàn)增強(qiáng)操作的代理
public class GodProxy implements Human{
Human huamnGenarator;
public GodProxy(Human huamnGenarator){
this.huamnGenarator = huamnGenarator;
}
@Override
public void sex() {
System.out.println( "God begin to make human" );
huamnGenarator.sex();
System.out.println(" End of work ");
}
}
代理實現(xiàn)效果
public class 靜態(tài)代理 {
public static void main(String[] args) {
GodProxy proxy_1 = new GodProxy(new Man());
GodProxy proxy_2 = new GodProxy(new Women());
proxy_1.sex();
System.out.println("\n\n");
proxy_2.sex();
}
}

動態(tài)代理
實現(xiàn)簡述
相對與靜態(tài)代理,一個代理類proxy只能針對一個接口實現(xiàn)操作,眾所周知類可以實現(xiàn)多個接口,那能不能對所有的實現(xiàn)類的接口實現(xiàn)增強(qiáng)操作呢?可以的,我們需要通過反射的方式,通過向上轉(zhuǎn)型使得傳入?yún)?shù)為Object對象,但是Object里面的getClass方法獲取Class對象,實際上是實現(xiàn)類的Class(由Java的向上轉(zhuǎn)型特性可知)。 獲取實現(xiàn)類對象的class對象且獲得其實現(xiàn)的接口數(shù)組。我們可以達(dá)到這個目的:這就是動態(tài)代理:對多個接口實現(xiàn)靜態(tài)代理。
動態(tài)代理類主要實現(xiàn)InvocationHandler這個接口,接口實現(xiàn)invoke方法來實現(xiàn)增強(qiáng)操作。并通過一個自定義的方法來創(chuàng)建和綁定動態(tài)代理類和入?yún)ⅲㄏ蛏限D(zhuǎn)型為obj的實現(xiàn)類對象),從而實現(xiàn)實現(xiàn)類對象方法的增強(qiáng)操作。
即我們通過動態(tài)代理傳入實例類對象,在自己寫的newProxyInstance方法中通過Proxy類的newProxyInstance方法代理生成一個新的實例類對象,這個新的對象不僅包含所有的入?yún)嵙︻悓ο笮畔?,且在通過代理類生成新的實例類對象過程中注入了invoke方法(我們實現(xiàn)InvocationHandler接口的核心方法)的邏輯。
這里我們有兩個疑問:
1、如何實現(xiàn)增強(qiáng)的invoke方法;——通過java.lang.reflect.Proxy.newProxyInstance方法中復(fù)原向上轉(zhuǎn)型的obj對象為原對象(具體實現(xiàn)類對象),并綁定增強(qiáng)的invoke方法。
2、代理對象如何復(fù)現(xiàn)對象方法; ——利用向上轉(zhuǎn)型的復(fù)原不變性。
要點:向上轉(zhuǎn)型
向上轉(zhuǎn)型:子類實例賦值給父類引用。 無法調(diào)用子類拓展方法,但是她的實現(xiàn)確確實實存在:
Object obj = new Girl();
Girl girl = (Girl) obj;
System.err.println(obj.getClass().getName());
System.err.println(obj.getClass().getInterfaces().length);
girl.nickName();
輸出:
src.代理模式.Girl
2少女
創(chuàng)建YoungMan接口
這里還將用到Human接口,兩個接口。
public interface YoungMan {
public void nickName();
}
創(chuàng)建兩個接口實現(xiàn)類
public class Boy implements Human,YoungMan{
@Override
public void sex() {
System.out.println( "this is Man" );
}
@Override
public void nickName() {
System.out.println( "少年" );
}
}
public class Girl implements Human,YoungMan{
@Override
public void sex() {
System.out.println( "this is Women" );
}
@Override
public void nickName() {
System.out.println( "少女" );
}
}
創(chuàng)建動態(tài)代理實例對象
這里我們主要通過Proxy.newProxyInstance方法創(chuàng)建一個代理類,傳參:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
類加載器:指定代理類由哪個classloader加載;代理類需要代理實現(xiàn)的接口方法;InvocationHandler對象:表示的是當(dāng)動態(tài)代理對象調(diào)用方法的時候?qū)嶋H執(zhí)行的會是該InvocationHandler對象上的invoke方法(即增強(qiáng)方法)。在invoke方法中通過反射方法名稱去執(zhí)行實際要執(zhí)行的方法和增強(qiáng)操作。(實例說明可以看文末補(bǔ)充)
注意返回的這里自定義的newProxyInstance是Object。
invoke方法中的args為方法傳入的參數(shù)們;
public class GodForYoungProxy implements InvocationHandler {
private Object godForYoungProxy;
//參數(shù)為Object設(shè)計一個向上轉(zhuǎn)型
public Object newProxyInstance(Object godForYoungProxy) {
this.godForYoungProxy = godForYoungProxy;
//this指的是GodForYoungProxy這個InvocationHandler實現(xiàn)類
System.err.println( godForYoungProxy.getClass().getName() );
return Proxy.newProxyInstance(godForYoungProxy.getClass().getClassLoader(), godForYoungProxy.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(" God for Young begin to work ... ");
proxy = method.invoke(godForYoungProxy, args);
System.out.println(" End of work ");
return proxy;
}
}
代理實現(xiàn)效果
public class 動態(tài)代理 {
public static void main(String[] args) {
GodForYoungProxy godForYoungProxy = new GodForYoungProxy();
Human human = (Human) godForYoungProxy.newProxyInstance(new Boy());
YoungMan youngMan = (YoungMan) godForYoungProxy.newProxyInstance(new Boy());
human.sex();
youngMan.nickName();
//向上轉(zhuǎn)型測試
// Object obj = new Girl();
// Girl girl = (Girl) obj;
// System.err.println(obj.getClass().getName());
// System.err.println(obj.getClass().getInterfaces().length);
// girl.nickName();
}
}

代理生成對象中的obj向上轉(zhuǎn)型對象的.getClass().getName()打印:

要點:InvocationHandler補(bǔ)充
結(jié)合創(chuàng)建動態(tài)代理實例對象目錄內(nèi)容補(bǔ)充說明如下:官網(wǎng)的Proxy.newProxyInstance中的入?yún)⒄f明
loader – the class loader to define the proxy class
interfaces – the list of interfaces for the proxy class to implement
h – the invocation handler to dispatch method invocations to =>將方法調(diào)用分派到的調(diào)用處理程序
同樣的上述代碼實現(xiàn)效果,我們將實現(xiàn)的GodForYoungProxy implements InvocationHandler中的invoke方法注釋一行:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(" God for Young begin to work ... ");
// proxy = method.invoke(godForYoungProxy, args);
System.out.println(" End of work ");
return proxy;
}
明顯的,我們發(fā)現(xiàn)少了實際的sex和nickName方法的輸出內(nèi)容,因為沒有實現(xiàn)對應(yīng)調(diào)用方法:

通過這樣的特性,其實我們在invoke方法里面擁有很強(qiáng)的操作性,比如說讓指定方法執(zhí)行,對不同方法執(zhí)行不同策略等。
代理模式和修飾模式的區(qū)別
代理模式和裝飾者模式很相似,但是他們的區(qū)別在于:
1、代理模式是在類編譯的時候,增強(qiáng)方法就已經(jīng)確定的,有些動態(tài)代理不支持多層嵌套;裝飾者則可以不斷遞歸被構(gòu)造裝飾;
2、代理模式:強(qiáng)調(diào)對對象的訪問控制,方法的使用都是通過反射實現(xiàn);代理模式則是強(qiáng)調(diào)功能的增加,相當(dāng)于在原基礎(chǔ)的被修飾者上不斷套娃。
總結(jié)
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
springboot serverEndpoint導(dǎo)致@resource注解不生效
在SpringBoot中,@Resource注解用于注入依賴,本文主要介紹了springboot serverEndpoint導(dǎo)致@resource注解不生效,具有一定的參考價值,感興趣的可以了解一下2023-12-12
java實現(xiàn)順序結(jié)構(gòu)線性列表的函數(shù)代碼
java實現(xiàn)順序結(jié)構(gòu)線性列表的函數(shù)代碼。需要的朋友可以過來參考下,希望對大家有所幫助2013-10-10
如何解決Maven打包時每次都出現(xiàn)Download maven-metadata.xml卡住問題
這篇文章主要介紹了如何解決Maven打包時每次都出現(xiàn)Download maven-metadata.xml卡住問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05
java ExecutorService CompletionService線程池區(qū)別與選擇
這篇文章主要為大家介紹了java ExecutorService CompletionService線程池區(qū)別與選擇使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09

