Spring Roo 入门

工程 | Ben Alex | 2009 年 5 月 27 日 | ...

更新:“Spring Roo 简介”博客系列的第三篇现已发布,详细介绍了 Roo 的内部架构。

我得坦白一件事。虽然你们中的许多人知道我最近几个月一直在忙着开发Spring Roo,但我还有一个独立的项目,它还没提交到 Subversion。另一个项目是规划我们的婚礼,因为下个月我未婚妻和我将出国结婚。所以当我思考在这篇博客文章中能向大家展示关于 Roo 的什么内容时,我想到我应该借此机会使用 Roo 构建我们婚礼的 RSVP(回复)网站!所以今天我们将学习如何使用 Roo 构建一个婚礼 RSVP 网站,我的一些同事评论说这是一个追求工作与生活平衡的进取范例。:-)

进度更新

如果您错过了 2009 年 5 月 1 日发布的 Spring Roo 博客系列的第一篇,简而言之,我介绍了 SpringSource 新推出的开源生产力工具的愿景,该工具旨在帮助那些希望快速构建符合最佳实践的 Java Spring 应用程序的人们。正如许多试用过 alpha 版本的人所发现的,Spring Roo 提供了一种强大且易于使用的应用程序开发方法,其背后的许多动机都体现在第一个 Jira 问题 ROO-1 中(由 Spring 之父、SpringSource 首席执行官 Rod Johnson 记录)。

今天我很高兴宣布我们刚刚发布了Spring Roo 1.0.0.M1。此版本不仅包含大量修复、增强和 31% 的性能提升,还带来了一系列令人兴奋的新功能,包括电子邮件服务JMSSpring Web Flow、简化的安装以及自动化的 Selenium 支持。这些都建立在 alpha 版本中已有的许多功能之上,正如我在之前的博客文章中提到的那样。

除了开发第一个里程碑版本外,在过去的一个月里,我们还建立了典型的开源项目公共项目基础设施。我们现在拥有社区支持论坛、Jira 问题跟踪、公共 Subversion 代码库、FishEye 源代码监控。过去一个月在 #roo Twitter 频道上的一些评论包括“我很惊喜”、“喜欢它”、“自定义 roo 插件非常容易”、“创新来了”、“第一个里程碑版本会很酷!”、“Roo 看起来很有趣且有效”、“非常棒的工具”、“非常酷”等等。我们也开始看到第一个社区贡献的插件,这强调了您可以轻松地使用自己的自定义功能扩展 Roo。

本月早些时候,我们还结束了社区项目命名竞赛,“Spring Roo”轻松获胜。总共收到了 952 张有效票,分布在 Spring Roo (467)、Spring Boost (180)、Spring Spark (179)、Spring HyperDrive (64) 和 Spring Dart (62) 之间。感谢所有投票的人!

安装

现在我们已将简化的安装添加到 Spring Roo 1.0.0.M1 中,安装就像下载 ZIP 文件、解压,然后将“bin”目录添加到您的路径一样简单
  • Windows 用户应打开“控制面板”,然后依次单击“系统”、“高级”、“环境变量”,双击“Path”条目。在路径末尾追加“;C:\spring-roo-1.0.0.M1\bin”(或您安装 Roo 的位置)。
  • *nix 用户应创建指向 roo.sh shell 脚本的符号链接。通常,诸如“sudo ln -s ~/spring-roo-1.0.0.M1/bin/roo.sh /usr/bin/roo”之类的命令即可奏效。
虽然您可以独立使用 Spring Roo,但我还建议您下载 SpringSource Tool Suite (STS) 2.3.0 或更高版本。STS 是我们免费的基于 Eclipse 的 IDE,提供了许多简化 Spring 应用程序开发的功能。例如,如果您下载 STS 2.3.0 或更高版本,您将获得内置的 Spring Roo 支持,甚至无需安装独立的 Spring Roo shell。

本博客将假设您正在使用独立的 Spring Roo shell。除非另有说明,否则所有命令在标准 Spring Roo 和 STS 2.3.0 或更高版本中都以相同方式工作。主要区别之一是,在使用 Roo shell 时,您按 TAB 键获取完成选项,而在 STS 中,TAB 键被 CTRL + SPACE 替代。我们使用 CTRL + SPACE 是因为它是 Eclipse 等基于 IDE 中用于完成的更传统按键组合。

