怎么理解Java和SAPABAP的靜態(tài)代理和動(dòng)態(tài)代理

本篇內(nèi)容介紹了“怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

為貴溪等地區(qū)用戶提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及貴溪網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、貴溪網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!

下圖是某應(yīng)用里方法的常規(guī)實(shí)現(xiàn):權(quán)限檢查,日志記錄和性能檢測(cè)的代碼一次又一次地侵入到本應(yīng)只包含業(yè)務(wù)代碼的三個(gè)方法中:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

下圖是應(yīng)用AOP之后的方法實(shí)現(xiàn):三個(gè)方法體內(nèi)只包含純粹的業(yè)務(wù)代碼,看起來(lái)清爽了很多。權(quán)限檢查,日志記錄和性能檢測(cè)的代碼,作為仍需關(guān)注的三個(gè)方面,以切面的方式編織到三個(gè)方法中。Weave,AOP里的術(shù)語(yǔ),中文材料里經(jīng)常譯成“編織”,描述了被代理類的方法通過(guò)非源代碼修改層面被增添以新邏輯的動(dòng)作。

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

我們說(shuō)面向?qū)ο缶幊?Object Oriented Programming,簡(jiǎn)稱OOP)是一種理念,不同的編程語(yǔ)言可以有不同的實(shí)現(xiàn)。同理,AOP這種理念,不同的編程語(yǔ)言也存在不同的實(shí)現(xiàn)。

Java AOP的實(shí)現(xiàn)可以分為靜態(tài)代理和動(dòng)態(tài)代理兩種。無(wú)論哪種代理方式,一言以蔽之,AOP的核心為,業(yè)務(wù)邏輯位于原始類中始終保持不變,而編織的非業(yè)務(wù)邏輯位于代理類中。運(yùn)行時(shí)執(zhí)行的代碼,實(shí)際上被調(diào)用的是代理類,原始類的業(yè)務(wù)邏輯通過(guò)代理類被間接地調(diào)用。

代理模式的UML圖:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

業(yè)務(wù)邏輯在編譯期間被編織進(jìn)入代理類的方式,稱為靜態(tài)代理;業(yè)務(wù)邏輯在運(yùn)行期間才進(jìn)行編織的方式,稱為動(dòng)態(tài)代理。準(zhǔn)確地說(shuō),編譯期編織還可細(xì)分為編譯時(shí)和編譯后編織,而運(yùn)行期間編織又可細(xì)分為載入時(shí)編織和運(yùn)行時(shí)編織,但這種細(xì)分方式不影響本文接下來(lái)的闡述,所以后續(xù)仍只按照編譯期和運(yùn)行期兩大類來(lái)介紹。

看一些具體的例子。

Java靜態(tài)代理

定義一個(gè)IDeveloper的接口,里面包含一個(gè)writeCode的方法。創(chuàng)建一個(gè)Developer類,實(shí)現(xiàn)該方法。

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

測(cè)試:創(chuàng)建一個(gè)名為Jerry的Developer實(shí)例,調(diào)用writeCode方法。

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

假設(shè)我想讓Developer在寫代碼之前,先編寫對(duì)應(yīng)的文檔,但我不想把寫文檔這個(gè)邏輯,侵入到writeCode方法里。這里“編寫文檔”,就相當(dāng)于待編織的非業(yè)務(wù)邏輯,或者叫做待編織的切面邏輯。

使用靜態(tài)代理的思路,另外新建一個(gè)代理類DeveloperProxy:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

注意上圖的writeCode方法,首先第8行完成文檔編寫的任務(wù),然后代理類在第9行調(diào)用被代理類Developer的writeCode方法,完成寫代碼的實(shí)際業(yè)務(wù)邏輯。

測(cè)試代碼:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

Developer和DeveloperProxy都實(shí)現(xiàn)了同一個(gè)接口IDeveloper,對(duì)于消費(fèi)者代碼來(lái)說(shuō),它完全感知不到也不必要去感知這兩個(gè)接口實(shí)現(xiàn)類的內(nèi)部差異——這一切對(duì)消費(fèi)者代碼來(lái)說(shuō)完全透明。消費(fèi)者拿到的引入,指向的是類型為IDeveloper接口的變量,然后調(diào)用定義在接口上的writeCode方法即可。

靜態(tài)代理的優(yōu)缺點(diǎn)

從以上例子可以看出,靜態(tài)代理工作的基石是接口,如果原始類由于某種原因,無(wú)法改造成為某個(gè)接口的實(shí)現(xiàn)類(比如原始類來(lái)自系統(tǒng)遺留代碼,無(wú)法重構(gòu)),則靜態(tài)代理這條路行不通。

