Apache OFbiz MiniLang 源碼解讀

來源:互聯網
上載者:User

標籤:apache   ofbiz   minilang   

MiniLang所有元素的父類——MiniLangElement

MiniLang 是基於XML的“描述型語言”。所有的元素,包括節點、屬性都繼承自該類。它包含三個屬性:


  • lineNumber:表示解析MiniLang的源碼(通常是Java)所處的行號,主要是為了便於日誌記錄
  • tagName:當前元素的tag名稱,主要用於日誌記錄
  • simpleMethod:simpleMethod是一個大的“傳輸對象”,裡面實現了MiniLang支援的所有執行方式,其作用類似於serviceengine中的serviceDispatcher。
該類中沒有太多的功能代碼,除了outputTraceMessage。它用於記錄追蹤記錄檔訊息,以上提及的兩個屬性(lineNumber/tagName)主要用在這裡。
對應於服務引擎的實現——SimpleServiceEngine在之前一篇關於ofbizservice engine的剖析中,我們談到了裡面也有minilang相關的一個執行引擎,其實就是此處的SimpleServiceEngine。它繼承了GenericAsyncEngine(這裡繼承的語義並不是is-a的關係,主要是代碼複用,因為GenericAsyncEngine實現了runAsync,而其他的子引擎都沒必要實現這個介面方法)。
SimpleServiceEngine的繼承關係圖:

SimpleServiceEngine實現了父類中的兩個抽象run方法:


但這兩個方法都不包含主要的實現邏輯,真正的邏輯被定義在私人方法:serviceInvoker中,它最終是通過SimpleMethod的靜態runSimpleService方法來執行的。
BeanScript的引擎實現——SimpleMethodBsfEngine準確來說,它是對IBM的Beanscripting 庫的適配器。用於在minilang中調用Beanscript。它實現了如下方法:

其中apply以及call未提供實現(拋出異常),它在內部維護了一個內容物件,用來存放BSFDeclaredBean,而該內容物件用於為SimpleMethod的執行提供另一個上下文。維護內容物件通過如下兩個方法:
  • declareBean
  • undeclareBean
方法執行的主要邏輯實現位於eval中。它通過方法參數找到SimpleMethod對象,然後構造執行的內容物件,調用SimpleMethod對象的exec執行。
MiniLang真正的執行者——SimpleMethodSimpleMethod繼承自MiniLangElement,說明它也是MiniLang中的一個元素。SimpleMethod包含了很多跟執行任務相關的代碼:

當然這些方法大多是應對MiniLang可以應用場合的重載,可以看到它可以應用於簡單方法、事件以及服務。最終真正的執行方法是exec方法。
當然還有一些方法是靜態協助方法。比如readOperations,它返回當前元素所有子項目所被代表的MethodOperation執行個體的集合,(關於MethodOperation後面會講到,每個minilang節點都最終被影射為一個MethodOperation,由Java解釋執行)。
驗證檢查器——MiniLangValidate由於MiniLang是採用xml來描述任務的“語言”,因此它並不具備程式設計語言的很多編譯檢查機制。所以它採用了一些輔助方法來進行檢查:包括了驗證屬性、驗證子項目、測試元素是否為空白若為空白設定預設值、測試某屬性是否為常量值等、是strict 模式還是寬鬆模式以及錯誤處理等等
MiniLang的協助類——MiniLangUtil該類是MiniLang的協助類,提供了一些方法:調用SimpleMethod的方法、類型轉換/擷取的方法、設定MiniLang內容的方法等
欄位驗證及轉換器——SimpleMapProcessorSimpleMapProcessor從內容物件中擷取到欄位資訊,然後進行驗證以及轉換(當然真正的驗證以及轉換邏輯並不是由他來實現的,它只是起到觸發動作),然後將驗證通過的欄位放入目的地組合中去。MiniLang會定義很多這樣的MapProcessor,而SimpleMapProcessor主要用於擷取這些所有的mapProcessor(內部擷取,不對外公開)然後運行他們:

對於所有的mapProcessor,SimpleMapProcessor支援兩種緩衝,一種是基於字串xmlResource作key的緩衝,一種是基於URLxmlURL為key的緩衝。而所有的runSimpleMapProcessor都是先擷取到mapProcessor集合中特定的mapProcessor,然後調用其exec方法來執行驗證以及轉換動作(後面會有介紹)。
操作描述的基類——SimpleMapOperationMiniLang中提供了很多的operation,而所有這些operation都繼承自SimpleMapOperation這個抽象類別:


SimpleMapOperation的構造方法接收兩個參數:
  • element:一個xml元素
  • simpleMapProcess:SimpleMapProcess類的執行個體