您还应验证是否安装了 Maven 2.0.9 或更高版本。虽然 Roo 本身不使用 Maven 并且可以在未安装的情况下运行,但 Roo 创建的项目目前使用 Maven。此外,如果您安装了早期 Roo alpha 版本之一,请务必删除 ROO_HOME 变量。

应用程序需求

现在您已经安装了 Roo,让我们考虑一下我们的婚礼 RSVP 应用程序的需求。然后,我们将结合使用 Roo 和一些手动编码来构建应用程序。对某些手动编码的需求反映了 Roo 的一个基本理念:您仍然需要开发那些使您的应用程序与众不同的部分。但是,您会看到 Roo 被设计为以一种完全透明、熟悉且支持双向同步(round-trip supporting)的方式来实现手动编码和定制

尽管有在线 RSVP,我们仍然寄出了纸质婚礼请柬。每张请柬的背面都有一个小小的“邀请码”。这些码不容易猜到,但很容易阅读和输入(不是 UUID!)。婚礼请柬的文字邀请宾客访问我们的婚礼 RSVP 网站进行回复。当他们访问婚礼 RSVP 网站时,会要求宾客输入他们的邀请码。

宾客输入邀请码后,任何现有的 RSVP 记录将被检索并允许他们编辑。如果他们之前没有回复过,则会提供一个新的 RSVP 表单供他们填写。表单只会简单地询问宾客有多少人参加,以及是否有任何特殊要求(例如饮食需求)。他们还会被要求输入他们的电子邮件地址,如果提供了电子邮件地址,应用程序将发送一份基于电子邮件的回复确认。我们还会记录他们回复的日期和时间,如果他们多次更改回复,这将非常有用。

创建持久化项目

现在我们有了想构建的真实世界应用程序的想法,让我们使用 Roo 来构建它。第一步是创建一个空目录并加载 Roo
$ mkdir wedding
$ cd wedding
$ roo

如果您按照上述安装说明操作,您应该会看到下方显示的 Roo 标志。如果您没有看到类似的消息,请返回并检查您的安装是否正确。

Start logo

Roo 中有很多可用性功能。如果您输入“hint”,将显示分步说明。如果您输入“help”,将看到当前所有可用的命令(这些命令在项目生命周期的不同阶段会变化)。此外,在几乎所有情况下,您都可以按 TAB 键来获取完成服务。如果您正在学习 Roo,“hint”是您的朋友。它不仅会教您命令,还会教您关于 shell 以及键盘功能的工作原理。遇到疑问时养成使用“hint”的习惯,尤其是在您的前几个 Roo 项目中。

让我们开始我们的婚礼项目。在 Roo shell 中输入“create project”命令后,您应该会收到以下输出

roo> project --topLevelPackage com.wedding
Created /home/balex/wedding/pom.xml
Created SRC_MAIN_JAVA
Created SRC_MAIN_RESOURCES
Created SRC_TEST_JAVA
Created SRC_TEST_RESOURCES
Created SRC_MAIN_WEBAPP
Created SRC_MAIN_RESOURCES/META-INF/spring
Created SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml

如控制台输出所示,Roo 创建了一个 Maven 2 项目结构。即使您此时退出 Roo 且不再重新加载它,此时您也已拥有一个配置正确的 Spring 3 web 应用程序,包括 URL 重写、基于注解的类路径扫描以及任何类的依赖注入——即使是那些使用“new”关键字或通过像 Hibernate 这样的 ORM 创建的类。您甚至可以使用“mvn tomcat:run”启动嵌入式 Tomcat 容器。

如果您此时输入“hint”,Roo 会建议您安装 JPA 提供者和数据库。现在就让我们来做这件事

roo> persistence setup --provider HIBERNATE --database HYPERSONIC_PERSISTENT
Created SRC_MAIN_RESOURCES/META-INF/persistence.xml
Created SRC_MAIN_RESOURCES/META-INF/spring/database.properties
Managed SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml
Managed ROOT/pom.xml

请注意,我们在命令中选择了 Hibernate 和一个持久化的 Hypersonic 数据库。这个选择是通过 TAB 键完成的,所以我们实际上无需完全输入这些命令和参数。还要注意底部的两个“Managed”语句。这些表示 Roo 正在管理的文件或目录。Roo 内置了文件撤销服务,所以如果出现问题,它会自动回滚有问题的命令。