針對(duì)每個(gè)原始類,采用靜態(tài)代理,都需要?jiǎng)?chuàng)建一個(gè)具有持久存儲(chǔ)的代理類。這種方式便于理解,并且非業(yè)務(wù)邏輯(前例中的“寫文檔”行為)在編譯期間植入靜態(tài)代理類,實(shí)際運(yùn)行時(shí)性能優(yōu)于即將介紹的動(dòng)態(tài)代理。

在Java里如果不想手動(dòng)創(chuàng)建靜態(tài)代理類,可以使用工具AspectJ來(lái)自動(dòng)完成。由于本文的讀者主要是ABAP開(kāi)發(fā)人員,這里略過(guò)其使用方式。

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

ABAP靜態(tài)代理類的自動(dòng)創(chuàng)建

我仿照J(rèn)ava AspectJ的思路,用ABAP寫了一個(gè)類似的原型。下面是使用方法。

首先我創(chuàng)建一個(gè)類CL_HELLOWORLD:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

我想自動(dòng)為該類創(chuàng)建一個(gè)靜態(tài)代理,在代理類的PRINT方法里,除了調(diào)用這個(gè)原始類的PRINT方法外,再做一些額外的邏輯,比如打印一些輸出。

調(diào)用下圖的GET_PROXY方法,將自動(dòng)為CL_HELLOWORLD創(chuàng)建一個(gè)靜態(tài)代理類,將第7行和第8行指定的額外邏輯編織到靜態(tài)代理類的PRINT方法里:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

測(cè)試:調(diào)用靜態(tài)代理類的PRINT方法,得到下圖的輸出,能觀察到編織到靜態(tài)代理類的兩行WRITE語(yǔ)句,分別在原始類PRINT方法之前和之后被調(diào)用了:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

SE24可以觀察到,通過(guò)我寫的工具自動(dòng)創(chuàng)建的ABAP靜態(tài)類,及編織到代理類方法PRINT里的額外邏輯:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

這個(gè)工具的核心是調(diào)用ABAP Class API生成新的ABAP類,源代碼可以在文末Jerry提供的鏈接里獲得:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

Spring AOP的動(dòng)態(tài)代理

所謂動(dòng)態(tài)代理,即AOP框架在編譯期不會(huì)對(duì)原始類做任何處理,而是直到應(yīng)用運(yùn)行期間,在內(nèi)存中臨時(shí)為需要被代理的類生成一個(gè)AOP對(duì)象,該對(duì)象包含了原始類的全部方法,并且在被代理的方法處做了增強(qiáng)處理,編織入新的邏輯,并回調(diào)原始類的方法。

Spring AOP動(dòng)態(tài)代理有兩種實(shí)現(xiàn)方式:JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理。

JDK動(dòng)態(tài)代理

JDK動(dòng)態(tài)代理的原理是基于Java反射機(jī)制實(shí)現(xiàn)的方法攔截器機(jī)制。

我們?cè)诘谝粋€(gè)例子的基礎(chǔ)上,增添一個(gè)新的ITester接口,代表測(cè)試人員這個(gè)崗位:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

現(xiàn)在的需求是給測(cè)試人員的doTesting方法內(nèi)也植入編寫文檔的邏輯。如果采用靜態(tài)代理的方式,我們得又創(chuàng)建一個(gè)TesterProxy的靜態(tài)代理類。隨著開(kāi)發(fā)小組里人員崗位類型的增加,這些靜態(tài)代理類的個(gè)數(shù)也隨之增加。

那么用動(dòng)態(tài)代理如何優(yōu)雅地避免這個(gè)問(wèn)題呢?

創(chuàng)建一個(gè)新的代理類,取名為EnginnerProxy,名字暗示了這個(gè)實(shí)現(xiàn)了JDK標(biāo)準(zhǔn)接口InnovationHandler的類,在運(yùn)行時(shí)能統(tǒng)一代理一個(gè)軟件開(kāi)發(fā)團(tuán)隊(duì)里所有角色的工程師類的方法。

第七行的bind方法,接收一個(gè)被代理類的實(shí)例,在運(yùn)行時(shí)動(dòng)態(tài)為該實(shí)例創(chuàng)建一個(gè)臨時(shí)的代理類實(shí)例。所謂臨時(shí),指該代理實(shí)例的生命周期只存在于當(dāng)前會(huì)話中,應(yīng)用運(yùn)行結(jié)束后即銷毀,不會(huì)像靜態(tài)代理類那樣會(huì)持久化存儲(chǔ)。