構造方法中還初始化了一些參數,比如failMessage、failProperty等。
另外提供了一個方法addMessage,它用於結合failMessage、failProperty,以提供更加完善的錯誤資訊。另一個很重要的方法exec被標記為abstract,以待子類的實現。
字串參數綜合處理器——SimpleMapProcess該類是MiniLang中參數的綜合處理器。它包含一個readOperations方法,用於遍曆當前節點的所有子節點,分別對這些子節點的節點名稱進行匹配並產生各種不同的SimpleMapOperation的執行個體,然後加入到集合中去。
而該類也提供了一個exec方法,它遍曆所有的處理器,一個一個得對它們運行各處理器自己的exec方法以完成相應的處理。
提供比較操作的基類——BaseCompareMiniLang中有很多操作是比較操作,而BaseCompare就是執行比較的抽象基類,從很明顯看出它繼承自SimpleMapOperation。
在建構函式中,它從參數element中提取三個屬性:
  • operator: 比較操作符
  • type: 比較的物件類型
  • format:格式
另外還有三個方法:

  • doRealCompare是真正用於比較的靜態方法,它依賴於ofbiz的其他實現,返回bool值的比較結果
  • doCompare調用doRealCompare獲得比較結果,如果結果為false,則調用SimpleMapOperation的addMessage方法,添加失敗資訊
  • exec:空實現,這是符合語義的,因為這隻是個compareoperation
值比較子——Compare該類提供對輸入參數的比較功能。在建構函式中,它提取該元素的value屬性,作為要比較的對象之一。
該類對exec方法的實現為:
從方法參數inMap(MiniLang執行所需的所有傳入參數的集合)中獲得某個fieldName的值(此處fieldName,正是SimpleMapProcess當前正在處理元素中提取出來的)作為另一個比較對象,然後調用BaseCompare的doCompare進行比較。
欄位比較子——CompareField上面的Compare提供某個參數的值跟MiniLang定義的某個元素的值進行比較。而CompareField提供某個參數的值跟MiniLang定義的某個欄位的參數值進行比較,這個聽著比較繞口,看看代碼就明白了:
Compare:
public Compare(Element element, SimpleMapProcess simpleMapProcess) {        super(element, simpleMapProcess);        this.value = element.getAttribute("value");    }    @Override    public void exec(Map<String, Object> inMap, Map<String, Object> results, List<Object> messages, Locale locale, ClassLoader loader) {        Object fieldValue = inMap.get(fieldName);        doCompare(fieldValue, value, messages, locale, loader, true);    }

CompareField:
public CompareField(Element element, SimpleMapProcess simpleMapProcess) {        super(element, simpleMapProcess);        this.compareName = element.getAttribute("field");    }    @Override    public void exec(Map<String, Object> inMap, Map<String, Object> results, List<Object> messages, Locale locale, ClassLoader loader) {        Object compareValue = inMap.get(compareName);        Object fieldValue = inMap.get(fieldName);        doCompare(fieldValue, compareValue, messages, locale, loader, false);    }

MiniLang的拷貝操作——Copy該類提供將一個參數從輸入參數列表拷貝到輸出參數列表。在構造方法中,擷取元素需要拷貝到的目標key,並擷取是否替換以及如果為null則設定相關的配置。exec方法中主要實現拷貝動作。
對欄位的非空檢測——NotEmpty該類用於檢測當前欄位是否為非空(null或者Null 字元串),如果為空白則設定錯誤資訊。樣本:
<not-empty>            <fail-property resource="AccountingUiLabels" property="AccountingCardNumberMissing"/>        </not-empty>

MiniLang的類型轉換器——ConvertConvert類用於轉換某個處於輸入參數集合中的參數然後將其放入輸出參數集合。在構造方法中,它先獲得要處理元素的“to-field”屬性,然後獲得要轉換的類型。在exec中,擷取要轉換的對象,進行轉換並放入輸出參數集合
MiniLang的Regex驗證器——RegexpRegexp用於Regex驗證,從處理元素中擷取expr屬性,然後在exec方法中,先將當前欄位轉換為String,然後對其進行正則匹配。
MiniLang的驗證方法調用器——ValidateMethodminilang支援對字串調用指定的驗證方法進行驗證,形如:
<validate-method method="isAnyCard" class="org.ofbiz.base.util.UtilValidate">            <fail-property resource="AccountingUiLabels" property="AccountingCardNumberIncorrect"/>        </validate-method>

