承接《Spring Roo 簡介,第 3 部分:開發 Spring Roo 附加組件》,本文深入講解進階附加組件與包裝器附加組件的開發。建議先閱讀第 3 部分理解附加組件架構與簡單附加組件創建。
簡單附加組件僅能修改 POM、配置檔;進階附加組件額外賦予生成 Java 原始碼與 ITD (Inter-type Declarations, *.aj) 的能力,是 Spring Roo 代碼生成的核心。
Roo Shell 執行:
project --topLevelPackage com.dw.demo --projectName entity-demo jpa setup --database FIREBIRD --provider HIBERNATE entity --class ~.domain.Book
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
entity 輸出(生成 Java 與 *.aj,標誌進階附加組件):
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 僅含註解:
@RooJavaBean @RooToString @RooEntity
public class Book { }
註解驅動 ITD 生成:@RooToString → Book_Roo_ToString.aj (toString()),@RooEntity → 實體相關 ITD。ITD 允許在獨立編譯單元修改類型靜態結構(增欄位/方法/介面),Roo 管理其全生命週期。
CommandMarker 的類,註冊命令。目標:為實體自動實作 Comparable 介面與 compareTo() 方法(使用 Apache Commons Lang CompareToBuilder)。
addon create advanced --topLevelPackage org.xebia.roo.addon.compareto --projectName spring-dw-roo-compareto-addon
ComparetoCommands.java:實作 CommandMarker,@CliAvailabilityIndicator 控制命令可見性,@CliCommand 註冊命令 (value/help)。ComparetoOperations.java / ComparetoOperationsImpl.java:命令委托實作。核心方法:isCommandAvailable():上下文感知(如專案已建立)。setup():配置任務(加依賴、倉庫、Spring 上下文)。annotateType():給指定類型加註解 (@RooCompareto)。annotateAll():掃描所有 @RooJavaBean 類型批量加註解。RooCompareto.java:觸發代碼生成的註解。ComparetoMetadataProvider.java:OSGi 服務,註冊元數據增減觸發器,getMetadata() 供 Roo 呼叫。ComparetoMetadata.java:核心代碼生成類,使用 ItdTypeDetailsBuilder 構建 ITD(增欄位/方法/介面/註解)。commons-lang3:3.1 Maven 依賴。java.lang.Comparable 介面。compareTo() 方法 ITD。<configuration>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
</configuration>
public void setup() {
projectOperations.addRepository("", new Repository(...));
List<Dependency> dependencies = new ArrayList<>();
dependencies.add(new Dependency("org.xebia.roo.addon.compareto", ..., DependencyType.JAR, DependencyScope.PROVIDED));
Element configuration = XmlUtils.getConfiguration(getClass());
for (Element depEl : XmlUtils.findElements("/configuration/dependencies/dependency", configuration)) {
dependencies.add(new Dependency(depEl));
}
projectOperations.addDependencies("", dependencies);
}
JavaType comparableInterface = new JavaType("java.lang.Comparable");
builder.addImplementsType(comparableInterface);
使用 MethodMetadataBuilder,分步驟:
Object obj,回傳 int。Book rhs = (Book) obj;。CompareToBuilder 串聯所有欄位 .append(this.field, rhs.field) 並 .toComparison()。關鍵挑戰:ComparetoMetadata 需獲取目標類所有欄位。解法:ComparetoMetadataProvider.locateFields() 過濾靜態/暫態/集合/陣列/版本欄位,回傳 FieldMetadata 列表傳給 ComparetoMetadata。
最終 getCompareToMethod() 動態構建方法體:
final ImportRegistrationResolver imports = builder.getImportRegistrationResolver();
imports.addImport(new JavaType("org.apache.commons.lang3.builder.CompareToBuilder"));
// ... 構建 instanceof、轉型、CompareToBuilder 鏈式調用 ...
bodyBuilder.appendFormalLine(builder.toString());
mvn clean install 建構附加組件 Jar (需 GPG 密碼)。bookshop,執行 JPA 設置與實體/欄位建立命令。osgi start --url file://<jar路徑> 安裝啟動附加組件。compareto + Tab 顯示三命令:setup、add、all。compareto setup → 更新 POM (加倉庫、commons-lang3、附加組件註解 Jar)。compareto all → 生成 Book_Roo_Compareto.aj: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();
}
}
坑點修正:perform package 失敗因 Maven 無法解析附加組件依賴。解法:建立獨立註解模組 xebia-spring-roo-addon-annotations,configuration.xml 引入該 Jar 而非附加組件 Jar,並更新 setup() 解析 /configuration/repositories。
場景:對 Oracle 資料庫反向工程需 OSGi 版 JDBC 驅動,版權限制 Spring Roo 不提供。包裝器附加組件將非 OSGi Jar 轉為 OSGi Bundle。
mvn install:install-file -Dfile=ojdbc5.jar -DgroupId=com.oracle -DartifactId=ojdbc5 -Dversion=11.2.0.2 -Dpackaging=jaroracle-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.compom.xml。perform command --mavenCommand bundle:bundle 生成 OSGi Bundle。完成!可用於反向工程等需 OSGi 驅動場景。
本文完整演示進階附加組件(代碼生成核心)與包裝器附加組件(第三方 Jar OSGi 化)開發全流程。擴展 Spring Roo 功能,優先考慮附加組件機制。下一篇將探討使用 Spring Roo 開發 GWT 應用。
| 描述 | 名字 | 大小 | 下載方法 |
|---|---|---|---|
| 範例代碼 | bookshop.zip | 14KB | HTTP |
| 範例代碼 | spring-dw-roo-compareto-addon.zip | 18KB | HTTP |