靜態(tài)代理和動(dòng)態(tài)代理
?
如何理解代理?
????????代理是一種軟件設(shè)計(jì)模式,它允許一個(gè)對象(稱為代理對象)控制對另一個(gè)對象(稱為真實(shí)對象)的訪問。代理對象可以通過在真實(shí)對象的前后添加額外邏輯來增強(qiáng)或限制真實(shí)對象的行為。
簡單來說代理可以幫助原對象進(jìn)行功能性增強(qiáng)(在原有的功能上添加新的功能)。
例如:明星約談先關(guān)業(yè)務(wù)后達(dá)成,收錢后,開始準(zhǔn)備唱歌,而唱歌需要前需要準(zhǔn)備場地和先關(guān)設(shè)備。此時(shí)如果該明星有以為代理(或者說經(jīng)紀(jì)人)可以幫明星進(jìn)行相關(guān)的業(yè)務(wù)洽談,收取演唱費(fèi)用,以及準(zhǔn)備相關(guān)的場地和設(shè)施,明星就只負(fù)責(zé)唱歌就可以了,對明星而言這樣效率更高業(yè)務(wù)能力更強(qiáng)了(可以理解為增強(qiáng))。
靜態(tài)代理案例:
下文關(guān)于靜態(tài)代理相關(guān)代碼的思路:
是使用靜態(tài)代理模式來實(shí)現(xiàn)對明星對象的代理。靜態(tài)代理是指在編譯時(shí)就已經(jīng)確定代理類和被代理類的關(guān)系,代理類與被代理類實(shí)現(xiàn)相同的接口或繼承相同的父類。
具體步驟如下:
首先定義一個(gè)接口或者父類,作為明星對象和代理對象的共同接口。在這個(gè)示例中,可以定義一個(gè)名為
Star
的接口,其中包含了明星對象和代理對象都需要實(shí)現(xiàn)的方法,比如singSongs()
,talk()
等。創(chuàng)建一個(gè)真正的明星對象類
Realstar
,實(shí)現(xiàn)Star
接口,并實(shí)現(xiàn)其中的方法。這個(gè)類是真正執(zhí)行唱歌、談話等操作的類,即被代理的對象。創(chuàng)建一個(gè)代理對象類
StarProxy
,同樣實(shí)現(xiàn)Star
接口,并實(shí)現(xiàn)其中的方法。這個(gè)類中含有一個(gè)成員變量,用于存儲真正的明星對象,以便在代理對象中調(diào)用。同時(shí),還可以在代理對象中實(shí)現(xiàn)一些額外的操作,比如收費(fèi)、約談項(xiàng)目等。在代理對象的方法中,可以根據(jù)需要在執(zhí)行真正的明星對象方法之前或之后進(jìn)行一些額外的操作。例如,在
singSongs()
方法中,可以在調(diào)用真正的明星對象的singSongs()
方法之前輸出一些準(zhǔn)備信息,在方法之后輸出一些結(jié)束信息。在測試類中,實(shí)例化一個(gè)真正的明星對象
Realstar
,并設(shè)置其用戶信息。然后,實(shí)例化一個(gè)代理對象StarProxy
,并設(shè)置其用戶代理信息和真正的明星對象??梢酝ㄟ^調(diào)用代理對象的方法來間接調(diào)用真正的明星對象的方法,同時(shí)享受到代理對象中實(shí)現(xiàn)的一些額外操作。
通過這樣的設(shè)計(jì),可以在不修改真正的明星對象的情況下,增加一些額外的功能和管理操作。代理對象作為真正明星對象的中介,可以處理一些與明星工作相關(guān)的事務(wù),起到解耦和管理的作用。
代碼示例:
????????為貼合實(shí)際便于理解定義一個(gè)people類
明星接口
真正明星實(shí)現(xiàn)類
明星代理實(shí)現(xiàn)類
代理或者在此處貼合情景叫經(jīng)紀(jì)人,經(jīng)紀(jì)人也實(shí)現(xiàn)明星接口,可以理解為,經(jīng)紀(jì)人是為明星做代理工作的(做一些工作管理,減輕明星負(fù)擔(dān)),所以代理(或者說經(jīng)紀(jì)人)需要知道明星有哪些技能好做的工作,所以說明星本來要做的工作,現(xiàn)在有代理去分擔(dān)一部分,唱歌的主要工作還由明星本人來,代理幫助明星做一些其他工作提高效率(可以理解為代理的作用是加強(qiáng)了明星的業(yè)務(wù)能力)。
?測試類
運(yùn)行效果:

