天天看點

關于Eclipse Modeling Framework進行模組化,第二部分使用 Eclipse Modeling Framework 進行模組化,第 2 部分

Eclipse 的 Java Emitter Templates(JET) 是一個開放源代碼工具,可以在 Eclipse Modeling Framework(EMF)中生成代碼。 JET 與 JSP 非常類似,不同之處在于 JET 功能更強大,也更靈活,可以生成 Java、 SQL 和任何其他語言的代碼,包括 JSP。本文将介紹如何建立和配置 JET,并将其部署到各種環境中。

開發人員通常都使用一些工具來生成常用的代碼。 Eclipse 使用者可能對一些标準的工具非常熟悉,這些工具可以為標明的屬性生成 for(;;) 循環, main() 方法,以及標明屬性的通路方法。将這些簡單而機械的任務變得自動化,可以加快程式設計的速度,并簡化程式設計的過程。在某些情況中,例如為 J2EE 伺服器生成部署代碼,自動生成代碼就可以節省大量時間,并可以隐藏具體實作特有的一些複雜性,這樣就可以将程式部署到不同的 J2EE 伺服器上。自動生成代碼的功能并不隻是為開發大型工具的供應商提供的,在很多項目中都可以使用這種功能來提高效率。 Eclipse 的 JET 被包裝為 EMF 的一部分,可以簡單而有效地向項目中添加自動生成的代碼。本文将介紹在各種環境中如何使用 JET 。

JET 與 JSP 非常類似:二者使用相同的文法,實際上在背景都被編譯成 Java 程式;二者都用來将呈現頁面與模型和控制器分離開來;二者都可以接受輸入的對象作為參數,都可以在代碼中插入字元串值(表達式),可以直接使用 Java 代碼執行循環、聲明變量或執行邏輯流程控制(腳本);二者都可以很好地表示所生成對象的結構,(Web 頁面、Java 類或檔案),而且可以支援使用者的詳細定制。

JET 與 JSP 在幾個關鍵的地方存在差別。在 JET 中,可以變換标記的結構來支援在不同的語言中生成代碼。通常 JET 程式的輸入都是一個配置檔案,而不是使用者的輸入(當然也不禁止這樣使用)。而且對于一個給定的工作流來說,JET 通常隻會執行一次。這并不是技術上的限制;您可以看到 JET 有很多完全不同的用法。

開始

要使用 JET,建立一個新 Java 項目 JETExample ,并将源檔案夾設定為 src 。為了讓 JET 啟用這個項目,請點選滑鼠右鍵,然後選擇 Add JET Nature。這樣就會在新項目的根目錄下建立一個 templates 目錄。JET 的預設配置使用項目的根目錄來儲存編譯出來的 Java 檔案。要修改這種設定,打開該項目的 properties 視窗,選擇 JET Settings,并将 source container 設定為 src 。這樣在運作 JET 編譯器時,就會将編譯出來的 JET Java 檔案儲存到這個正确的源檔案夾中。

現在我們已經準備好建立第一個 JET 了。JET 編譯器會為每個 JET 都建立一個 Java 源檔案,是以習慣上是将模闆命名為 NewClass.javajet ,其中 NewClass 是要生成的類名。雖然這種命名方式不是強制的,但是這樣可以避免産生混亂。

首先在模闆目錄中建立一個新檔案 GenDAO.javajet 。這樣系統會出現一個對話框,警告您在這個新檔案的第 1 行第 1 列處有編譯錯誤。如果您詳細地看以下警告資訊,就會發現它說 "The jet directive is missing"(沒有 jet 指令)。雖然這在技術上沒有什麼錯誤,因為我們剛才隻不過是建立了一個空檔案,但是這個警告資訊卻很容易産生混亂并誤導我們的思路。單擊 'OK' 關閉警告對話框,然後單擊 'Cancel' 清除 New File 對話框(這個檔案已經建立了)。為了防止再次出現這種問題,我們的首要問題是建立 jet 指令。

每個 JET 都必須以 jet 指令開始。這樣可以告訴 JET 編譯器編譯出來的 Java 模闆是什麼樣子(并不是模闆生成了什麼内容,而是編譯生成的模闆類是什麼樣子;請原諒,這個術語有些容易讓人迷惑)。此處還要給出一些标準的 Java 類資訊。例如,在下面這個例子中使用了以下資訊:

清單 1 的内容是真正自解釋的。在編譯 JET 模闆時,會建立一個 Java 檔案 GenDAO ,并将其儲存到 com.ibm.pdc.example.jet.gen 中,它将導入指定的包。重複一遍,這隻是說明模闆像什麼樣子,而不是模闆将要生成的内容 -- 後者稍後将會介紹。注意 JET 輸出結果的 Java 檔案名是在 jet 的聲明中定義的,它并不局限于這個檔案名。如果兩個模闆聲明了相同的類名,那麼它們就會互相影響到對方的變化,而不會産生任何警告資訊。 如果您隻是拷貝并粘貼模闆檔案,而沒有正确地修改所有的 jet 聲明,那就可能出現這種情況。因為在模闆目錄中建立新檔案時會産生警告,而拷貝和粘貼是非常常見的,是以要自己小心這個問題。

JSP 可以通過預先聲明的變量(例如會話、錯誤、上下文和請求)擷取資訊, JET 與此類似,也可以使用預先聲明的變量向模闆傳遞資訊。JET 隻使用兩個隐式的變量: stringBuffer ,其類型為 StringBuffer (奇怪吧?),它用來在調用 generate() 時建構輸出字元串;以及一個參數,出于友善起見,我們稱之為 argument ,它是 Object 類型。典型的 JET 模闆的第一行會将其轉換為一個更适合的類,如清單 2 所示。

正如您可以看到的一樣,JET 的預設文法與 JSP 相同:使用 <%...%> 包括代碼,使用 <%= ... %> 列印表達式的值。與 JSP 類似,正确地使用 <% ... %> 标簽就可以添加任何邏輯循環或結構,就像是在任何 Java 方法中一樣。例如:

關于Eclipse Modeling Framework進行模組化,第二部分使用 Eclipse Modeling Framework 進行模組化,第 2 部分
關于Eclipse Modeling Framework進行模組化,第二部分使用 Eclipse Modeling Framework 進行模組化,第 2 部分

在定義完 JET 之後,儲存檔案并在包浏覽器中在這個檔案上點選滑鼠右鍵,選擇 Compile Template。如果一切正常,就會在 com.ibm.pdc.example.jet.gen 包中建立一個類 GenDAO 。其中隻有一個方法 public String generate(Object argument) (參見清單 4),這樣做的結果就是在 javajet 模闆中定義的内容。

關于Eclipse Modeling Framework進行模組化,第二部分使用 Eclipse Modeling Framework 進行模組化,第 2 部分
關于Eclipse Modeling Framework進行模組化,第二部分使用 Eclipse Modeling Framework 進行模組化,第 2 部分

編寫好模闆之後,您可能就會注意到一些公共的元素,這些元數會反複出現,例如所有生成的代碼中都添加的版權資訊。在 JSP 中,這是通過 include 聲明處理的。将所有想要添加的内容都放到一個檔案中,并将該檔案命名為 'copyright.inc',然後在 javajet 模闆中添加 <%@ include file="copyright.inc" %> 語句。所指定的包含檔案會被添加到編譯後的輸出結果中,是以它可以引用到現在為止已經聲明的任何變量。擴充名 .inc 可以任意,隻是不要采用以 jet 或 JET 結尾的名字,否則将試圖編譯包含檔案,這樣該檔案的了解性自然很差。

如果隻使用包含檔案還不能滿足要求,您可能會想添加其他一些方法,或者對代碼生成過程進行定制;最簡單的方法是建立一個新的 JET 骨架。骨架檔案就是描述編譯後的 JET 模闆樣子的一個模闆。預設的骨架如清單 5 所示。

所有的 import 語句都位于最開始, CLASS 會被替換為在 jet 聲明的 class 屬性中設定的類名, generate() 方法的代碼會被替換為執行生成操作的代碼。是以,要修改編譯後的模闆代碼的樣子,我們隻需要建立一個新的骨架檔案并進行自己想要的定制即可,但是仍然要在原來的地方保留基本的元素。

要建立一個定制的骨架,在 custom.skeleton 模闆目錄中建立一個新檔案,如清單 6 所示。

關于Eclipse Modeling Framework進行模組化,第二部分使用 Eclipse Modeling Framework 進行模組化,第 2 部分
關于Eclipse Modeling Framework進行模組化,第二部分使用 Eclipse Modeling Framework 進行模組化,第 2 部分

然後在想要使用這個定制骨架的任何 JET 模闆中,向 javajet 檔案中的 jet 聲明添加 skeleton="custom.skeleton" 屬性。

或者,也可以使用它對基類進行擴充,例如 public class CLASS extends MyGenerator ,并在基類中添加所有必要的幫助器方法。這樣可能會更加整潔,因為它保留了代碼的通用性,并可以簡化開發過程,因為 JET 編譯器并不能總是給出最正确的錯誤消息。

定制骨架也可以用來修改方法名和 generate() 方法的參數清單,這樣非常挑剔的開發人員就可以任意定制模闆。說 JET 要将 generate() 的代碼替換為要生成的代碼,其實有些不太準确。實際上,它隻會替換在骨架中聲明的最後一個方法的代碼,是以如果粗心地修改骨架的代碼,就很容易出錯,而且會讓您的同僚迷惑不解。

