本系列 “Spring Roo 簡介” 的 Spring Roo 簡介,第 3 部分:開發 Spring Roo 的附加組件 討論了 Spring Roo 附 加架構,以及如何使用 addon create 命令創建國際化的、簡單的附加組件。本文主要關注 Spring Roo 支持的其余兩種類 型的附加組件,即高級附加組件和包裝器附加組件。建議您在開始閱讀本文之前先閱讀第 3 部分的文章。
高級附加 組件的簡介
高級附加組件允許 Spring Roo 執行簡單附加組件所能執行的一切操作,例如,利用依賴關系或插件更 新 Maven POM 文件,更新或添加配置文件,增強現有的 Java 類型,並使用 AspectJ ITD 引入新的 Java 類型。添加源代 碼的功能使高級附加組件比所有其他附加組件都更強大。在創建一個 Spring Roo 高級附加組件之前,請先研究一下 Spring Roo 提供的現有高級附加組件。
使用中的高級附加組件
目前使用的一個高級附加組件是 JPA,它能 執行與持久性相關的工作,即為數據庫添加支持並創建新的實體。要查看此組件的運行情況,請打開 Roo shell,並在 清 單 1 中執行此命令。在本文中,我使用的是 Spring Roo V1.2.0.M1。
清單 1. JPA 示例
project -- topLevelPackage com.dw.demo --projectName entity-demo jpa setup --database FIREBIRD --provider HIBERNATE entity --class ~.domain.Book
jpa setup 和 entity 命令均等同於一個名叫 org.springframework.roo.addon.jpa 的高級附加組件。Roo shell 上的 jpa setup 和 entity 命令的輸出允許明確地對 簡單附加組件和高級附加組件進行劃分。清單 2 顯示了 JPA setup 命令的輸出。
清單 2. JPA setup 命令的輸出
Created SRC_MAIN_RESOURCES/META-INF/spring/database.properties Updated ROOT/pom.xml [added dependencies ...] Updated SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml Created SRC_MAIN_RESOURCES/META-INF/persistence.xml
jpa setup 命令的輸出顯示,它正在執行配置功能,比 如,在 pom.xml 中添加依賴關系、更新 Spring applicationContext.xml,以及創建特定於持久性的 persistence.xml 文 件。假設 JPA setup 命令相當於一個簡單的附加組件,因為它不創建或更新 Java 源代碼。在與上面顯示的設置相似的場 景中使用一個簡單的附加組件。
清單 3 顯示了 entity 命令的輸出。
清單 3. entity 命令的輸出
Created SRC_MAIN_JAVA/com/dw/demo/domain Created SRC_MAIN_JAVA/com/dw/demo/domain/Book.java Created SRC_MAIN_JAVA/com/dw/demo/domain/Book_Roo_Configurable.aj Created SRC_MAIN_JAVA/com/dw/demo/domain/Book_Roo_Jpa_Entity.aj Created SRC_MAIN_JAVA/com/dw/demo/domain/Book_Roo_Entity.aj Created SRC_MAIN_JAVA/com/dw/demo/domain/Book_Roo_ToString.aj
該輸出顯示創建了一個名為 Book.java 的 Java 文件以及四個 *.aj 文件。識別高級附加組件的黃金法則是生成 Java 文件和/或 *.aj 文件,如同 entity 命令一樣 。這些 *Roo_*.aj 文件被稱為類型間聲明 (Inter-type Declarations, ITD)。ITD 允許一個類型(一個方面)聲明另一個 類型,也就是說,您可以通過添加方法和字段或者更改它們的類型層次來修改任何類型的靜態結構。Roo 使用 ITD 作為代 碼生成構件,並管理其整個生命周期。ITD 允許 Roo 在單獨的編譯單元中生成代碼,但是無法將他們組合到相同的編譯類 中。
查看 entity 命令的輸出後,請考慮一下如何通過 Spring Roo 生成這些構件(.java 和.aj 文件)。參見 清 單 4 中的一個 Book.java 文件樣例。
清單 4. Book.java 文件
package com.dw.demo.domain; import org.springframework.roo.addon.entity.RooEntity; import org.springframework.roo.addon.javabean.RooJavaBean; import org.springframework.roo.addon.tostring.RooToString; @RooJavaBean @RooToString @RooEntity public class Book { }
除了類中的注釋以外,Java 文件看起來很普通。看一下注釋和 .aj 文件的名稱,顯然,一些注釋相當於 .aj 文件添加的函數。例如,RooToString 相當於 Book_Roo_ToString.aj 文件並添加了 toString() 方法。RooEntity 相當於 Book_Roo_Entity .aj、Book_Roo_Jpa_Entity 以及與持久性相關的一些方法。我們暫時將其余的內容先放一放。要了解如 何利用注釋生成 ITD,請先了解 Spring Roo 如何提供高級附加組件功能。
Roo shell 啟動後,會掃描所有的類,並注冊所有實現 CommandMarker 接口的類。CommandMarker 接口會告知 Roo,這 些類將定義該附加組件能執行的命令。
所有的這些高級附加組件會向 Spring Roo 提供的 OSGi 運行時注冊其服務。這些服務指定了觸發代碼生成的條件。對 於所有的高級附加組件,觸發點就是一個注釋。例如,如果 Java 類型擁有 RooToString 注釋,則只會觸發針對 toString() 方法生成的高級附加組件。這種情況也適用於其他注釋。
一旦使用了 entity --class ~.domain.Book,附加組件就會創建一個帶注釋的名為 Book.java 的 Java 文件。其他的 附加組件會在 Java 類擁有這些注釋時或擁有為它們編寫的 .aj 文件時觸發。
在創建自己的高級附加組件時,您會看見更多的相關說明。
org.springframework.roo.addon.jpa 附加組件只 是 Spring Roo 所提供的高級附加組件的一個示例。其他的高級附加組件還包括 GWT、控制器、JSON 等。Spring Roo 1.2.0 發行版本還包含兩個更高級的附加組件,即 addon-equals 和 addon-jsf。addon-equals 附加組件提供了一個實體 的 equals 和 hashcode 方法的實現,addon-jsf 則在 Spring Roo 應用程序中提供 JSF 支持。要玩轉最新的 Spring Roo 快照,請構建 Spring Roo 代碼或從 Spring Roo 存儲庫 中 下載每日快照。
在 My Entity Class 中包含 compareTo() 方法
值對象或實體通常是實現 java.lang.Comparable 接口所必需的 ,它們還提供了 compareTo() 方法的實現。Comparable 接口在實現它的每一個類的對象上進行完全排序。當您實現 Comparable 時,可以執行以下操作:
調用 java.util.Collections.sort 和 java.util.Collections.binarySearch
調用 java.util.Arrays.sort 和 java.util.Arrays.binarySearch
將對象用作 java.util.TreeMap 中的鍵
將對象用作 java.util.TreeSet 中的元素
在本文中,您將構建一個高級的附加組件,該組件將為您在應用程序中創建的實體提 供了 compareTo() 的實現。因為您想在自己的應用程序中添加 Java 代碼,所以必須創建一個高級附加組件。
項目 的創建
Spring Roo 文檔 詳細地說明了如何在 Google 代碼之上創建一個項目和 Maven 存儲庫,所以有必要在此重復一下。請 注意,我將使用 "spring-dw-roo-compareto-addon" 作為項目名稱。
如果您正在使用的不是最新版本的 Spring Roo(1.2.0.RC1),請從 項目網站 下載此版本。請解壓縮此版本並安裝它 ,如 Spring Roo 簡介,第 1 部分:從源代碼構建 所述。
Spring Roo 擯棄或移除了早期版本中使用的一些類。
創建一個高級附加組件
創建項目後,您會看到一個 名為 spring-dw-roo-compareto-addonAfter 的目錄,目錄中只有一個 .svn 文件夾。從命令行中導航至 spring-dw-roo- compareto-addon 目錄,並啟動 Roo shell。然後鍵入以下 命令:
addon create advanced --topLevelPackage org.xebia.roo.addon.compareto --projectName spring-dw-roo-compareto-addon
就這樣!您就創建了一個高級附加組件。
接下來,在 Roo shell 上,運 行 perform package 命令以創建一個附加組件 jar。清單 5 顯示了 addon create advanced 命令生成的文件。
清單 5. addon create advanced 命令生成的文件
Created ROOT/pom.xml Created SRC_MAIN_JAVA Created SRC_MAIN_RESOURCES Created SRC_TEST_JAVA Created SRC_TEST_RESOURCES Created SPRING_CONFIG_ROOT Created ROOT/readme.txt Created ROOT/legal Created ROOT/legal/LICENSE.TXT Created SRC_MAIN_JAVA/org/xebia/roo/addon/compareto Created SRC_MAIN_JAVA/org/xebia/roo/addon/compareto/ComparetoCommands.java Created SRC_MAIN_JAVA/org/xebia/roo/addon/compareto/ComparetoOperations.java Created SRC_MAIN_JAVA/org/xebia/roo/addon/compareto/ComparetoOperationsImpl.java Created SRC_MAIN_JAVA/org/xebia/roo/addon/compareto/ComparetoMetadata.java Created SRC_MAIN_JAVA/org/xebia/roo/addon/compareto/ComparetoMetadataProvider.java Created SRC_MAIN_JAVA/org/xebia/roo/addon/compareto/RooCompareto.java Created ROOT/src/main/assembly Created ROOT/src/main/assembly/assembly.xml Created SRC_MAIN_RESOURCES/org/xebia/roo/addon/compareto Created SRC_MAIN_RESOURCES/org/xebia/roo/addon/compareto/configuration.xml
一些已生成的文件,如 pom.xml、readme.txt 和 license.txt 不需要 Spring Roo 簡介,第 3 部分:開發 Spring Roo 的附加組件 所討論的任 何簡介,也不需要加以說明。更多有趣的構件包括:
ComparetoCommands.java
ComparetoOperations.java
ComparetoOperationsImpl.java
ComparetoMetadata. java
ComparetoMetadataProvider.java
RooCompareto.java
現在,依次查看每個生成的構件。
ComparetoCommands.java:該類實現了 CommandMarker 接口並展示了兩種方法(一個帶有 CliAvailablityIndicator 注釋,另一個帶有 CliCommand 注釋)。CliAvailablityIndicator 注釋告知 Spring Roo 可以 看見該命令的時間。例如,只有用戶在 Roo shell 中或直接在項目中定義持久性設置之後,才能使用 'entity' 命令。使用 @CliCommand 注釋的方法會使用 Roo shell 注冊該命令。@CliCommand 注釋擁有兩個屬性:一個是定義命令名 稱的 value 屬性;另一個是 help 屬性,它會在鍵入幫助命令時定義所顯示的幫助消息。要獲得 *Commands 類的詳細說明 ,請參閱 Spring Roo 簡介,第 3 部分:開發 Spring Roo 的附加組件。
ComparetoOperationsImpl.java:該 ComparetoCommands 類將所有工作都委托給 ComparetoOperationsImpl 類。在此類中生成的四個方法是:
isCommandAvailable():此方法由 ComparetoCommands 類中帶 CliAvailabilityIndicator ComparetoCommands 注釋的 方法進行調用,以查看該命令是否應該可見。這樣做是為了確保命令是上下文感知的。此方法可執行各種檢驗。例如,如果 已經創建了項目,則應該只能看見該命令,或者,如果已經設置了持久性,則應該只能看見該命令。並不強制一定要提供命 令可見條件。只需返回 true 來確保該命令總是可見。
setup():此方法由ComparetoCommands 類中使用 @CliCommand 注釋的 setup() 方法進行調用。此代碼清楚地表明,該類負責執行與設置相關的任務,比如,添加 Maven 依賴關系,添加 Maven 存儲庫,或者創建或更新 Spring 上下文文件(正如 Spring Roo 簡介,第 3 部分:開發 Spring Roo 的附加組件 中對 Jamon Roo 附加組件所做的操作一樣)。
annotateType():此方法與 annotateAll() 方法是簡單附加組件中沒有 的兩個新方法。該方法的功能是在特定的 Java 類型上添加一個注釋 (RooCompareto)。該方法使用了一些 Spring Roo 提 供的服務來獲取給定 Java 類的類詳細資料,並將 RooJCompareto 注釋附加到其中。
annotateAll():此方法會查找所 有使用 RooJavaBean 注釋的類型,並在所有那些類型上調用 annotateType() 方法。當所有實體都應該擁有 RooCompareto 注釋時使用此方法。
RooCompareto.java:此注釋的存在會導致附加組件生成代碼。
ComparetoMetadataProvider.java:該類是一個 Spring Roo 服務,由 Roo 調用,用於為附加組件檢索元數據。該類注 冊了添加和移除元數據的觸發器。無需在此類進行任何修改,但是請記住,該類擁有一個名為 getMetadata() 的方法,只 要存在任何帶有 RooCompareto 注釋的 Java 類型,就會通過 Spring Roo 調用該方法。
ComparetoMetadata.java:該 類是負責生成與附加組件相對應的 ITD。在該生成的代碼中,使用了一個名為 ItdTypeDetailsBuilder 的類來創建一個帶 有一個字段和方法的 ITD。在本文後面部分,您需要修改默認的生成代碼,以滿足添加一個 compareTo 方法和實現 Comparable 接口的需求。
修改附加組件以滿足需求
您可能想要創建一個將 compareTo 方法添加到實 體類的附加組件。您應該執行以下操作:
將 commons-lang V3.1 的 Maven 依賴關系添加到目標項目中。這是必須 的,因為 commons-lang 提供了一個名為 CompareToBuilder 的構建器類,可以使用它來構建 compareTo 方法。
使得實 體類實現 Comparable 接口。
為 compareTo 方法創建一個 ITD。
添加 Maven 依賴關系
要滿足這些需求 ,則需要更改 ComparetoOperationsImpl 和 ComparetoMetadata 類。依次完成這些更改。
首先,在目標項目中添 加 Maven commons-lang 依賴關系。更新 configuration.xml 文件,使之擁有 commons-lang 依賴關系,而不是默認提供 的 Spring batch 依賴關系,正如 清單 6 中所示。
清單 6. 更新 configuration.xml 文件
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <configuration> <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.1</version> </dependency> </dependencies> </configuration>
接下來,修改 ComparetoOperationsImpl 類中的 setup() 方法的實現,以便讀取 commons-lang Maven 依賴關系,而不是 Spring batch Maven 依賴關系,正如 清單 7 中所示。這裡未顯示 annotateType 和 annotateAll() 方法,因為沒有對它們進行任何更改。
清單 7. 修改 setup() 方法的實現
@Component @Service public class ComparetoOperationsImpl implements ComparetoOperations { @Reference private ProjectOperations projectOperations; @Reference private TypeLocationService typeLocationService; @Reference private TypeManagementService typeManagementService; /** {@inheritDoc} */ public void setup() { // Install the add-on Google code repository needed to get the annotation projectOperations.addRepository("", new Repository("Compareto Roo add-on repository", "Compareto Roo add-on repository", "https://spring-dw-roo-compareto-addon.googlecode.com/svn/repo")); List<Dependency> dependencies = new ArrayList<Dependency>(); // Install the dependency on the add-on jar ( dependencies.add(new Dependency("org.xebia.roo.addon.compareto", "org.xebia.roo.addon.compareto", "0.1.0.BUILD-SNAPSHOT", DependencyType.JAR, DependencyScope.PROVIDED)); Element configuration = XmlUtils.getConfiguration(getClass()); for (Element dependencyElement : XmlUtils.findElements( "/configuration/dependencies/dependency", configuration)) { dependencies.add(new Dependency(dependencyElement)); } projectOperations.addDependencies("", dependencies); } }
到目前為止所做的更改與 Spring Roo 簡介,第 3 部分:開發 Spring Roo 的附加組件 中用來創建 Jamon 簡 單附加組件的更改類似。
讓實體類實現 Comparable 接口
在代碼中添加 Maven 依賴關系後,您需要確定您 的實體類已經實現了 java.lang.Comparable 接口。為此,請修改由 ComparetoMetadata 類生成的 ITD。元數據類使用 ItdTypeDetailsBuilder 類生成 ITD,該類提供了向 ITD 添加方法、字段、注釋、接口等元素的各種添加方法。要讓 Java 類型實現一個接口,請使用 ItdTypeDetailsBuilder 類中的 addImplementsType 方法,如 清單 8 中所示。我只展示了 ComparetoMetadata 構造函數,因為 ITD 的構造是在構造函數中完成的。
清單 8. 實現 java.lang.Comparable 接 口
public ComparetoMetadata(String identifier, JavaType aspect Name, PhysicalTypeMetadata governorPhysicalTypeMetadata) { super(identifier, aspect Name, governorPhysicalTypeMetadata); Assert.isTrue(isValid(identifier), "Metadata identification string '" + identifier + "' does not appear to be a valid"); JavaType comparableInterface = new JavaType("java.lang.Comparable"); builder.addImplementsType(comparableInterface); itdTypeDetails = builder.build(); }
為 compareTo 方法創建一個 ITD
讓 Java 類型實現 Comparable 接口後,您還必須提供 compareTo 方法的實現。CompareToBuilder 類為創建 compareTo 方法提供了一個流暢接口。Spring Roo equals 附加組件使用 EqualsBuilder 和 HashcodeBuilder 來提供 equals 和 hashcode 方法的實現。讓我們舉一個例子,您一定要清楚 CompareToBuilder 是如何幫助創建 compareTo 方法。假設您擁有一個名叫 Book 的實體,並且您想要使用 CompareToBuilder 為它提供 compareTo 實現。清單 9 顯示了 Book 類和 compareTo 方法
清單 9. Book 類和 compareTo 方法
import org.apache.commons.lang3.builder.CompareToBuilder; public class Book implements Comparable { private String title; private String author; private double price; public Book(String title, String author, double price) { this.title = title; this.author = author; this.price = price; } // getters and setters public int compareTo(Book o) { if(!(o instanceof Book)){ return -1; } Book book = (Book)o return new CompareToBuilder().append(this.title, book.title).append(this.author, book.author).append(this.price, book.price).toComparison(); } @Override public String toString() { return "Book [title=" + title + ", author=" + author + ", price=" + price + "]"; } }
清單 9 中的 compareTo 方法執行下列操作:
如果 o 不是 instanceOfBook,則返回 -1
如果 o 是 instanceOfBook,則將 o 的類型強制轉換為 Book
創建一個 CompareToBuilder 類的對象,然後在字段上調用 append 方法
循序漸進地使用以下這些步驟構建 compareTo 方法:
如果 o 不是 instanceOf Book,則返回 - 1
在添加 instanceOf 檢查之前,創建 compareTo 方法。請參見 清單 10。
清單 10. 創建 compareTo 方法
public ComparetoMetadata(String identifier, JavaType aspect Name, PhysicalTypeMetadata governorPhysicalTypeMetadata) { super(identifier, aspect Name, governorPhysicalTypeMetadata); Assert.isTrue(isValid(identifier), "Metadata identification string '" + identifier + "' does not appear to be a valid"); JavaType comparableInterface = new JavaType("java.lang.Comparable"); builder.addImplementsType(comparableInterface); builder.addMethod(getCompareToMethod()); itdTypeDetails = builder.build(); } private MethodMetadata getCompareToMethod() { final JavaType parameterType = JavaType.OBJECT; final List<JavaSymbolName> parameterNames = Arrays.asList(new JavaSymbolName("obj")); final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); bodyBuilder.appendFormalLine("return -1;"); final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, new JavaSymbolName("compareTo"), JavaType.INT_PRIMITIVE, AnnotatedJavaType.convertFromJavaTypes(parameterType), parameterNames, bodyBuilder); return methodBuilder.build(); }
getCompareToMethod() 使用 MethodMetadataBuilder 類生成 compareTo 方法元數據。MethodMetadataBuilder 是一個 Spring Roo 提供的 Builder 類,用於構建方法元數據提。要構建方法元數據,首先要構造一個 MethodMetadataBuilder 對象,傳遞參數(比如訪問修飾符、方法名稱、返回類型、參數列表或方法主體構建器)為 compareTo 方法創建元數據,如 清單 11 中所示。
清單 11. instanceOf 檢查
private MethodMetadata getCompareToMethod() { final JavaType parameterType = JavaType.OBJECT; String parameterName = "obj"; final List<JavaSymbolName> parameterNames = Arrays.asList(new JavaSymbolName(parameterName)); final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); final String typeName = destination.getSimpleTypeName(); bodyBuilder.appendFormalLine("if (!(" + parameterName + " instanceof " + typeName + ")) {"); bodyBuilder.indent(); bodyBuilder.appendFormalLine("return -1;"); bodyBuilder.indentRemove(); bodyBuilder.appendFormalLine("}"); bodyBuilder.appendFormalLine("return -1;"); final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, new JavaSymbolName("compareTo"), JavaType.INT_PRIMITIVE, AnnotatedJavaType.convertFromJavaTypes(parameterType), parameterNames, bodyBuilder); return methodBuilder.build(); }
如果 o 是 instanceOfBook,則將 o 的類型強制轉換為 Book
下一個步驟是一個強制轉換,因此您 能夠構建 compareTo 方法。為此,請將此行追加到 instanceOf 檢查的後面:
bodyBuilder.appendFormalLine (typeName + " rhs = (" + typeName + ") " +
OBJECT_NAME + ";");
創建 CompareToBuilder 類的對象,然後再在字段上調用 append 方法
要構建 compareTo 方法,則需要訪問一個類中的 所有字段。ComparetoMetadata 類不包含任何有關類型的信息,所以它不能獲取該類的字段。此信息可由 ComparetoMetadataProvider 提供,如 清單 12 中所示。
清單 12. ComparetoMetadataProvider
protected ItdTypeDetailsProvidingMetadataItem getMetadata(String metadataId, JavaType aspect Name, PhysicalTypeMetadata governorPhysicalTypeMetadata, String itdFilename) { final String[] excludeFields = {}; final MemberDetails memberDetails = getMemberDetails(governorPhysicalTypeMetadata); if (memberDetails == null) { return null; } final JavaType javaType = governorPhysicalTypeMetadata.getMemberHoldingTypeDetails().getName(); final List<FieldMetadata> compareToFields = locateFields(javaType, excludeFields, memberDetails, metadataId); return new ComparetoMetadata(metadataId, aspect Name, governorPhysicalTypeMetadata, compareToFields); } private List<FieldMetadata> locateFields(final JavaType javaType, final String[] excludeFields, final MemberDetails memberDetails, final String metadataIdentificationString) { final SortedSet<FieldMetadata> locatedFields = new TreeSet<FieldMetadata>(new Comparator<FieldMetadata>() { public int compare(final FieldMetadata l, final FieldMetadata r) { return l.getFieldName().compareTo(r.getFieldName()); } }); final List<?> excludeFieldsList = CollectionUtils.arrayToList(excludeFields); final FieldMetadata versionField = persistenceMemberLocator.getVersionField(javaType); for (final FieldMetadata field : memberDetails.getFields()) { if (excludeFieldsList.contains(field.getFieldName().getSymbolName())) { continue; } if (Modifier.isStatic(field.getModifier()) || Modifier.isTransient(field.getModifier()) || field.getFieldType().isCommonCollectionType() || field.getFieldType().isArray()) { continue; } if (versionField != null && field.getFieldName().equals(versionField.getFieldName())) { continue; } locatedFields.add(field); metadataDependencyRegistry.registerDependency( field.getDeclaredByMetadataId(), metadataIdentificationString ); } return new ArrayList<FieldMetadata>(locatedFields); }
擁有這些字段後,將它們傳送到 ComparetoMetadata,以便能夠構建 compareTo 方法,如 清單 13 中所示 。
清單 13. 傳遞元數據以構建 compareTo 方法
private List<FieldMetadata> compareToFields; public ComparetoMetadata(String identifier, JavaType aspectName, PhysicalTypeMetadata governorPhysicalTypeMetadata, List<FieldMetadata> compareToFields) { super(identifier, aspectName, governorPhysicalTypeMetadata); Assert.isTrue(isValid(identifier), "Metadata identification string '" + identifier + "' does not appear to be a valid"); this.compareToFields = compareToFields; if (!CollectionUtils.isEmpty(compareToFields)) { JavaType comparableInterface = new JavaType("java.lang.Comparable"); builder.addImplementsType(comparableInterface); builder.addMethod(getCompareToMethod()); } itdTypeDetails = builder.build(); } private MethodMetadata getCompareToMethod() { final JavaType parameterType = JavaType.OBJECT; String parameterName = "obj"; final List<JavaSymbolName> parameterNames = Arrays.asList(new JavaSymbolName(parameterName)); final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); final ImportRegistrationResolver imports = builder.getImportRegistrationResolver(); imports.addImport( newJavaType("org.apache.commons.lang3.builder.CompareToBuilder") ); final String typeName = destination.getSimpleTypeName(); bodyBuilder.appendFormalLine("if (!(" + parameterName + " instanceof " + typeName + ")) {"); bodyBuilder.indent(); bodyBuilder.appendFormalLine("return -1;"); bodyBuilder.indentRemove(); bodyBuilder.appendFormalLine("}"); bodyBuilder.appendFormalLine(typeName + " rhs = (" + typeName + ") " + parameterName + ";"); final StringBuilder builder = new StringBuilder(); builder.append("return new CompareToBuilder()"); for (final FieldMetadata field : compareToFields) { builder.append(".append(" + field.getFieldName() + ", rhs." + field.getFieldName() + ")"); } builder.append(".toComparison();"); bodyBuilder.appendFormalLine(builder.toString()); final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, new JavaSymbolName("compareTo"), JavaType.INT_PRIMITIVE, AnnotatedJavaType.convertFromJavaTypes(parameterType), parameterNames, bodyBuilder); return methodBuilder.build(); }
測試
這完成了 compareTo 附加組件的實現。您可以從 Google 代碼存儲庫 中下載此附加組件的完整源代碼。現在,您 可以測試您剛創建的 compareTo。
退出 roo shell 並運行 mvn clean install 命令。在構建流程中,系統會要求您鍵入 GPG 通行碼。
構建 Roo 附加組件後,打開一個新的命令行並創建一個名叫 bookshop 的目錄。
導航至 bookshop 目錄並鍵入 roo 命令來打開一個 Roo shell。
在 Roo shell 中執行來自 清單 14 的命令。
清單 14. 創建附加組件
project --topLevelPackage com.xebia.roo.bookshop --projectName bookshop jpa setup --database HYPERSONIC_IN_MEMORY --provider HIBERNATE entity jpa --class ~.domain.Book field string --fieldName title --notNull field string --fieldName author --notNull field number --fieldName price --type double --notNull
要安裝並啟動該附加組件,請在 Roo shell 上鍵 入以下內容:
osgi start --url file://<location to compareTo addon jar>
這會安裝並激活您的 compareTo 附加組件。您可以使用 OSGi ps 命令查看附加組件的狀態。
鍵入 compareto 並按下選項卡,查看 清單 15 中的三個 compareto addon 命令。
清單 15. 查看 compareto addon 命令
roo> compareto
compareto add compareto all compareto setup
清單 15 中陳述的步驟會確認此 compareto 附加組件是否正確安裝。下一個步驟是運行 setup 命令,該命令將配置這些必 要的依賴關系。請參見 清單 16。
清單 16. 運行 setup 命令
roo> compareto setup Updated ROOT/pom.xml [added repository https://spring-dw-roo-compareto-addon.googlecode.com/svn/repo; added dependencies org.xebia.roo.addon.compareto:org.xebia.roo.addon.compareto: 0.1.0.BUILD, org.apache.commons:commons-lang3:3.1; removed dependency org.apache.commons:commons-lang3:3.0.1]
運行 compareto setup 命令後,下一個合 理的步驟就是向實體類添加 compareTo 方法。您可以通過 compareto add 或 compareto all 來實現此操作,具體操作取 決於您是想僅為一個實體類生成 compareTo 方法,還是想為所有實體類生成 compareTo 方法。讓我們為樣例 bookshop 應 用程序(參見 下載)中的所有實體類添加 compareTo 方法。請參見 清單 17。
清單 17. 為所有的實體類添加 compareTo 方法
roo> compareto all Updated SRC_MAIN_JAVA/com/xebia/roo/bookshop/domain/Book.java Created SRC_MAIN_JAVA/com/xebia/roo/bookshop/domain/Book_Roo_Compareto.aj
正如您在上面 compareto all 命令的輸出中所看到的,該命令會生成一個名為 Book_Roo_Compareto.aj 的 ITD。此文件將包含 compareTo 方法。清 單 18 顯示了 Book_Roo_Compareto.aj。
清單 18. Book_Roo_Compareto.aj
import org.apache.commons.lang.builder.CompareToBuilder; privileged aspect Book_Roo_Compareto { declare parents: Book implements java.lang.Comparable; public int Book.compareTo(java.lang.Object obj) { if (!(obj instanceof Book)) { return -1; } Book rhs = (Book) obj; return new CompareToBuilder().append(author, rhs.author).append(id, rhs.id).append(price, rhs.price).append(title, rhs.title).toComparison(); } }
在 Roo shell 上運行 perform package 命令,查看添加附件組件後一切是否編譯正確。令人驚訝的是,構建會 失敗,因為 Maven 不能解決 Spring Roo 綁定依賴關系的問題。這些綁定依賴關系來自於 compareTo 附加組件。您需要該 附加組件上的依賴關系,因為您的實體必須使用 Compareto 進行注釋。這是您惟一需要從附加組件中做的事情。我發現最 好的方法是創建另一個 Maven 模塊並擁有其所有附加組件的依賴關系。這跟 Spring Roo 所做的行不同。Spring Roo 不依 賴於所用的每個附加組件。它擁有一個包含所有依賴關系的通用 Spring Roo annotations jar。我創建了一個項目 xebia -spring-roo-addon-annotation 並將 Compareto 注釋放在此模塊中。接著,我更新了 configuration.xml,以便將此 jar 添加到客戶端項目,而不是附加組件 jar。 清單 19 顯示了 configuration.xml。
清單 19. configuration.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <configuration> <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.1</version> </dependency> <dependency> <groupId>org.xebia.roo.addon</groupId> <artifactId>xebia-spring-roo-addon-annotations</artifactId> <version>0.0.1</version> </dependency> </dependencies> <repositories> <repository> <id>spring-roo-addon-annoations</id> <name>Spring Roo Addon Annotation</name> <url>https://xebia-spring-roo-addon-annotations.googlecode.com/svn/repo</url> </repository> </repositories> </configuration>
更新 ComparetoOperationsImpl 類的 setup() 方法來讀取已更新的 configuration.xml 文件中指定的新依賴關系和存儲庫。請參見 清單 20。
清單 20. 更新 ComparetoOperationsImpl 類的 setup() 方法
public void setup() { bu List<Dependency> dependencies = new ArrayList<Dependency>(); Element configuration = XmlUtils.getConfiguration(getClass()); for (Element dependencyElement : XmlUtils.findElements("/configuration/dependencies/dependency", configuration)) { dependencies.add(new Dependency(dependencyElement)); } projectOperations.addDependencies("", dependencies); List<Element> repositories = XmlUtils.findElements( "/configuration/repositories/repository", configuration); for (Element repositoryElement : repositories) { Repository repository = new Repository(repositoryElement); projectOperations.addRepository(projectOperations.getFocusedModuleName(), repository); } }
接著執行以下步驟:
通過運行 mvn clean install 再次構建附加組件。
更新客戶端,正如您在 步驟 4 中生成它一樣。
要移除舊版附加組件,請在 Roo shell 中鍵入此命令:
addon remove -- bundleSymbolicName
org.xebia.roo.addon.compareto
通過運行 osgi install 命令再次安裝該附加組件。
安裝附加組件後,運行 compareto setup 和 compareto all 命令。
您將看到 compareTo ITD。運行 perform package 命令,一切表現良好。
一旦測試到附加組件正在部署環境中運行,就可以將它放入您所創建的 Google 代 碼項目中。要向外部世界發布該附加組件,請遵循與 Spring Roo 簡介,第 3 部分:開發 Spring Roo 的附加組件 中發布 i18n 附加組件相同的過程。同樣地,要使用 RooBot 注冊附加組件,請遵循 Spring Roo 文檔。
實現非 OSGi JDBC 驅動程序 OSGi 與包裝器附加組件的兼容
包裝器附加組件通常用於將非 OSGi JDBC 驅動程序轉換成 OSGi 兼容綁定 。您需要包裝 JDBC 驅動程序的一個地方是:您必須使用 Spring Roo 對某個 Oracle 數據庫執行反向工程。由於版權問題 ,pring Roo 並沒有提供 OSGi Oracle JDBC 驅動程序。在對一個 Oracle 數據庫執行反向工程之前,首先要實現驅動程序 OSGi 的兼容性。要為 Oracle JDBC 驅動程序創建一個包裝器附加組件,請執行如下操作:
通過鍵入以下命令,將 Oracle JDBC 安裝在您的本地機器的 Maven 目錄中。
mvn install:install-file -Dfile=ojdbc5.jar - DgroupId=com.oracle
-DartifactId=ojdbc5 -Dversion=11.2.0.2 -Dpackaging=jar
創建一個名為 oracle-wrapper-addon 的新目錄,並從此命令行導航至該目錄。
打開 Roo shell 並執行包裝器附加組件命令:addon create wrapper --topLevelPackage com.oracle.wrapper.jdbc --groupId com.oracle --artifactId ojdbc5 --version 11.2.0.2 --vendorName Oracle --licenseUrl oracle.com
該命令只生成 pom.xml 文件,該文件將用於將一個非 OSGi Oracle JDBC 驅動器轉換成一個 OSGi 驅動程序。
在 Roo shell 內運行此命令,以創建該 OSGi 綁定:perform command --mavenCommand bundle:bundle
就這樣,您現在已經成功創建了一個非 OSGi jar 的 OSGi 綁定。
結束語
在本文中,您了解了 Spring Ro 中的高級附加組件和包裝器附加組件。還學習了如何創建高級附加組件和包 裝器附加組件。本文完成了探索 Spring Roo 的一個重要特性的旅程:編寫附加組件。無論何時想擴展 Spring Roo 函數, 請記得考慮創建附加組件。
在本系列 "Spring Roo 簡介" 的下一篇文章,我們將討論如何使用 Spring Roo 編寫 GWT 應用程序。
下載