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 和键盘功能的工作原理。养成在有疑问时使用“hint”的习惯,尤其是在您的前几个 Roo 项目中。

让我们开始我们的婚礼项目。在将“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.*中。有一个“数据库属性列表”命令可以显示数据库配置。

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使用“数据库属性设置”命令的一个原因是,您正在创建一个稍后可以重放的独立脚本。您可以使用“脚本文件名.roo”命令执行脚本,这些脚本只是标准文本文件格式中的Roo命令。为了方便起见,我在Roo 1.0.0发行版中包含了wedding.roo脚本。请注意,还可以使用普通的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规范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://127.0.0.1:8080/wedding。当您准备好测试Web层时,请保持Tomcat服务器运行,然后执行以下命令。

$ mvn selenium:selenese

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

Selenium screet shot

安全性和日志记录

到目前为止,我们已经创建了wedding应用程序,运行了一些集成测试,在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。回想一下应用程序需求,我们希望使用邀请码(印在卡片背面)来确保只有受邀嘉宾才能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的未来版本中看到更多“安装”命令(欢迎您将安装程序请求添加到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

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

关于电子邮件集成的主题,我的同事Stefan Schmidt刚刚发布了一篇单独的博文,展示了如何将Roo电子邮件和JMS加载项一起使用。文章向您展示了更高级的配置选项,例如如何使用Gmail发送电子邮件。

IDE集成

我们现在已经到了可以使用Eclipse/STS完成应用程序的阶段。让我们将应用程序导入到Eclipse/STS中。

roo> perform eclipse
 (Maven console output condensed)

最后,让我们将项目导入 Eclipse/STS。您可以通过加载 Eclipse/STS,然后选择文件 > 导入 > 将现有项目导入工作区,并选择项目的目录来完成此操作。如果您没有使用 STS 2.3.0 或更高版本,请确保您已单独安装了 AJDT 1.6.5 或更高版本。当 AJDT 提示您是否要启用 JDT 编织时,请选择启用编织。这将在使用 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> 应该被删除。还应在此文件中添加一些登录.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 中的项目,然后选择运行方式 > 在服务器上运行
  • 右键单击 STS 中的项目,然后选择 Spring 工具 > 打开 Roo Shell,然后键入“deploy --server someServer”
现在,当您访问 https://127.0.0.1:8080/wedding 时,系统将提示您输入邀请码。使用上面 applicationContext-security.xml 文件中列出的用户名之一。RSVP 几次,您将看到应用程序正确地检索了您之前的 RSVP 记录。您还将看到它向您发送了一封电子邮件,假设您输入了电子邮件地址并且已正确配置了 SMTP 服务器(如果要更改 SMTP 服务器详细信息,请编辑 mail.properties)。以管理员用户身份登录,您将看到您可以访问所有 RSVP 记录、更改它们等等。

当然,在此阶段,我们通常会整理应用程序公开可见部分的外观和风格。然后,我们将运行“perform package”以提供一个 WAR,该 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 上。我们希望您喜欢使用 Spring Roo

获取 Spring 新闻通讯

与 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部