上述代碼簡單梳理:
People
類是一個(gè)普通的JavaBean,用來表示一個(gè)人的信息,包括姓名和年齡。Realstar
類實(shí)現(xiàn)了Star
接口,它是一個(gè)真正的明星實(shí)現(xiàn)類。在這個(gè)類中,使用了@Autowired注解將People
對象注入進(jìn)來,通過setter方法設(shè)置。該類實(shí)現(xiàn)了明星接口中的方法,包括singSongs()
、pay()
和talk()
。在singSongs()
方法中,打印輸出明星的準(zhǔn)備信息,并返回演唱結(jié)束的提示信息;在pay()
方法中,打印輸出為明星付款的金額;在talk()
方法中,打印輸出約談演唱會(huì)事宜的信息。StarProxy
類也實(shí)現(xiàn)了Star
接口,它是一個(gè)明星代理實(shí)現(xiàn)類,相當(dāng)于經(jīng)紀(jì)人。在這個(gè)類中,同樣使用了@Autowired注解將People
對象和Realstar
對象注入進(jìn)來。它實(shí)現(xiàn)了明星接口中的方法,其中singSongs()
方法調(diào)用了Realstar
對象的singSongs()
方法,實(shí)現(xiàn)了對明星唱歌的代理;pay()
方法打印輸出代理收費(fèi)的金額;talk()
方法打印輸出為明星代理約談項(xiàng)目的信息。
通過這樣的設(shè)計(jì),可以實(shí)現(xiàn)對真正明星對象的代理,代理對象可以在執(zhí)行真正明星對象的方法之前或之后進(jìn)行一些額外的操作,如收費(fèi)、管理和代辦事務(wù)等。同時(shí),通過依賴注入,注入了People
對象,使得真正明星對象和代理對象都可以獲取到People
對象的信息,以便在方法中使用。
動(dòng)態(tài)代理(JDK方式)
動(dòng)態(tài)代理類是在運(yùn)行時(shí)動(dòng)態(tài)生成的代理類,用于代理目標(biāo)對象的方法調(diào)用。下面我將講解如何創(chuàng)建一個(gè)動(dòng)態(tài)代理類以及如何使用它。
動(dòng)態(tài)代理類的創(chuàng)建步驟如下:
????1. 創(chuàng)建一個(gè)實(shí)現(xiàn)了 `InvocationHandler` 接口的類,該類用于處理代理對象方法的調(diào)用邏輯。在該類中,需要重寫 `invoke()` 方法。
????2. 在 `invoke()` 方法中,根據(jù)需要對不同的方法進(jìn)行特定的處理。比如可以在方法調(diào)用前后進(jìn)行額外的操作,或者對方法的參數(shù)進(jìn)行校驗(yàn)等。
????3. 使用 `Proxy.newProxyInstance()` 方法創(chuàng)建代理對象。該方法需要傳入三個(gè)參數(shù):
?????????? - `ClassLoader`: 類加載器,用于加載代理類??梢允褂媚繕?biāo)對象的類加載器或者其他類加載器。
?????????? - `Interfaces`: 代理類需要實(shí)現(xiàn)的接口列表。目標(biāo)對象必須實(shí)現(xiàn)這些接口。
?????????? - `InvocationHandler`: 處理代理對象方法調(diào)用的邏輯。傳入上一步創(chuàng)建的`InvocationHandler` 實(shí)例。
????4. 將返回的代理對象強(qiáng)制轉(zhuǎn)換為目標(biāo)對象所實(shí)現(xiàn)的接口類型(如果有多個(gè)接口,則選擇其中一個(gè))。
使用動(dòng)態(tài)代理類的步驟如下:
????1. 創(chuàng)建目標(biāo)對象,即要被代理的對象。
????2. 創(chuàng)建上述提到的實(shí)現(xiàn)了 `InvocationHandler` 接口的類的實(shí)例,并在構(gòu)造方法中傳入目標(biāo)對象。
????3. 調(diào)用實(shí)現(xiàn)類的 `creatStar()` 方法獲取代理對象。這個(gè)方法內(nèi)部會(huì)根據(jù)傳入的目標(biāo)對象創(chuàng)建代理對象。
????4. 通過代理對象調(diào)用方法,實(shí)際上是調(diào)用了實(shí)現(xiàn)類中的 `invoke()` 方法。
????總結(jié)來說,動(dòng)態(tài)代理類在運(yùn)行時(shí)生成,可以處理目標(biāo)對象的方法調(diào)用,并且可以在方法調(diào)用前后執(zhí)行額外的操作。使用動(dòng)態(tài)代理類時(shí),需要自定義一個(gè) `InvocationHandler` 接口的實(shí)現(xiàn)類,并將目標(biāo)對象傳入該類中,然后通過 `Proxy.newProxyInstance()` 方法創(chuàng)建代理對象。最后,通過代理對象調(diào)用方法即可實(shí)現(xiàn)代理功能。
代理類:
測試類:
上述動(dòng)態(tài)代理類的代碼簡單梳理:
這段代碼是使用 JDK 動(dòng)態(tài)代理實(shí)現(xiàn)的代理類。下面對代碼進(jìn)行分析和講解:
Starproxy02
類是代理類,它實(shí)現(xiàn)了動(dòng)態(tài)代理的邏輯。在構(gòu)造方法中接收一個(gè)Realstar
對象作為參數(shù),表示要代理的目標(biāo)對象。
creatStar()
方法用于創(chuàng)建代理對象。在這個(gè)方法中,通過調(diào)用Proxy.newProxyInstance()
方法創(chuàng)建代理對象。該方法需要傳入三個(gè)參數(shù):
ClassLoader
: 類加載器,用來加載代理類。
Interfaces
: 代理類需要實(shí)現(xiàn)的接口列表。
InvocationHandler
: 處理代理對象方法調(diào)用的邏輯。
InvocationHandler
是一個(gè)接口,定義了一個(gè)invoke()
方法,用于處理代理對象的方法調(diào)用。在這段代碼中,創(chuàng)建了一個(gè)匿名內(nèi)部類實(shí)現(xiàn)了InvocationHandler
接口。在這個(gè)實(shí)現(xiàn)類中,重寫了invoke()
方法。在
invoke()
方法中,首先通過判斷method.getName().equals("singSongs")
判斷方法是否是singSongs()
方法,如果是,則打印輸出 "動(dòng)態(tài)代理調(diào)用singSongs",然后調(diào)用真實(shí)對象的singSongs()
方法并返回結(jié)果。如果不是
singSongs()
方法,說明是其他方法,直接通過反射調(diào)用真實(shí)對象的相應(yīng)方法,并返回結(jié)果。在
AnnoTest
類的main()
方法中,創(chuàng)建了一個(gè)Realstar
對象realstar
,并將其傳入Starproxy02
的構(gòu)造方法中初始化代理類。然后通過調(diào)用creatStar()
方法得到代理對象star1
。最后,通過調(diào)用代理對象的方法,可以看到對于
singSongs()
方法,會(huì)輸出 "動(dòng)態(tài)代理調(diào)用singSongs",并且調(diào)用了真實(shí)對象的singSongs()
方法;對于其他方法,則直接調(diào)用真實(shí)對象的相應(yīng)方法。這段代碼通過 JDK 提供的動(dòng)態(tài)代理機(jī)制,實(shí)現(xiàn)了對
Realstar
類的代理。在代理過程中,利用了InvocationHandler
接口的invoke()
方法動(dòng)態(tài)處理方法調(diào)用。使用動(dòng)態(tài)代理能夠更加靈活地處理不同的目標(biāo)對象,避免了手動(dòng)編寫大量的代理類。
?運(yùn)行效果:

動(dòng)態(tài)代理(cglib方式)
CGLIB(Code Generation Library)是一個(gè)基于字節(jié)碼生成庫的第三方庫,用于生成和修改Java類的字節(jié)碼。它可以實(shí)現(xiàn)對類的動(dòng)態(tài)代理,即創(chuàng)建目標(biāo)類的子類作為代理類。
CGLIB的動(dòng)態(tài)代理與基于接口的 JDK 動(dòng)態(tài)代理有所不同。JDK 動(dòng)態(tài)代理要求目標(biāo)對象實(shí)現(xiàn)至少一個(gè)接口,而 CGLIB 動(dòng)態(tài)代理則可以對沒有實(shí)現(xiàn)接口的類進(jìn)行代理。CGLIB 動(dòng)態(tài)代理通過生成目標(biāo)類的子類來實(shí)現(xiàn)代理,子類繼承了目標(biāo)類的行為,并且可以覆蓋或增加新的方法。
使用 CGLIB 的動(dòng)態(tài)代理,你需要引入 CGLIB 庫并使用它提供的 Enhancer 類來創(chuàng)建代理對象。以下是一個(gè)示例:
上述示例中,`CglibDynamicProxy` 實(shí)現(xiàn)了 `MethodInterceptor` 接口,并重寫了 `intercept` 方法。在 `createProxy` 方法中,創(chuàng)建了一個(gè) `Enhancer` 對象,并設(shè)置了目標(biāo)類和回調(diào)對象。在 `intercept` 方法中,可以實(shí)現(xiàn)對目標(biāo)類方法的增強(qiáng)邏輯。`proxy.invokeSuper(obj, args)` 方法通過反射調(diào)用目標(biāo)對象的方法。
使用 CGLIB 創(chuàng)建代理對象的示例代碼如下:
在上述代碼中,`RealStar` 是目標(biāo)對象,`CglibDynamicProxy` 是代理對象,通過 `proxy.createProxy(realStar)` 創(chuàng)建了代理對象,并將其轉(zhuǎn)型為 `Star` 接口類型。然后可以調(diào)用代理對象的方法,代理對象會(huì)在方法調(diào)用前后添加額外的處理邏輯。
需要注意的是,CGLIB 動(dòng)態(tài)代理的原理是生成目標(biāo)類的子類,因此目標(biāo)類不能是 `final` 類型,并且被代理的方法不能是 `final` 或 `static` 的。同時(shí),如果目標(biāo)類的方法是 `final` 類型的,那么無法對該方法進(jìn)行代理。
動(dòng)態(tài)代理和靜態(tài)代理的區(qū)別人
動(dòng)態(tài)代理和靜態(tài)代理是設(shè)計(jì)模式中的兩種代理模式,它們在實(shí)現(xiàn)上有一些區(qū)別。
?????????? 1.靜態(tài)代理: 靜態(tài)代理是在編譯時(shí)就已經(jīng)確定的代理關(guān)系,代理類和目標(biāo)類都需要實(shí)現(xiàn)同一個(gè)接口或繼承同一個(gè)父類。代理類在編譯時(shí)就已經(jīng)確定了目標(biāo)類,并且在代理類中手動(dòng)編寫了對目標(biāo)方法的調(diào)用邏輯。
示例代碼中的StarProxy
類就是一個(gè)靜態(tài)代理的例子,它在編譯時(shí)就已經(jīng)確定了對Realstar
類的代理關(guān)系,通過在代理類中手動(dòng)調(diào)用Realstar
類的方法實(shí)現(xiàn)代理。
靜態(tài)代理的優(yōu)點(diǎn)是可以在代理類中加入額外的邏輯處理,如安全性檢查、日志記錄等。但缺點(diǎn)是代理類和目標(biāo)類耦合度高,每個(gè)目標(biāo)類都需要對應(yīng)一個(gè)代理類,如果目標(biāo)類很多,代理類的數(shù)量也會(huì)相應(yīng)增多,導(dǎo)致代碼冗余。
????????????2.動(dòng)態(tài)代理: 動(dòng)態(tài)代理是在運(yùn)行時(shí)生成代理類的代理方式,不需要手動(dòng)編寫代理類,而是利用Java提供的相關(guān)API動(dòng)態(tài)生成代理類。動(dòng)態(tài)代理可以代理任意的接口或類,無需提前確定目標(biāo)類。
動(dòng)態(tài)代理利用反射機(jī)制,在運(yùn)行時(shí)動(dòng)態(tài)生成代理類,代理類中的方法調(diào)用會(huì)通過InvocationHandler接口中的invoke()方法動(dòng)態(tài)轉(zhuǎn)發(fā)到目標(biāo)類的方法。因此,動(dòng)態(tài)代理能夠更加靈活地處理不同的目標(biāo)對象,不需要為每個(gè)目標(biāo)對象都手動(dòng)編寫一個(gè)專門的代理類。