淺談Java和SAP ABAP的靜態代理和動態代理,以及ABAP面向切面程式設計的嘗試
文章目錄
- Java的靜態代理
- 靜態代理的優缺點
- ABAP的靜態代理
- Spring AOP的動態代理
- JDK動態代理的優缺點
- CGLIB動態代理的優缺點
- ABAP CGLIB的模擬實現
- ABAP Pre和Post Exit
Jerry之前一篇文章 SAP產品增強技術回顧,提到基於Java程式語言實現的SAP Commerce,藉助Spring框架的支援,能使用面向切面程式設計的理念(Aspect Orient Programming,以下簡稱AOP),將業務程式碼和非業務程式碼(比如許可權檢查,日誌記錄,效能統計等)徹底分離開。
下圖是某應用裡方法的常規實現:許可權檢查,日誌記錄和效能檢測的程式碼一次又一次地侵入到本應只包含業務程式碼的三個方法中:
下圖是應用AOP之後的方法實現:三個方法體內只包含純粹的業務程式碼,看起來清爽了很多。許可權檢查,日誌記錄和效能檢測的程式碼,作為仍需關注的三個方面,以切面的方式編織到三個方法中。Weave,AOP裡的術語,中文材料裡經常譯成“編織”,描述了被代理類的方法通過非原始碼修改層面被增添以新邏輯的動作。
我們說物件導向程式設計(Object Oriented Programming,簡稱OOP)是一種理念,不同的程式語言可以有不同的實現。同理,AOP這種理念,不同的程式語言也存在不同的實現。
Java AOP的實現可以分為靜態代理和動態代理兩種。無論哪種代理方式,一言以蔽之,AOP的核心為,業務邏輯位於原始類中始終保持不變,而編織的非業務邏輯位於代理類中。執行時執行的程式碼,實際上被呼叫的是代理類,原始類的業務邏輯通過代理類被間接地呼叫。
代理模式的UML圖:
業務邏輯在編譯期間被編織進入代理類的方式,稱為靜態代理;業務邏輯在執行期間才進行編織的方式,稱為動態代理。準確地說,編譯期編織還可細分為編譯時和編譯後編織,而執行期間編織又可細分為載入時編織和執行時編織,但這種細分方式不影響本文接下來的闡述,所以後續仍只按照編譯期和執行期兩大類來介紹。
看一些具體的例子。
Java靜態代理
定義一個IDeveloper的介面,裡面包含一個writeCode的方法。建立一個Developer類,實現該方法。
測試:建立一個名為Jerry的Developer例項,呼叫writeCode方法。
假設我想讓Developer在寫程式碼之前,先編寫對應的文件,但我不想把寫文件這個邏輯,侵入到writeCode方法裡。這裡“編寫文件”,就相當於待編織的非業務邏輯,或者叫做待編織的切面邏輯。
使用靜態代理的思路,另外新建一個代理類DeveloperProxy:
注意上圖的writeCode方法,首先第8行完成文件編寫的任務,然後代理類在第9行呼叫被代理類Developer的writeCode方法,完成寫程式碼的實際業務邏輯。
測試程式碼:
Developer和DeveloperProxy都實現了同一個介面IDeveloper,對於消費者程式碼來說,它完全感知不到也不必要去感知這兩個介面實現類的內部差異——這一切對消費者程式碼來說完全透明。消費者拿到的引入,指向的是型別為IDeveloper介面的變數,然後呼叫定義在介面上的writeCode方法即可。
靜態代理的優缺點
從以上例子可以看出,靜態代理工作的基石是介面,如果原始類由於某種原因,無法改造成為某個介面的實現類(比如原始類來自系統遺留程式碼,無法重構),則靜態代理這條路行不通。
針對每個原始類,採用靜態代理,都需要建立一個具有持久儲存的代理類。這種方式便於理解,並且非業務邏輯(前例中的“寫文件”行為)在編譯期間植入靜態代理類,實際執行時效能優於即將介紹的動態代理。
在Java裡如果不想手動建立靜態代理類,可以使用工具AspectJ來自動完成。由於本文的讀者主要是ABAP開發人員,這裡略過其使用方式。
ABAP靜態代理類的自動建立
我仿照Java AspectJ的思路,用ABAP寫了一個類似的原型。下面是使用方法。
首先我建立一個類CL_HELLOWORLD:
我想自動為該類建立一個靜態代理,在代理類的PRINT方法裡,除了呼叫這個原始類的PRINT方法外,再做一些額外的邏輯,比如列印一些輸出。
呼叫下圖的GET_PROXY方法,將自動為CL_HELLOWORLD建立一個靜態代理類,將第7行和第8行指定的額外邏輯編織到靜態代理類的PRINT方法裡:
測試:呼叫靜態代理類的PRINT方法,得到下圖的輸出,能觀察到編織到靜態代理類的兩行WRITE語句,分別在原始類PRINT方法之前和之後被呼叫了:
SE24可以觀察到,通過我寫的工具自動建立的ABAP靜態類,及編織到代理類方法PRINT裡的額外邏輯:
這個工具的核心是呼叫ABAP Class API生成新的ABAP類,原始碼可以在文末Jerry提供的連結裡獲得:
Spring AOP的動態代理
所謂動態代理,即AOP框架在編譯期不會對原始類做任何處理,而是直到應用執行期間,在記憶體中臨時為需要被代理的類生成一個AOP物件,該物件包含了原始類的全部方法,並且在被代理的方法處做了增強處理,編織入新的邏輯,並回撥原始類的方法。
Spring AOP動態代理有兩種實現方式:JDK動態代理和CGLIB動態代理。
JDK動態代理
JDK動態代理的原理是基於Java反射機制實現的方法攔截器機制。
我們在第一個例子的基礎上,增添一個新的ITester介面,代表測試人員這個崗位:
現在的需求是給測試人員的doTesting方法內也植入編寫文件的邏輯。如果採用靜態代理的方式,我們得又建立一個TesterProxy的靜態代理類。隨著開發小組裡人員崗位型別的增加,這些靜態代理類的個數也隨之增加。
那麼用動態代理如何優雅地避免這個問題呢?
建立一個新的代理類,取名為EnginnerProxy,名字暗示了這個實現了JDK標準介面InnovationHandler的類,在執行時能統一代理一個軟體開發團隊裡所有角色的工程師類的方法。
第七行的bind方法,接收一個被代理類的例項,在執行時動態為該例項建立一個臨時的代理類例項。所謂臨時,指該代理例項的生命週期只存在於當前會話中,應用執行結束後即銷燬,不會像靜態代理類那樣會持久化儲存。
執行時代理類的方法一旦執行,無論是Developer的writeCode, 還是Tester的doTesting方法,均會被EnginnerProxy的invoke方法攔截,在invoke方法內統一執行第17行的文件撰寫邏輯,然後再呼叫18行包含了業務邏輯的原始類方法。
下圖是測試程式碼及執行結果,現在無論是Developer還是Tester,在寫程式碼和做測試之前,都會自動執行文件撰寫的任務了:
基於JDK動態代理的優缺點
顯而易見,在需要代理多個類時,動態代理只需建立一個統一的代理類,而不必像靜態代理那樣,需要為每個包含業務邏輯的類單獨建立代理類。而代理類“用後即焚”,也避免了在工程資料夾裡生成太多代理類。
另一方面,因為動態代理在執行時通過Java反射機制實現,執行時的效能劣於在編譯期間進行代理邏輯編織的靜態代理。此外,JDK動態代理工作的前提條件同靜態代理一樣,也需要被代理的類實現某個介面。
看個反例,假設產品經理類ProductOwner未實現任何介面:
使用JDK動態代理,在執行時會拋ClassCastException異常:
正因為JDK動態代理的這種侷限性,存在另一種動態代理的實現方式:基於CGLIB的動態代理。
CGLIB(Code Generation Library)是一個Java位元組碼生成庫,可以在執行時對Java類的位元組碼進行處理和增強,底層基於位元組碼處理框架ASM實現。
基於CGLIB的動態代理可以繞過JDK動態代理的限制,即使一個需要被代理的類沒有實現任何介面,也能使用CGLIB動態代理。
注意這次使用CGLIB建立的統一代理類,匯入的開發包來自net.sf.cglib.proxy, 而非JDK動態代理解決方案中的java.lang.reflect:
消費程式碼的風格同JDK動態代理類似:
CGLIB動態代理的優缺點
CGLIB克服了JDK動態代理需要被代理類必須實現某個介面才能工作的限制,然而其本身也有侷限性。CGLIB本質上是執行時用API操作Java類的位元組碼的方式,直接建立一個繼承自被代理類的子類,然後將切面邏輯編織到這個子類方法中去。顯而易見,如果被代理類被定義成無法繼承,比如被Java和ABAP裡的final關鍵字修飾,則CGLIB動態代理這種方式也無法工作。
做一個測試,我將ProductOwner類標誌為final,即無法被繼承,這時在執行之前的測試程式碼,會遇到異常和錯誤訊息:Cannot subclass final class
ABAP動態代理
因為ABAP無法在語言層面精確做到像Java JDK InnovationHandler那樣能夠用一個代理類統一攔截多個被代理類方法執行的效果,因此Jerry選擇對另一種動態代理,即CGLIB代理方式,用ABAP進行模擬。
首先建立一個需要被代理的類,業務邏輯寫在GREET方法裡。
接著使用Jerry自己實現的ABAP CGLIB工具類,通過其方法GET_PPROXY得到這個類的代理類,並呼叫代理類的GREET方法:
上圖第8行和第9行是包含了兩個切面邏輯的類,我期望其方法分別在被代理類的GREET呼叫之前和呼叫之後被執行。
ABAP CGLIB的核心在GET_PROXY方法裡的generate_proxy方法內:
這裡使用了ABAP動態生成類的關鍵字GENERATE SUBROUTINE POOL, 根據內表mt_source裡包含的預先拼湊好的原始碼,生成新的臨時類。這個類不會在SE24或者SE80裡儲存,僅僅存活在當前應用的會話裡。
第17行動態生成新的代理類之後,第21行生成一個該代理類的例項,然後在第23和26行分別植入切面邏輯。
最後呼叫這個代理類例項的GREET方法,列印輸出如下:
其中Hello World是原始被代理類即ZCL_JAVA_CGLIB的GREET方法的輸出,而它的前後兩行為呼叫ABAP CGLIB生成代理類時傳入的切面邏輯。
到目前為止,儘管我們意識到靜態代理和動態代理都各自存在一些缺陷,但從這些缺陷出現的原因,也再次提醒我們,在編寫新的程式碼時,要儘量面向介面程式設計,儘量避免直接面向實現程式設計,從而降低程式的耦合性,提高應用的可維護性,可複用性和可擴充套件性。
以上介紹的ABAP CGLIB工具只是Jerry開發的一個原型,在ABAP裡如果僅僅想將切面邏輯(比如許可權檢查,日誌記錄,效能分析)徹底地同業務邏輯隔離開,可以使用ABAP Netweaver提供的對類方法增強的標準方式:Pre-Exit和Post-Exit.
選中要增強的類,點選Enhance選單:
這種增強和被代理的類是分開儲存的:
建立新的Pre-Exit:
點選Pre-Exit的皮膚,就可以進去編寫程式碼了:
在執行時,被代理類ZCL_JAVA_CGLIB的GREET方法執行之前,Pre-Exit裡的程式碼會自動觸發:
Jerry之前在SAP Business By Design這個產品工作的時候,在不修改產品標準程式碼的前提下,用這種Exit技術實現了很多的客戶需求。典型的客戶需求是,在SAP標準UI增添擴充套件欄位,其值通過後臺複雜的邏輯計算出來。於是我們首先把後臺API的Response結構體做增強,新建一個擴充套件欄位;然後給後臺API取數方法建立一個Post-Exit,將擴充套件欄位的填充邏輯實現在Exit裡。
採用Pre和Post-Exit,雖然使用方式上和Java Spring AOP基於註解(Annotation)的工作方式相比有所差異,但從效果上看,也能實現Spring AOP將業務邏輯和非業務邏輯嚴格分開的需求。
本文介紹的Java和ABAP的靜態和動態代理,以及ABAP模擬Java CGLIB的實現,在Jerry釋出的SAP社群部落格上有詳細敘述:
-
Implement CGLIB in ABAP
-
Create dynamic proxy persistently in Java and ABAP
-
Various Proxy Design Pattern implementation variants in Java, ABAP and JavaScript
本文提到的Jerry開發的所有ABAP原型和工具,在這個 連結裡有原始碼。
今後如果有人聊到關於ABAP能否進行面向切面程式設計的話題,您或許可以提到Jerry這篇文章。感謝閱讀。
要獲取更多Jerry的原創文章,請關注公眾號"汪子熙":
ABAP專題
-
Jerry的ABAP, Java和JavaScript亂燉
-
ABAP開發人員未來應該學些什麼
-
Jerry 2017年的五一小長假:
8種經典排序演算法的ABAP實現
-
Jerry的ABAP原創技術文章合集
-
300行ABAP程式碼實現一個最簡單的區塊鏈原型
-
使用Java+SAP雲平臺+SAP Cloud Connector呼叫ABAP On-Premise系統裡的函式
-
在SAP雲平臺的CloudFoundry環境下消費ABAP On-Premise OData服務
-
ABAP vs Java, 蛙泳 vs 自由泳
-
聊聊C語言和ABAP
-
動手使用ABAP Channel開發一些小工具,提升日常工作效率
-
我用ABAP做過的那些無聊的事情
-
不喜歡SAP GUI?
那試試用Eclipse進行ABAP開發吧
-
使用Visual Studio Code編寫和啟用ABAP程式碼
-
你的ABAP程式給佛祖開過光麼?
來試試Jerry這個小技巧
-
在SAP雲平臺ABAP程式設計環境上編寫第一段ABAP程式
-
SAP官方釋出的ABAP程式設計規範
-
ABAP Code Inspector那些隱藏的功能,您都知道嗎?
-
還在用ABAP進行SAP產品的二次開發?來了解下這種全新的二次開發理念吧
-
ABAP Netweaver體內的那些寄生式程式語言
-
從SAP社群上的一篇部落格開始,聊聊SAP產品命名背後的那份情懷
-
雲端的ABAP Restful服務開發
-
如何在SAP雲平臺ABAP程式設計環境裡把CDS view暴露成OData服務
-
使用abapGit在ABAP On-Premises系統和SAP雲平臺ABAP環境之間進行程式碼傳輸
-
30分鐘用Restful ABAP Programming模型開發一個支援增刪改查的Fiori應用
-
Jerry帶您瞭解Restful ABAP Programming模型系列之二:Action和Validation的實現
-
Jerry帶您瞭解Restful ABAP Programming模型系列之三:
雲端ABAP應用除錯
-
SAP雲平臺上的ABAP程式設計環境裡如何消費第三方服務
-
ABAP開發者上雲的時候到了 - 現在大家可以免費使用SAP雲平臺ABAP環境的試用版了
-
學而不思則罔 - SAP雲平臺ABAP程式設計環境的由來和適用場景
-
SAP雲平臺裡的三叉戟應用
-
如何基於Restful ABAP Programming模型開發並部署一個支援增刪改查的Fiori應用
-
SAP 2019 TechEd Key Note解讀:雲時代下SAP從業人員如何做二次開發?
-
有哪些ABAP關鍵字和語法,到了ABAP雲環境上就沒辦法用了?
-
ABAP開發環境終於支援以駝峰命名法自動格式化ABAP變數名了
-
利用ABAP 740的新關鍵字REDUCE完成一個實際工作任務
-
一段讓人瑟瑟發抖的ABAP程式碼
-
昨日萬聖節ABAP怪獸級程式碼謎團,公佈答案啦
-
介紹一種在ABAP核心態進行內表高效拷貝的方法
-
使用SAP Cloud Application Programming模型開發OData的一個實際例子
-
當ABAP遇見普羅米修斯
-
使用ABAP繪製可伸縮向量圖
-
ABAP開發環境語法高亮的那些事兒
-
SAP錯誤訊息除錯之七種武器:
讓所有的錯誤訊息都能被定位
-
使用ABAP操作Excel的幾種方法
-
SAP GUI裡的收藏夾事務碼管理工具
-
SAP GUI和Windows登錄檔
-
有了Debug許可權就能幹壞事?小心了,你的一舉一動盡在系統監控中
-
ABAP CCDEF, CCIMP, CCMAC, CCAU, CMXXX這些東東是什麼鬼
-
實現ABAP條件斷點的三種方式
-
使用SAT跟蹤監控從瀏覽器開啟的SAP應用的效能和呼叫棧
-
一個13年ABAP老兵的建議:瞭解這些基礎知識,對ABAP開發有百利而無一害
-
SAP ABAP Netweaver容器化, 不可能完成的任務嗎?
-
SAP產品增強技術回顧
- SAP API開發方法大全
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/24475491/viewspace-2688374/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- SAP ABAP和Java的動態代理實現Java
- Java中的靜態代理和動態代理Java
- Java代理(jdk靜態代理、動態代理和cglib動態代理)JavaJDKCGLib
- 靜態代理和動態代理
- JAVA_動態代理AOP切面程式設計Java程式設計
- Java代理設計模式(Proxy)的四種具體實現:靜態代理和動態代理Java設計模式
- Java基礎系列-靜態代理和動態代理Java
- Java 靜態代理和動態代理的使用及原理解析Java
- JAVA 靜態代理 & 動態代理Java
- .NET Core 實現動態代理做AOP(面向切面程式設計)程式設計
- java執行原理、靜態代理和動態代理區分Java
- 設計模式總結——代理模式以及java的動態代理設計模式Java
- 靜態代理和動態代理(jdk/cglib)詳解JDKCGLib
- 設計模式(一) 動態代理初嘗試設計模式
- 面試常問的設計模式之代理模式的詳細解析!分析說明靜態代理模式和動態代理模式面試設計模式
- 輕鬆理解 Java 靜態代理/動態代理Java
- jdk的動態代理和靜態代理你還寫不出來嘛???JDK
- Java動態代理與靜態代理以及它能為我們做什麼Java
- Day67 Spring AOP(面向切面程式設計) 和代理設計模式Spring程式設計設計模式
- 靜態代理、動態代理與Mybatis的理解MyBatis
- Spring AOP裡的靜態代理和動態代理,你真的瞭解嘛?Spring
- Java代理之靜態代理Java
- java 反射和動態代理Java反射
- Java靜態代理Java
- 代理模式詳解:靜態代理、JDK動態代理與Cglib動態代理模式JDKCGLib
- 3.靜態代理&動態代理&CGlibCGLib
- AOP之靜態代理VS動態代理
- Java動態代理(JDK和cglib)JavaJDKCGLib
- Java靜態代理模式Java模式
- JDK動態代理和CGLib代理JDKCGLib
- JDK動態代理和 CGLIB 代理JDKCGLib
- 你必須會的 JDK 動態代理和 CGLIB 動態代理JDKCGLib
- 深入淺出MyBatis:反射和動態代理MyBatis反射
- Java動態代理和反射機制Java反射
- Java中的超程式設計與動態代理技術Java程式設計
- 23種設計模式之代理模式(靜態代理)設計模式
- 深入理解靜態代理與JDK動態代理JDK
- cglib動態代理和jdk動態代理的區別與應用CGLibJDK