正如您可以看到的一樣,模闆一旦編譯好之後,就是一個标準的 Java 類。要在程式中使用這個類,隻需要分發編譯後的模闆類,而不需要分發 javajet 模闆。或者,您可能希望讓使用者可以修改模闆,并在啟動時自動重新編譯模闆。EMF 可以實作這種功能,任何需要這種功能或對此感興趣的人都可以進入 plugins/org.eclipse.emf.codegen.ecore/templates 中,并修改 EMF 生成模型或編輯器的方式。

如果您隻是希望可以隻分發編譯後的模闆類,那麼編譯過程可以實作自動化。迄今為止,我們隻看到了如何使用 JET Eclipse 插件來編譯 JET 模闆,但實際上我們可以編寫一些腳本來實作這種功能,或者将生成代碼的工作作為一項 ANT 任務。

要讓最終使用者可以定制模闆(以及對模闆的調試),可以選擇在運作時對模闆進行編譯。實作這種功能有幾種方法,首先我們使用一個非常有用的類 org.eclipse.emf.codegen.jet.JETEmitter ,它可以對細節進行抽象。常見的(但通常是錯誤的)代碼非常簡單,如清單 7 所示。

如果您試圖在一個标準的 main() 方法中運作這段代碼,就會發現第一個問題。 generate() 方法會觸發一個 NullPointerException 異常,因為 JETEmitter 假設自己正被一個插件調用。在初始化過程中,它将調用 CodeGenPlugin.getPlugin().getString() ,這個函數會失敗,因為 CodeGenPlugin.getPlugin() 為空。

關于Eclipse Modeling Framework進行模組化,第二部分使用 Eclipse Modeling Framework 進行模組化,第 2 部分
關于Eclipse Modeling Framework進行模組化,第二部分使用 Eclipse Modeling Framework 進行模組化,第 2 部分

在指令行中編譯 JET 非常簡單,不會受到 classpath 問題的影響,這個問題會使編譯一個 main() 方法都非常困難。在上面這種情況中,難點并不是将 javajet 編譯成 Java 代碼,而是将這個 Java 代碼編譯成 .class 。在指令行中,我們可以更好地控制 classpath,這樣可以分解每個步驟,最終再組合起來,就可以使整個工作順利而簡單。唯一一個技巧是我們需要以一種 "無頭" 模式(沒有使用者界面)來運作 Eclipse,但即便是這個問題也已經考慮到了。要編譯 JET,請檢視一下 plugins/org.eclipse.emf.codegen_1.1.0/test 。這個目錄中包含了 Windows 和 Unix 使用的腳本,以及一個要驗證的 JET 例子。

最終,JET 使用 "<%" 和 "%>" 來标記模闆,然而這與 JSP 使用的标記相同。如果您希望生成 JSP 程式,那就隻能修改定界符。這可以在模闆開頭的 jet 聲明中使用 startTag 和 endTag 屬性實作,如清單 9 所示。在這種情況中,我使用 "[%" 和 "%]" 作為開始定界符和結束定界符。正如您可以看到的一樣, "[%= expression %]" 可以正确處理,就像前面的 "<%= expression %>" 一樣。

關于Eclipse Modeling Framework進行模組化,第二部分使用 Eclipse Modeling Framework 進行模組化,第 2 部分
關于Eclipse Modeling Framework進行模組化,第二部分使用 Eclipse Modeling Framework 進行模組化,第 2 部分

有一個不幸的事實:很多代碼都是通過拷貝/粘貼而實作重用的,不管是大型軟體還是小型軟體都是如此。很多時候這個問題并沒有明顯的解決方案,即使面向對象語 言也不能解決問題。在重複出現相同的基本代碼模式而隻對實作稍微進行了一些修改的情況中,将通用的代碼放到一個模闆中,然後使用 JET 來生成各種變化,這是一種很好的節省時間和精力的辦法。JSP 早已采用了這種方法,是以 JET 可以從 JSP 的成功中借鑒很多東西。JET 使用與 JSP 相同的基本布局和語義,但是允許更靈活的定制。為了實作更好的控制,模闆可以進行預編譯;為了實作更高的靈活性,也可以在運作時編譯和分發。

在本系列的下一篇文章中,我們将介紹如何為 Prime Time 生成代碼,這包括允許使用者定制代碼,以及內建以域或方法甚至更細粒度級别的修改,進而允許重新生成代碼。我們還會将它們都綁定到一個插件中,進而展示一種将生成的代碼內建到開發過程的方法。

本文轉自SummerChill部落格園部落格,原文連結:http://www.cnblogs.com/DreamDrive/p/4186727.html,如需轉載請自行聯系原作者