本系列 “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-langV3.1 的 Maven 依赖关系添加到目标项目中。这是必须的,因为commons-lang提供了一个名为CompareToBuilder的构建器类,可以使用它来构建compareTo方法。 - 使得实体类实现
Comparable接口。 - 为
compareTo方法创建一个 ITD。
要满足这些需求,则需要更改 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-langMaven 依赖关系,而不是 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 简单附加组件的更改类似。
在代码中添加 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();
}
|
让 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. ComparetoMetadataProviderprotected 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.ajimport 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 应用程序。
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| 样例代码 | bookshop.zip | 14KB | HTTP |
| 样例代码 | spring-dw-roo-compareto-addon.zip | 18KB | HTTP |
学习
- 阅读 Spring Roo文档,并查找有关高效使用 Roo 的所有信息。
- 务必阅读此 Spring Roo 系列的其他文章:
- Spring Roo 简介,第 1 部分:从源代码构建
- Spring Roo 简介,第 2 部分:使用 Spring Roo 开发应用程序
- Spring Roo 简介,第 3 部分:开发 Spring Roo 的附加组件
- Spring Roo 简介,第 4 部分:用 Spring Roo 和 Cloud Foundry 在云中快速开发应用程序
- 在 Spring Roo 源代码的 Web 视图中浏览 Spring Roo 代码。
- 了解 java.lang.Comparable 接口。
- 阅读 Spring Roo 文档,了解如何在 Google 代码上创建一个项目和 Maven 存储库。
- 遵循 Spring Roo 文档 使用 RooBot 注册该附加组件。
- 在 developerWorks 中国网站 Java 技术专区,查找 Java 编程各方面的文章、教程、论坛等。
- 关注 Twitter 上的 developerWorks。
- 观看 演示如何用 WebSphere Studio 快速开发Web Services,从初学者的产品安装和设置演示,到面向经验丰富的开发人员的高级功能。
- 随时关注 developerWorks 技术活动和网络广播。
- 访问 developerWorks Open source 专区获得丰富的 how-to 信息、工具和项目更新以及最受欢迎的文章和教程,帮助您用开放源码技术进行开发,并将它们与 IBM 产品结合使用。
获得产品和技术
- 访问 Spring Roo 存储库 以下载每日代码快照。
- 从项目网站上下载最新版本的 Spring Roo (1.2.0.RC1)。
- 从 Google 代码存储库中下载 compareTo 附加组件 的完整源代码。
- 获取 IBM 产品评估试用版软件(可下载或索取 DVD 光盘),并使用专为开发人员设计的软件改革您的下一个开源开发项目。
讨论
- 查看开发人员推动的博客、论坛、组和 wiki,并与其他 developerWorks 用户交流。帮助构建 developerWorks 社区中的 实际开放源码 讨论组。
- 加入 developerWorks 中文社区,developerWorks 社区是一个面向全球 IT 专业人员,可以提供博客、书签、wiki、群组、联系、共享和协作等社区功能的专业社交网络社区。
- 加入 IBM 软件下载与技术交流群组,参与在线交流。

Shekhar Gulati 是效力于 Xebia India 的一名 Java 顾问。他拥有 6 年的企业 Java 经验。他在 Spring 产品组合项目(比如 Spring、Spring-WS、Spring Roo 等)方面有丰富的经验。他主要关注 Spring、NoSQL 数据库、Hadoop、SRAD 框架(如 Sring Roo)、云计算(主要是 Google App Engine、CloudFoundry、OpenShift 等 PaaS 服务)和 Hadoop。他是一位活跃的作家,为 JavaLobby、Developer.com 和 IBM developerWorks 撰稿,他在 http://whyjava.wordpress.com/ 上拥有自己的博客。您可以在 twitter @ http://twitter.com/#!/shekhargulati 上关注他。