運(yùn)行時(shí)代理類的方法一旦執(zhí)行,無(wú)論是Developer的writeCode, 還是Tester的doTesting方法,均會(huì)被EnginnerProxy的invoke方法攔截,在invoke方法內(nèi)統(tǒng)一執(zhí)行第17行的文檔撰寫邏輯,然后再調(diào)用18行包含了業(yè)務(wù)邏輯的原始類方法。

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

下圖是測(cè)試代碼及運(yùn)行結(jié)果,現(xiàn)在無(wú)論是Developer還是Tester,在寫代碼和做測(cè)試之前,都會(huì)自動(dòng)執(zhí)行文檔撰寫的任務(wù)了:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

基于JDK動(dòng)態(tài)代理的優(yōu)缺點(diǎn)

顯而易見(jiàn),在需要代理多個(gè)類時(shí),動(dòng)態(tài)代理只需創(chuàng)建一個(gè)統(tǒng)一的代理類,而不必像靜態(tài)代理那樣,需要為每個(gè)包含業(yè)務(wù)邏輯的類單獨(dú)創(chuàng)建代理類。而代理類“用后即焚”,也避免了在工程文件夾里生成太多代理類。

另一方面,因?yàn)閯?dòng)態(tài)代理在運(yùn)行時(shí)通過(guò)Java反射機(jī)制實(shí)現(xiàn),運(yùn)行時(shí)的性能劣于在編譯期間進(jìn)行代理邏輯編織的靜態(tài)代理。此外,JDK動(dòng)態(tài)代理工作的前提條件同靜態(tài)代理一樣,也需要被代理的類實(shí)現(xiàn)某個(gè)接口。

看個(gè)反例,假設(shè)產(chǎn)品經(jīng)理類ProductOwner未實(shí)現(xiàn)任何接口:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

使用JDK動(dòng)態(tài)代理,在運(yùn)行時(shí)會(huì)拋ClassCastException異常:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

正因?yàn)镴DK動(dòng)態(tài)代理的這種局限性,存在另一種動(dòng)態(tài)代理的實(shí)現(xiàn)方式:基于CGLIB的動(dòng)態(tài)代理。

CGLIB(Code Generation Library)是一個(gè)Java字節(jié)碼生成庫(kù),可以在運(yùn)行時(shí)對(duì)Java類的字節(jié)碼進(jìn)行處理和增強(qiáng),底層基于字節(jié)碼處理框架ASM實(shí)現(xiàn)。

基于CGLIB的動(dòng)態(tài)代理可以繞過(guò)JDK動(dòng)態(tài)代理的限制,即使一個(gè)需要被代理的類沒(méi)有實(shí)現(xiàn)任何接口,也能使用CGLIB動(dòng)態(tài)代理。

注意這次使用CGLIB創(chuàng)建的統(tǒng)一代理類,導(dǎo)入的開(kāi)發(fā)包來(lái)自net.sf.cglib.proxy, 而非JDK動(dòng)態(tài)代理解決方案中的java.lang.reflect:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

消費(fèi)代碼的風(fēng)格同JDK動(dòng)態(tài)代理類似:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

CGLIB動(dòng)態(tài)代理的優(yōu)缺點(diǎn)

CGLIB克服了JDK動(dòng)態(tài)代理需要被代理類必須實(shí)現(xiàn)某個(gè)接口才能工作的限制,然而其本身也有局限性。CGLIB本質(zhì)上是運(yùn)行時(shí)用API操作Java類的字節(jié)碼的方式,直接創(chuàng)建一個(gè)繼承自被代理類的子類,然后將切面邏輯編織到這個(gè)子類方法中去。顯而易見(jiàn),如果被代理類被定義成無(wú)法繼承,比如被Java和ABAP里的final關(guān)鍵字修飾,則CGLIB動(dòng)態(tài)代理這種方式也無(wú)法工作。

做一個(gè)測(cè)試,我將ProductOwner類標(biāo)志為final,即無(wú)法被繼承,這時(shí)在運(yùn)行之前的測(cè)試代碼,會(huì)遇到異常和錯(cuò)誤消息:Cannot subclass final class

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

ABAP動(dòng)態(tài)代理

因?yàn)锳BAP無(wú)法在語(yǔ)言層面精確做到像Java JDK InnovationHandler那樣能夠用一個(gè)代理類統(tǒng)一攔截多個(gè)被代理類方法執(zhí)行的效果,因此Jerry選擇對(duì)另一種動(dòng)態(tài)代理,即CGLIB代理方式,用ABAP進(jìn)行模擬。

