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 提供了一种强大且易于使用的生产力应用程序开发方法,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 用户应加载控制面板,然后单击系统、高级、环境变量,并双击“路径”条目。在路径末尾添加“;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,并且可以在未安装 Maven 的情况下运行,但由 Roo 创建的项目目前使用 Maven。此外,如果您安装了较早的 Roo alpha 版本,请务必删除 ROO_HOME 变量。

应用程序要求

既然您已经安装了 Roo,我们来考虑一下我们的婚礼 RSVP 应用程序的需求。然后我们将使用 Roo 和一些手动编码相结合来构建应用程序。一些手动编码的需求反映了 Roo 的一个基本理念,即您仍然需要开发应用程序中使其与众不同的那些部分。然而,您会发现,Roo 的设计旨在以完全透明、熟悉和支持往返的方式实现手动编码和自定义

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

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

创建持久化项目

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

如果您遵循了上述安装说明,您应该会看到下方所示的 Roo 徽标。如果您没有看到类似这样的消息,请返回检查您的安装是否正确。

Start logo

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

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

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”关键字或通过 ORM(如 Hibernate)创建的类。您甚至可以使用“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 inter-type declarations (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 规范REST)带来的好处。 当然,如果您不希望使用 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 操作,如持久化、删除、查找、合并等是否都正常工作。每个实体总共执行八个测试,所有这些都基于 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

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

$ mvn selenium:selenese

在执行 Selenium 测试期间,您应该会看到类似以下的图像:

Selenium screet shot

安全和日志

到目前为止,我们已经创建了我们的婚礼应用程序,运行了一些集成测试,在网页浏览器中玩了一下,并使用自动化的 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。回想一下应用程序要求,我们希望使用邀请码(印在卡片背面)来确保只有受邀宾客才能 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> 应该被删除。还应该在此文件中添加一些适当的文本,在 login.jsp 中向客人解释他们可以在卡的哪个位置找到邀请码。

安全现已设置完毕。现在我们打开 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 已确认:${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”
现在访问 https://:8080/wedding,系统会提示您输入邀请码。使用上面 applicationContext-security.xml 文件中列出的用户名之一。RSVP 几次后,您会看到应用程序正确检索了您之前的 RSVP 记录。您还会看到它向您发送了一封电子邮件,前提是您输入了电子邮件地址并配置了正确的 SMTP 服务器(如果需要更改 SMTP 服务器详细信息,请编辑 mail.properties)。以管理员用户身份登录,您会发现您可以访问所有 RSVP 记录,更改它们等等。

自然,在此阶段,我们通常会整理应用程序中公开可见部分的外观和感觉。然后,我们将运行“perform package”以提供一个 WAR 文件,该文件已准备好部署到生产服务器环境,例如 SpringSource tc ServerSpringSource dm Server

结论

在这篇博客文章中,我们介绍了安装 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 社区所有即将举行的活动。

查看所有