在構造方法中,它先去擷取該元素的method屬性以及class屬性。其exec方法,先獲得該欄位的值,然後將其轉換為String類型,獲得當前線程的classloader,載入該類,擷取該方法(值得一提的,這裡只支援靜態方法)。因為在用反射調用該方法的時候並沒有傳入該類的執行個體。
字串輸入參數產生器——MakeInStringOperationMiniLang支援根據配置產生特定的字串值作為一個輸入參數,這裡涉及到幾種不同的字串產生操作類型:

MakeInStringOperation是抽象基類,它僅僅定義了一個抽象方法:exec。
  • PropertyOper:基於Property的產生參數
  • InFieldOper:基於輸入參數列表(inMap)產生參數
  • ConstantOper:追加“字串常量”產生參數
字串產生器的容器——MakeInStringMakeInString是上面這些MakeInStringOperation的容器,在建構函式中,它先獲得當前元素的field屬性,作為某個參數的key。然後擷取當前元素的所有子項目,判斷節點名為其產生不同的MakeInStringOperation並放入一個集合中去。
在exec方法中,遍曆所有的MakeInStringOperation,分別執行它們的exec方法,並擷取它們返回的結果。
真實的Map處理器——MapProcessor之前上面有提到SimpleMapProcessor其實是一個總處理器,而真正的處理器是MapProcessor。所謂的處理包括兩個方面:
  • makeInStrings:對應miniLang中的“make-in-string”子節點
  • simpleMapProcess:對應miniLang中的“process”子節點
樣本:
<simple-map-processor name="newAffilPostalPurpose">    <make-in-string field="contactMechPurposeTypeId"><constant>BILLING_LOCATION</constant></make-in-string>    <process field="contactMechId"><copy/><not-empty>    <fail-property resource="PartyUiLabels" property="PartyContactMechIdMissing"/></not-empty></process>    <process field="contactMechPurposeTypeId"><copy/></process>  </simple-map-processor>

沒錯,其實之前我們一直都在談跟這兩個方面相關的實作類別。
MapProcessor用兩個集合來放置上面的兩種處理器。在構造方法中,它先去遍曆如上說的兩個子節點,來初始化這兩個集合。
而同樣不出意外的是,exec會分別遍曆兩個處理器中的執行個體來處理map中的參數。
元素實體的基類——MethodOperation我們進入到miniLang認可的合法“文法”的部分了,它們非常得多,我們先看一下包的整合關係圖:

MethodOperation正是這些包裡所有類的抽象基類。MethodOperation繼承自MiniLangElement。它擁有一個受保護的構造方法,需要兩個參數:
  • element:當前表示的元素
  • simpleMethod:SimpleMethod類的執行個體
主要用於初始化MiniLangElement。
它定義了一個抽象方法exec,該方法接收一個內容物件。返回一個bool值,用於指示指令碼的執行是否需要繼續。
該類定義了一個用於建立MethodOperation的工廠介面,主要包含了建立MethodOperation的介面方法。
值得一提的是,MethodOperation類中還定義了一個DeprecatedOperation註解,用於標示已廢棄的MethodOperation。
MiniLang中跟Entity相關的實體毫無疑問,跟entity相關的所有操作都是由Delegator代理的,這個在之前的entityengine相關的文章中有介紹過。在miniLang中需要對entity進行操作,就需要將那些文法翻譯為Delegator操作它們的java代碼。而這些都被封裝在entityopspackage中。並且這裡每個實作類別中都包含有一個實現了MethodOperation內定義的工廠介面的靜態內部類,用於建立特定的工廠。
另外每個實作類別的toString方法都還原了miniLang中當前節點的字串表達。
MiniLang中跟if判斷條件有關的判斷實體
  • IfNotEmpty:對應miniLang中的if-not-empty節點,用於判斷當前欄位是否為空白。在建構函式中,它會解析出其所有子節點的操作以及如果有else元素節點,則同時解析出else元素下的所有子節點操作。在exec方法中,先判斷如果滿足不為空白的條件,則執行if下的所有操作,否則如果else節點下操作不為空白,則執行這些操作
  • IfInstanceOf:判斷某個對象是否是某個類的執行個體。而至於處理過程跟IfNotEmpty類似
  • CheckPermission:檢查許可權,在之前的一篇文章中,我們簡單談了一下ofbiz的許可權設計,其中有多個層面上的許可權檢查。由於minilang在ofbiz中可以實現service、eca、seca,因此在其中也必須嵌入對許可權的檢查文法。在該類中,定義了一個內部實體類:PermissionInfo。它用於從CheckPermission節點,提取出permission,action。並定義有一個hasPermission方法,用於判斷使用者有無實體操作許可權或是否通過了認證授權。在CheckPermission的exec方法中調用hasPermission進行判斷。