首先創(chuàng)建一個(gè)需要被代理的類,業(yè)務(wù)邏輯寫在GREET方法里。

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

接著使用Jerry自己實(shí)現(xiàn)的ABAP CGLIB工具類,通過(guò)其方法GET_PPROXY得到這個(gè)類的代理類,并調(diào)用代理類的GREET方法:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

上圖第8行和第9行是包含了兩個(gè)切面邏輯的類,我期望其方法分別在被代理類的GREET調(diào)用之前和調(diào)用之后被執(zhí)行。

ABAP CGLIB的核心在GET_PROXY方法里的generate_proxy方法內(nèi):

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

這里使用了ABAP動(dòng)態(tài)生成類的關(guān)鍵字GENERATE SUBROUTINE POOL, 根據(jù)內(nèi)表mt_source里包含的預(yù)先拼湊好的源代碼,生成新的臨時(shí)類。這個(gè)類不會(huì)在SE24或者SE80里存儲(chǔ),僅僅存活在當(dāng)前應(yīng)用的會(huì)話里。

第17行動(dòng)態(tài)生成新的代理類之后,第21行生成一個(gè)該代理類的實(shí)例,然后在第23和26行分別植入切面邏輯。

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

最后調(diào)用這個(gè)代理類實(shí)例的GREET方法,打印輸出如下:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

其中Hello World是原始被代理類即ZCL_JAVA_CGLIB的GREET方法的輸出,而它的前后兩行為調(diào)用ABAP CGLIB生成代理類時(shí)傳入的切面邏輯。

到目前為止,盡管我們意識(shí)到靜態(tài)代理和動(dòng)態(tài)代理都各自存在一些缺陷,但從這些缺陷出現(xiàn)的原因,也再次提醒我們,在編寫新的代碼時(shí),要盡量面向接口編程,盡量避免直接面向?qū)崿F(xiàn)編程,從而降低程序的耦合性,提高應(yīng)用的可維護(hù)性,可復(fù)用性和可擴(kuò)展性。

以上介紹的ABAP CGLIB工具只是Jerry開(kāi)發(fā)的一個(gè)原型,在ABAP里如果僅僅想將切面邏輯(比如權(quán)限檢查,日志記錄,性能分析)徹底地同業(yè)務(wù)邏輯隔離開(kāi),可以使用ABAP Netweaver提供的對(duì)類方法增強(qiáng)的標(biāo)準(zhǔn)方式:Pre-Exit和Post-Exit.

選中要增強(qiáng)的類,點(diǎn)擊Enhance菜單:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

這種增強(qiáng)和被代理的類是分開(kāi)存儲(chǔ)的:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

創(chuàng)建新的Pre-Exit:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

點(diǎn)擊Pre-Exit的面板,就可以進(jìn)去編寫代碼了:

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

在運(yùn)行時(shí),被代理類ZCL_JAVA_CGLIB的GREET方法執(zhí)行之前,Pre-Exit里的代碼會(huì)自動(dòng)觸發(fā):

怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理

Jerry之前在SAP Business By Design這個(gè)產(chǎn)品工作的時(shí)候,在不修改產(chǎn)品標(biāo)準(zhǔn)代碼的前提下,用這種Exit技術(shù)實(shí)現(xiàn)了很多的客戶需求。典型的客戶需求是,在SAP標(biāo)準(zhǔn)UI增添擴(kuò)展字段,其值通過(guò)后臺(tái)復(fù)雜的邏輯計(jì)算出來(lái)。于是我們首先把后臺(tái)API的Response結(jié)構(gòu)體做增強(qiáng),新建一個(gè)擴(kuò)展字段;然后給后臺(tái)API取數(shù)方法創(chuàng)建一個(gè)Post-Exit,將擴(kuò)展字段的填充邏輯實(shí)現(xiàn)在Exit里。

采用Pre和Post-Exit,雖然使用方式上和Java Spring AOP基于注解(Annotation)的工作方式相比有所差異,但從效果上看,也能實(shí)現(xiàn)Spring AOP將業(yè)務(wù)邏輯和非業(yè)務(wù)邏輯嚴(yán)格分開(kāi)的需求。

“怎么理解Java和SAP ABAP的靜態(tài)代理和動(dòng)態(tài)代理”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

分享題目:怎么理解Java和SAPABAP的靜態(tài)代理和動(dòng)態(tài)代理
文章路徑:http://bm7419.com/article18/igsdgp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈、定制網(wǎng)站、網(wǎng)站改版服務(wù)器托管、企業(yè)建站、移動(dòng)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站建設(shè)