由于 Roo 使用 JPA,我们可以享受跨不同 JPA 实现的可移植性优势。如果您查看 Roo 生成的代码,您会发现没有一行代码是特定于某个持久化提供者的。您还会发现 Roo 提供的代码效率极高。Java 的一大优势是其显著的性能,Roo 从避免反射到优化 toString() 方法中的字符串操作(以及介于两者之间的所有操作),无所不用其极地最大化应用程序的运行时性能。

因为我们请求了一个持久化数据库,它默认存储在 ~/wedding.* 中。有一个“database properties list”命令会显示数据库配置

roo> database properties list
database.driverClassName = org.hsqldb.jdbcDriver
database.password =
database.url = jdbc:hsqldb:${user.home}/wedding
database.username = sa

虽然默认位置可以正常工作,但让我们将它更改到其他地方

roo> database properties set --key database.url --value jdbc:hsqldb:/home/balex/our-wedding
Managed SRC_MAIN_RESOURCES/META-INF/spring/database.properties

如控制台输出所示,Roo 所做的只是编辑了一个标准的 database.properties 文件。您也可以使用文本编辑器或 IDE 合法地编辑您的项目文件。Roo 不会介意。它从一开始就被设计成您可以同时使用其他工具与 Roo 一起工作,并且一切都能正常运行。