MiniLang中跟條件相關的判斷實體
  • Conditional:所有condition實體所繼承的介面,該介面包含兩個方法
    • checkCondition:檢查條件,返回Bool值結果
    • prettyPrint:列印,主要用於執行Assert命令時,給出完整的錯誤資訊
  • ConditionalFactory:一個用於建立條件實體的工廠,需要它的部分原因是:miniLang可以被擴充用於支援額外的條件元素(通過繼承該類然後提供自訂的實現)。該類是一個抽象泛型類,在靜態構造器中,它擷取配置過的所有的ConditionalFactory並加以緩衝。該類包含一個靜態方法,以及兩個抽象方法:
    • makeConditional:從緩衝中擷取一個factory並建立某個condition的執行個體
    • createCondition:有待所有condition實現的建立方法
    • getName:有待所有condition實現的返回其名稱的方法
    • 因此下面的每個condition實作類別,都會建立一個靜態內部工廠類並繼承ConditionalFactory以及之前提到過的Factory(MethodOperation語義相關)。
  • Compare:一個抽象類別(也可以理解為協助類),提供各種條件比較。包含了一個doCompare方法,由多個內部靜態類實現。
  • CombinedCondition:該類是一個抽象類別,它用於實現由and/or/not/xor串連的多個條件的判斷。它並沒有繼承MethodOperation(沒有實現之前的exec方法,因此它也不是一個簡單的operation),而是繼承自MiniLangElement,但同時實現了Conditional介面(總得來看,Conditional提供比MethodOperation更窄的“介面”,但Conditional只負責條件相關的比較操作)。它擁有一個subConditions集合,該集合在構造方法中被初始化同時存放當前節點的子節點對應的condition。該類並沒有對具體的邏輯操作符進行區分實現,但對ConditionalFactory的實現卻給出了基於不同邏輯操作符的實現(多個不同的靜態內部類)。在每個不同的factory中,會根據自身的語義執行個體化CombinedCondition。
  • MasterIf/ElseIf:這兩個類用於聯合實現if/ else if / else的條件判斷邏輯。
MiniLang中跟環境檢查/設定相關的操作實體
ventops 包中包含了一些miniLang中支援的事件,比如:欄位與請求參數之間的互相轉換;欄位與session參數之間的轉換。在必須實現的exec方法中,首先必須判斷當前的methodType必須為EVENT,其他的並無不同。
MiniLang中跟調用相關的操作實體
在miniLang中支援對其他組件的調用,比如Service、bsh、script、classMethod等。這些都實現在callops包中,雖然它們之間並無相互間的關聯關係。但它們都繼承自MethodOperation。並且每個類內部都實現了一個靜態內部工廠。
MiniLang中其他動作實體
otherops包內涉及了計算、跟蹤、日誌記錄相關的幾個非功能性操作的實現。
跟方法相關的參數基類——MethodObject
MethodObject是一個抽象類別,提供多MiniLang中傳遞參數的抽象。它包含三個抽象方法:
  • 獲得對象的值:getObject
  • 獲得類型的Class對象:getTypeClass
  • 獲得類型名稱的字串表示:getTypeName
以及兩個實作類別:
  • FieldObject:抽象類別通用參數類型
  • StringObject:抽象了字串的類型表示
欄位的通用對象表示——FieldObjectMiniLang的文法中有很多field屬性,它們對應到程式裡的Model類就是FieldObject。該類有一個關鍵屬性就是type。至於方法,最關鍵的方法是getObject,它從訊息內容物件(MethodContext)中擷取其值。
字串的表示類——StringObject該對象的執行個體表示一個JavaString對象,用於傳遞到MiniLang進行方法調用。它具有兩個屬性:
  • 當前XML元素的文本值,位於xml的開/閉標籤之間的值
  • 當前XML元素的“value”屬性的值
關鍵方法為:getObject,它將串連value屬性的值跟文本值,作為一個整體字串返回。
訊息元素的表示類——MessageElementMiniLang對於方法通常都可以配置fail-message,用於方法調用產生異常時提供相關的失敗資訊。該類即用於解析並封裝此對象。
方法調用的內容物件——MethodContextMethodContext是一個大對象,它包含MiniLang中方法執行需要的一切對象資訊。其中的關鍵對象:
  • delegator:Delegator的執行個體,用於代理ofbiz中所有的資料訪問
  • dispatcher:LocalDispatcher的執行個體,用於代理ofbiz中所有的服務訪問
  • request:HttpServletRequest的執行個體,表示用戶端的請求對象
  • response:HttpServletResponse的執行個體,表示對用戶端個請求的響應對象
  • userLogin:GenericValue的執行個體,封裝了使用者身份認證
另外,提供了一些get/put方法。

Apache OFbiz MiniLang 源碼解讀

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.