您可能喜欢通过 Roo 使用“database properties set”命令的一个原因是您正在制作一个独立的脚本,以后可以重放。您可以使用“script filename.roo”命令执行脚本,这些脚本只是标准文本文件格式的 Roo 命令。为了方便您,我已将 wedding.roo 脚本包含在 Roo 1.0.0 分发版中。请注意,脚本中也可以使用普通的 Java 注释语法(//, /* 和 */)包含注释。

创建实体

现在让我们创建一个 JPA 实体。该实体将存储在我们的数据库中,并代表我们应用程序的整个领域模型。您可以使用您选择的 IDE、文本编辑器或 Roo shell 创建实体
roo> entity --class ~.domain.Rsvp
Created SRC_MAIN_JAVA/com/wedding/domain
Created SRC_MAIN_JAVA/com/wedding/domain/Rsvp.java
Created SRC_MAIN_JAVA/com/wedding/domain/Rsvp_Roo_Entity.aj
Created SRC_MAIN_JAVA/com/wedding/domain/Rsvp_Roo_ToString.aj
Created SRC_MAIN_JAVA/com/wedding/domain/Rsvp_Roo_Configurable.aj

此时我猜想你们中的一些人可能在想,“那些 .aj 文件是什么?” 简单来说,这些是 AspectJ 跨类型声明 (ITD),它们非常有效地实现了关注点分离,同时还能保持与相关 Roo 插件未来版本的兼容性。.aj 文件由 Roo 自动创建、维护和删除,允许最终用户安全地忽略它们。事实上,STS 2.1.0+ 默认会自动隐藏它们,就像基于 Eclipse 的 IDE 隐藏 .classpath、.project 和 .settings 资源一样。毕竟,这些资源仅仅是工具的内部输出,您很少会打开它们——更不用说自己维护了。我会在我的下一篇博客文章中更详细地讨论这些以及其他 Roo 内部机制,所以在此之前我将推迟进一步的讨论。

您可能已经注意到我们在“.domain”包中创建了 Rsvp 实体。“”字符会自动扩展为您的项目顶级包,您可能还记得我们在最初创建项目时指定过这个包。因此,Roo 完全理解 Java 包的概念,并允许您以您认为最直观的方式组织您的项目包结构。

自然地,一个实体通常会包含一些字段,所以让我们添加它们(此处省略了 Roo 的输出,因为它只是管理上面列出的相同文件)

roo> field string code --notNull --sizeMin 1 --sizeMax 30
roo> field string email --sizeMax 30
roo> field number attending --type java.lang.Integer
roo> field string specialRequests --sizeMax 100
roo> field date confirmed --type java.util.Date

在第一行中,您会注意到我们使用了 --notNull 参数,以及 --sizeMin 和 --sizeMax 参数。这些参数指的是新的 Bean Validation 标准,也称为 JSR 303。这个标准提供了自动化的 web 层和持久化层验证,包括为数据库中的表创建正确的 DDL使用 Roo 的优点之一是您可以轻松获得相关标准的好处,例如 JSR 303、JPA、Servlet SpecificationREST,无需额外努力。当然,如果您不愿意,您不必使用 JSR 303 参数。

从上面的字段命令中需要注意的另一点是,我们没有指定要将这些字段插入哪个实体。Roo 会自动判断您可能希望将字段添加到 Rsvp,因为这是您上次处理的实体。如果您希望明确指定或将字段指向另一个实体,也可以指定“--class ~.SomeEntity”参数(在这种情况下,该实体将成为后续与实体相关的命令的默认目标)。

证明它有效:JUnit、Web 层和 Selenium

现在我们有了一个如果部署就能实际运行的应用程序。但不要只听我说的——让我们添加一些功能,以便您可以自己尝试一下这个应用程序。

让我们从 JUnit 集成测试开始。您可以使用一个命令获取集成测试

roo> test integration
Created SRC_TEST_JAVA/com/wedding/domain
Created SRC_TEST_JAVA/com/wedding/domain/RsvpDataOnDemand.java
Created SRC_TEST_JAVA/com/wedding/domain/RsvpIntegrationTest.java
Created SRC_TEST_JAVA/com/wedding/domain/RsvpDataOnDemand_Roo_Configurable.aj
Created SRC_TEST_JAVA/com/wedding/domain/RsvpDataOnDemand_Roo_DataOnDemand.aj
Created SRC_TEST_JAVA/com/wedding/domain/RsvpIntegrationTest_Roo_Configurable.aj
Created SRC_TEST_JAVA/com/wedding/domain/RsvpIntegrationTest_Roo_IntegrationTest.aj

这个集成测试将验证常见的 JPA 操作,如 persist、remove、find、merge 等是否都正常工作。每个实体总共执行八个测试,所有测试都基于 Spring Framework 丰富的集成测试基础设施。虽然我们可以在这个阶段运行集成测试,但添加一个 web 层也是轻而易举的事情

roo> controller scaffold ~.web.RsvpController
Created SRC_MAIN_JAVA/com/wedding/web
Created SRC_MAIN_JAVA/com/wedding/web/RsvpController.java
Created SRC_MAIN_WEBAPP/WEB-INF/config
Created SRC_MAIN_WEBAPP/WEB-INF/config/webmvc-config.xml
Created SRC_MAIN_JAVA/com/wedding/web/RsvpController_Roo_Controller.aj
Created SRC_MAIN_WEBAPP/images
Created SRC_MAIN_WEBAPP/images/banner-graphic.png
Created SRC_MAIN_WEBAPP/images/springsource-logo.png
Created SRC_MAIN_WEBAPP/images/resultset_first.png
Created SRC_MAIN_WEBAPP/images/resultset_next.png
Created SRC_MAIN_WEBAPP/images/resultset_previous.png
Created SRC_MAIN_WEBAPP/images/resultset_last.png
Created SRC_MAIN_WEBAPP/images/us.png
Created SRC_MAIN_WEBAPP/images/de.png
Created SRC_MAIN_WEBAPP/images/list.png
Created SRC_MAIN_WEBAPP/images/add.png
Created SRC_MAIN_WEBAPP/styles
Created SRC_MAIN_WEBAPP/styles/roo-menu-left.css
Created SRC_MAIN_WEBAPP/styles/roo-menu-right.css
Created SRC_MAIN_WEBAPP/WEB-INF/classes
Created SRC_MAIN_WEBAPP/WEB-INF/classes/left.properties
Created SRC_MAIN_WEBAPP/WEB-INF/classes/right.properties
Created SRC_MAIN_WEBAPP/WEB-INF/layouts
Created SRC_MAIN_WEBAPP/WEB-INF/layouts/layouts.xml
Created SRC_MAIN_WEBAPP/WEB-INF/layouts/default.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views
Created SRC_MAIN_WEBAPP/WEB-INF/views/dataAccessFailure.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/resourceNotFound.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/uncaughtException.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/index.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/views.xml
Created SRC_MAIN_WEBAPP/WEB-INF/tags
Created SRC_MAIN_WEBAPP/WEB-INF/tags/pagination.tagx
Created SRC_MAIN_WEBAPP/WEB-INF/tags/language.tagx
Created SRC_MAIN_WEBAPP/WEB-INF/tags/theme.tagx
Created SRC_MAIN_WEBAPP/WEB-INF/i18n
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/messages.properties
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages_de.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/messages_de.properties
Created SRC_MAIN_WEBAPP/images/show.png
Created SRC_MAIN_WEBAPP/images/update.png
Created SRC_MAIN_WEBAPP/images/delete.png
Created SRC_MAIN_WEBAPP/WEB-INF/views/rsvp
Managed SRC_MAIN_WEBAPP/WEB-INF/config/webmvc-config.xml
Created SRC_MAIN_WEBAPP/WEB-INF/views/rsvp/list.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/rsvp/show.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/rsvp/create.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Managed SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/rsvp/update.jspx
Managed SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/rsvp/views.xml
Created SRC_MAIN_WEBAPP/WEB-INF/urlrewrite.xml
Created SRC_MAIN_WEBAPP/WEB-INF/web.xml
Managed SRC_MAIN_WEBAPP/WEB-INF/web.xml
Managed ROOT/pom.xml

Roo 提供的自动化 web 层构建在 Spring Framework 3 出色的REST 支持之上。所有端点都是完全 RESTful 的,并使用清晰、格式正确的 URL。Roo 的自动化 web 层在多种情况下都很有用,特别是

  • 通过 REST 客户端进行编程访问
  • 应用程序的管理部分
  • 用作您手动创建的控制器和 JSP 的模板
最后,让我们添加一个 Selenium 测试,它将实际验证我们新的 RsvpController 是否正常工作
roo> selenium test --controller ~.web.RsvpController
Created SRC_MAIN_WEBAPP/selenium
Created SRC_MAIN_WEBAPP/selenium/test-rsvp.xhtml
Created SRC_MAIN_WEBAPP/selenium/test-suite.xhtml
Managed SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Managed ROOT/pom.xml

好的,让我们运行以下命令来看看应用程序的实际效果

roo> perform test
 (Maven console output condensed)
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.wedding.domain.RsvpIntegrationTest
Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.726 sec
roo> quit
$ mvn tomcat:run

现在您可以使用浏览器访问 http://localhost:8080/wedding。当您准备测试 web 层时,请保持 Tomcat 服务器运行,然后执行以下命令

$ mvn selenium:selenese

在执行 Selenium 测试期间,您应该会看到类似于

Selenium screet shot

安全和日志记录

到目前为止,我们已经创建了我们的婚礼应用程序,运行了一些集成测试,在 web 浏览器中试用了一下,并使用自动化的 Selenium 测试验证了 web 层的正确操作。下一步是微调基于 Log4J 的日志记录
roo> logging setup --package WEB --level DEBUG
Created SRC_MAIN_RESOURCES/META-INF/spring/log4j.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/web.xml

现在让我们关注安全性。目前任何人都可以访问我们的网站,并使用现有的 RESTful 管理后端创建、更新和删除 RSVP。回想一下应用程序需求,我们希望使用邀请码(印在卡片背面)来确保只有被邀请的宾客才能回复。幸运的是,Spring Security 为我们提供了一种非常快速的方法来满足这一需求,并且 Roo 可以用一行命令安装 Spring Security

roo> security setup
Managed ROOT/pom.xml
Created SRC_MAIN_RESOURCES/META-INF/spring/applicationContext-security.xml
Created SRC_MAIN_WEBAPP/WEB-INF/views/login.jspx
Managed SRC_MAIN_WEBAPP/WEB-INF/views/views.xml
Managed SRC_MAIN_WEBAPP/WEB-INF/web.xml

还有类似的命令,如“web flow”和“jms setup”,但我们不会在本博客文章中探讨它们。您可以期待在未来版本的 Roo 中看到更多“install”命令(也欢迎您将安装程序请求添加到 Roo 问题跟踪器)。

手动控制器、动态查找器和电子邮件支持

如上所述,我们的自动化 web 层非常适合我们应用程序的管理后端。但我们还需要应用程序的一部分,它适合准宾客使用。应用程序的这一部分必须理解邀请码、管理 RSVP 和发送电子邮件之间的关系。由于这些需求将需要一些编程,让我们构建一个新的控制器
roo> controller class --class ~.web.PublicRsvpController
Created SRC_MAIN_JAVA/com/wedding/web/PublicRsvpController.java
Managed SRC_MAIN_WEBAPP/WEB-INF/web.xml
Managed ROOT/pom.xml

PublicRsvpController 将响应 HTTP GET 和 POST 请求。PublicRsvpController.java 源文件中已自动提供了用于这些操作的两个存根方法。

如果我们考虑 GET 用例,我们的目标是为特定的邀请码检索正确的 RSVP。其工作方式是 Spring Security 将要求用户登录才能使用应用程序,我们将每个邀请码视为一个唯一的登录名。因此,GET 方法需要从 Spring Security 获取当前登录用户的名称,然后从数据库中检索相应的 RSVP。通常,您此时会编写 JPA QL 查询来获取具有匹配码的特定 Rsvp 实例,但由于您正在使用 Roo,您可以省去麻烦,转而使用动态查找器。

动态查找器为您提供了几乎无限范围的预定义查询。这些查询内部都使用 JPA QL,提供了最大的基于标准的兼容性和可移植性。所有动态查找器(以及其他 Roo 方法)都实现为格式正确、类型安全的 Java 方法——带来了熟悉性、IDE 代码辅助、调试器集成和显著的运行时性能等所有常规优势。您可以使用如下命令列出可用的动态查找器

roo> finder list --class ~.domain.Rsvp --filter code,equ
findRsvpsByCodeEquals(String code)
findRsvpsByCodeNotEquals(String code)

请注意,“--filter”参数将输出限制为仅包含“code”和“equ”字符串的建议方法签名。您可以通过省略“-filter”参数或指定“-depth 2”(如果您希望查询涉及更多属性,可以指定 3、4 等)来指示 Roo 显示更多组合。

一旦找到您想使用的动态查找器,只需添加它

roo> finder add --finderName findRsvpsByCodeEquals
Managed SRC_MAIN_JAVA/com/wedding/domain/Rsvp.java
Created SRC_MAIN_JAVA/com/wedding/domain/Rsvp_Roo_Finder.aj
Managed SRC_MAIN_JAVA/com/wedding/web/RsvpController_Roo_Controller.aj
Created SRC_MAIN_WEBAPP/WEB-INF/views/rsvp/findRsvpsByCodeEquals.jspx
Managed SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Managed SRC_MAIN_WEBAPP/WEB-INF/views/rsvp/views.xml

如果我们考虑 PublicRsvpController 的 POST 用例,我们的需求规定我们应该向宾客发送一封电子邮件以确认他们的 RSVP。通常我们会翻阅 Spring 参考指南,找到关于配置电子邮件支持的部分,但现在我们只需让 Roo 为我们处理即可

roo> email sender setup --hostServer 127.0.0.1
Created SRC_MAIN_RESOURCES/META-INF/spring/email.properties
Managed SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml
Managed ROOT/pom.xml
roo> field email template --class ~.web.PublicRsvpController
Managed SRC_MAIN_JAVA/com/wedding/web/PublicRsvpController.java
Managed SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml
Managed SRC_MAIN_JAVA/com/wedding/web/PublicRsvpController.java

最后一个命令向 PublicRsvpController 添加了一个 Spring MailSender 字段,并提供了一个方法向我们展示如何使用它。

关于电子邮件集成的话题,我的同事 Stefan Schmidt 刚刚发表了另一篇博客文章,展示了如何将 Roo 电子邮件和 JMS 插件结合使用。文章展示了更高级的配置选项,例如如何使用 Gmail 发送电子邮件。

IDE 集成

现在我们已经到了可以使用 Eclipse/STS 来完成应用程序的地步。让我们将应用程序导入到 Eclipse/STS 中
roo> perform eclipse
 (Maven console output condensed)

最后,让我们将项目导入到 Eclipse/STS 中。通过启动 Eclipse/STS,然后选择 File > Import > Existing Projects into Workspace,并选择项目目录来完成此操作。如果您使用的不是 STS 2.3.0 或更高版本,请确保您已单独安装了 AJDT 1.6.5 或更高版本。当 AJDT 提示您是否要启用 JDT weaving 时,选择启用 weaving。这将在使用 Eclipse 的 Java 编辑器时提供更好的 Roo 体验。

最后步骤

现在我们将使用 Eclipse/STS 更改几个文件。以下截图显示了我们最终的项目结构,我已高亮显示了我们将要更改的文件: structure

首先编辑 applicationContext-security.xml 文件。进行一些小的更改,使其类似于以下文件


    <http auto-config="true" use-expressions="true">
    	<form-login login-processing-url="/static/j_spring_security_check" login-page="/login" authentication-failure-url="/login?login_error=t"/>
        <logout logout-url="/static/j_spring_security_logout"/>
        <intercept-url pattern="/rsvp/**" access="hasRole('ROLE_ADMIN')"/>
        <intercept-url pattern="/resources/**" access="permitAll" />
        <intercept-url pattern="/static/**" access="permitAll" />
        <intercept-url pattern="/login**" access="permitAll" />
        <intercept-url pattern="/**" access="isAuthenticated()" />
    </http>

    <authentication-manager alias="authenticationManager">
    	<authentication-provider>
	        <user-service>
	            <user name="admin1234" password="ignored" authorities="ROLE_ADMIN"/>
		        <user name="user12345" password="ignored" authorities="ROLE_USER"/>
		        <user name="user67890" password="ignored" authorities="ROLE_USER"/>
		    </user-service>
    	</authentication-provider>
	</authentication-manager>

上面的文件显示邀请码实际上就是用户名,并且我们忽略了密码。Spring Security 不知道我们忽略了密码,因此我们需要编辑 src/main/webapp/WEB-INF/views/login.jspx 文件,并在表单中添加一行 <input name="j_password" type="hidden" value="ignored"/>。当然,包含“j_password”标签和输入元素的现有 <div> 应该被删除。还应在该文件中添加一些适当的文本,向宾客解释他们可以在卡片的哪个位置找到他们的邀请码。

现在安全性已经设置好了。现在让我们打开 PublicRsvpController.java 文件。如所示,Roo 已经为电子邮件功能提供了存根,并为您提供了空的 Spring MVC 方法来完成。这是整个应用程序中唯一实际需要的 Java 编程,而且由于这些方法使用了 Spring MVC 和 Spring 的 MailSender 类的常规功能,我在此将不再进一步讨论它们


@RequestMapping("/publicrsvp/**")
@Controller
@SessionAttributes("rsvp")
public class PublicRsvpController {

    @Autowired
    private transient MailSender mailTemplate;

    @RequestMapping
    public String get(ModelMap modelMap) {
    	modelMap.put("rsvp", getRsvp());
    	return "publicrsvp";
    }

    @RequestMapping(method = RequestMethod.POST)
    public String post(@ModelAttribute("rsvp") Rsvp rsvp, ModelMap modelMap) {
  	rsvp.setConfirmed(new Date());
        if (rsvp.getId() == null) {
        	rsvp.persist();
        } else {
        	rsvp.merge();
        }
    	if (rsvp.getEmail().length() > 0) {
        	sendMessage("Ben Alex <[email protected]>", "RSVP to our wedding", rsvp.getEmail(), "Your RSVP has been saved: " + rsvp.toString());
    	}
    	modelMap.put("rsvp", rsvp);
    	return "thanks";
    }

    private Rsvp getRsvp() {
    	Rsvp rsvp = new Rsvp();
    	try {
        	String code = SecurityContextHolder.getContext().getAuthentication().getName();
        	rsvp.setCode(code);
    		// Cast due to http://java.sun.com/javaee/5/docs/api/javax/persistence/Query.html#getSingleResult()
    		rsvp = (Rsvp) Rsvp.findRsvpsByCodeEquals(code).getSingleResult();
    	} catch (DataAccessException ignored) { /* no Rsvp for this code was found, so start a new Rsvp */ }
    	return rsvp;
    }

    private void sendMessage(String mailFrom, String subject, String mailTo, String message) {
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
        simpleMailMessage.setFrom(mailFrom);
        simpleMailMessage.setSubject(subject);
        simpleMailMessage.setTo(mailTo);
        simpleMailMessage.setText(message);
        mailTemplate.send(simpleMailMessage);
    }
}

请注意,“get”和“post”方法中我们返回了一个字符串,这应该与我们希望渲染的 JSP 视图名称相关联。因此,我们的下一步是提供这两个 JSP 文件。幸运的是,Roo 构建了一些 JSP 文件,它们可以作为有用的模板。

首先将 src/main/webapp/WEB-INF/index.jsp 重命名为 thanks.jsp。这将是“post”方法返回时显示的页面。您可能想要添加一些内容,例如“您的回复已确认:${rsvp}”。

接下来将 src/main/webapp/WEB-INF/views/rsvp/create.jspx 复制到 src/main/webapp/WEB-INF/views/publicrsvp.jspx。然后应该编辑此页面。您可以安全地删除“code”和“confirmed”部分,因为 PublicRsvpController 会处理它们。您还应该将“form_url”变量赋值更改为 <c:url value="/publicrsvp" var="form_url"/>。

由于 Spring Roo 使用 Tiles 来方便地为每个 web 视图进行品牌化,因此您需要编辑 src/main/webapp/WEB-INF/views/views.xml 文件。您需要将“index”定义重命名为“thanks”,并为新的 publicrsvp.jspx 添加一个新的“publicrsvp”定义。最终文件应类似于


<tiles-definitions>

	<definition extends="public" name="thanks">
		<put-attribute name="body" value="/WEB-INF/views/thanks.jspx"/>
	</definition>

	<definition extends="public" name="dataAccessFailure">
		<put-attribute name="body" value="/WEB-INF/views/dataAccessFailure.jspx"/>
	</definition>

	<definition extends="public" name="resourceNotFound">
		<put-attribute name="body" value="/WEB-INF/views/resourceNotFound.jspx"/>
	</definition>

	<definition extends="public" name="uncaughtException">
		<put-attribute name="body" value="/WEB-INF/views/uncaughtException.jspx"/>
	</definition>

	<definition extends="public" name="login">
        <put-attribute name="body" value="/WEB-INF/views/login.jspx"/>
    </definition>

	<definition extends="public" name="publicrsvp">
        <put-attribute name="body" value="/WEB-INF/views/publicrsvp.jspx"/>
    </definition>

</tiles-definitions>

最后一步是编辑 src/main/webapp/WEB-INF/urlrewrite.xml 文件,并更改针对 / 的 URL 重写规则。/app/index 应该修改为 /app/publicrsvp/,这表示默认情况下对新的 PublicRsvpController 执行 GET 操作。

现在您应该准备好测试部署了。您有几种选择

  • 从命令行使用“mvn tomcat:run”
  • 在 Eclipse/STS 中右键单击项目,选择 Run As > Run on Server
  • 在 STS 中右键单击项目,选择 Spring Tools > Open Roo Shell,然后输入“deploy --server someServer”
现在访问 http://localhost:8080/wedding 时,系统会提示您输入邀请码。使用上面 applicationContext-security.xml 文件中列出的用户名之一。回复几次,您就会看到应用程序正确检索了您先前的 RSVP 记录。您还会看到它会向您发送一封电子邮件,前提是您输入了电子邮件地址并配置了正常的 SMTP 服务器(如果您想更改 SMTP 服务器详细信息,请编辑 mail.properties 文件)。以管理员用户身份登录,您将看到您可以访问所有 RSVP 记录,更改它们等等。

自然地,在这个阶段,我们通常会整理应用程序公共可见部分的外观。然后我们会运行“perform package”命令,以生成一个可部署到生产服务器环境(例如 SpringSource tc ServerSpringSource dm Server)的 WAR 文件。

结论

在这篇博客文章中,我们介绍了安装 Roo、使用其 shell 以及快速构建一个具有以下特性的实际应用程序所需的步骤:
  • 基于 Spring Framework 3 的最佳实践应用程序架构
  • 基于 Maven 2 的项目结构
  • 基于 JPA 的持久化,在此例中使用了 Hibernate
  • 数据库存储到自定义的 Hypersonic 文件位置
  • Bean Validation (JSR 303) 支持,包括将约束传播到数据库 DDL
  • 基于 Spring Framework 集成测试功能的自动化 JUnit 集成测试
  • 自动化的 RESTful 应用程序后端
  • 您 web 层的自动化 Selenium 测试
  • 在我们的应用程序中用于实际用例的动态查找器
  • Spring Security 集成,包括 web URL 安全性和定制的登录页面
  • 电子邮件发送(有关通过 Roo 发送电子邮件的更多信息,请阅读 Stefan 的博客文章
  • Log4J 配置支持
  • 保持 URL 清晰和 RESTful 的 URL 重写
  • 一个手动 web 控制器
  • 嵌入式 Tomcat 服务器容器使用
  • Eclipse 和 STS 集成
未来几周和几个月内,我们将为 Roo 添加许多额外功能,我的下一篇博客文章将涵盖 Roo 的内部机制和架构。在此期间,我们非常欢迎您对 Roo 的评论、使用体验和反馈。社区论坛是提问的好地方,您也可以在 Twitter 上关注我们#roo)。我们希望您能享受使用 Spring Roo 的乐趣。

获取 Spring 时事通讯

订阅 Spring 时事通讯,保持联系

订阅

取得进展

VMware 提供培训和认证,助您加速前进。

了解更多

获取支持

Tanzu Spring 通过一项简单的订阅,提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将到来的活动

查看 Spring 社区所有即将到来的活动。

查看全部