领先一步
VMware 提供培训和认证,助您加速进步。
了解更多上周 Spring Integration 发布了 1.0 GA 版本,受 SpringONE Americas 大会上 Adrian 主题演讲(不,不是蒙提·派森的短剧,只是 Grails 的实时编码示例)的启发,我想展示如何在 Grails 应用程序这个稍有不同的环境下利用 Spring Integration 会很有趣。
请注意:本文转载自我的个人博客 @ www.russmiles.com
本系列文章将探讨如何将 Spring Integration 添加到 Grails 的多种配置中,最终将形成一个用于 Spring Integration 的完整 Grails 插件。这更像是一个在线日记,您将有机会看到我们如何迈出第一步,将 Spring Integration 引导到 Grails 应用程序中,通过使用 Spring Integration 的一些高级功能在不同基础设施之间桥接消息,最终运用这些经验来创建一个 Grails 插件,您可以用来快速轻松地将 Spring Integration 添加到您自己的 Grails 项目中。
如果您更喜欢在阅读文章时拥有已完成的项目,则完整的 Grails 项目源代码可在此处下载(注意:基于 Grails 1.1 Beta 1 构建)
要创建 Grails 应用程序,您只需确保 Grails 安装在您的路径上,然后从命令行发出以下命令
> grails create-app grails-plus-integration-demo
如果一切正常,您应该会看到以下输出
... 首先是其他输出,然后是 ...
在 <您的目录>/grails-plus-integration-demo 处创建了 Grails 应用程序
Grails 已经从您键入 grails create-app 命令的任何位置在 grails-plus-integration-demo 目录中创建了一个应用程序。切换到您的新 Grails 应用程序目录,然后运行它以确保一切正常
> cd grails-plus-integration-demo > grails run-app
您应该会看到类似以下内容的输出
... 其他输出,然后 ...
服务器正在运行。浏览到 https://:8080/grails-plus-integration-demo
打开您选择的浏览器,导航到正在运行的 grails 应用程序的地址,该地址应该是 https://:8080/grails-plus-integration-demo,您应该会看到类似以下内容

图 1. 您的新 Grails 应用程序,已启动并运行
好的,到目前为止没有什么突破性的,但现在我们已经准备好创建一些功能,最终将执行 Spring Integration 消息传递管道。要从命令行关闭 Grails 应用程序,请按 Ctrl-C。
对于本文中我们非常简单的示例应用程序,我们只需要一个名为“GreetingsMessage”的域对象。为此,请在您的新应用程序目录中使用以下 grails 命令
> grails create-domain-class GreetingsMessage
然后您应该会看到类似以下内容
正在运行脚本 /Applications/SpringSource/grails/grails-1.1-beta1/scripts/CreateDomainClass.groovy 环境设置为 development 为 GreetingsMessage 创建了 DomainClass 为 GreetingsMessage 创建了测试
创建了 GreetingsMessage 域类后,是时候添加几个属性了。为此,您需要编辑当前位于 grails-app/domain 目录中的 GreetingsMessage.groovy 文件。您只需要为我们的示例添加一行,即可允许我们发送一个字符串消息作为 GreetingsMessage 对象的内容

图 2. 用一个属性更新您的域类
保存您更新后的 GreetingsMessage 域类,就完成了。
使用命令行,创建一个名为“GreetingsMessageSender”的 grails 控制器
> grails create-controller GreetingsMessageSender
此命令应发出类似以下内容的输出
正在运行脚本 /Applications/SpringSource/grails/grails-1.1-beta1/scripts/CreateController.groovy 环境设置为 development 为 GreetingsMessageSender 创建了控制器 [mkdir] 创建了目录: /Users/russellmiles/project/grails-plus-integration-demo/grails-app/views/greetingsMessageSender 为 GreetingsMessageSender 创建了测试
您现在已经创建了一个空控制器及其功能的单元测试。Grails 还在您的 grail-app/views 目录中创建了一个名为 greetingsMessageSender 的空目录,当您添加了一些控制器方法后,该目录将包含您的视图模板。
下一步是使用我们希望它支持的方法更新控制器。打开并更新 grails-app/controllers/GreetingsMessageSender.groovy,使其与以下内容匹配

图 3. (部分)完成的 GreetingsMessageSender 控制器
如果您对 Groovy 不太熟悉,那么下面是该类的代码所做的工作
这个 send.gsp 视图中有相当多的代码,所以我建议您在这里下载代码,而不是自己全部输入。但是,如果您感到勇敢,可以将下面显示的代码复制到您的 send.gsp 文件中
图 4. 完成的 send.gsp 视图
视图中需要关注的关键代码是 g:form 和 flash.message 部分。g:form 包含一个输入文本框,它将表单中的内容字段填充到 HTTP 参数中,当表单提交时,这些参数将被 GreetingsMessageSenderController 上的 send 闭包拾取。
flash.message 代码显示 flash 集合的内容,在本例中是 send 闭包添加的消息。
您现在可以运行并尝试这个简单的界面了,尽管我们不期望它暂时能做太多事情。使用 grails run-app 命令再次运行您的应用程序,并导航到您的应用程序主页,然后尝试一下

图 5. Grails 应用程序主页上的新控制器链接
点击 GreetingsMessageController 会执行 'index' 方法,然后导致您的浏览器重定向到 send 闭包,然后您应该会看到您的新表单

图 6. 您的发送表单
您应该能够在文本框中输入一条消息,点击“发送”,然后,什么都不会发生……暂时。
计划是创建一个服务,它接受一个字符串,然后简单地将其转换为大写。这没什么太令人兴奋的,但这里是精彩的部分:我们将使其前端控制器不知道控制器在哪里,也不知道它支持什么接口。当然,我们可以简单地将我们的服务注入到控制器中,但这会将我们的控制器绑定到接口级别的服务。我们在这里要做的是将服务和控制器之间的契约表现为仅仅是将被传递的消息类型,在本例中是包含单个字符串的消息。
注意:当我提到契约和消息传递时,SOA 人员的耳朵可能会竖起来,意识到这与服务的目的非常接近,即您将契约声明为将与服务终点交换的消息类型。这绝对是故意的,但值得一提的是,尽管 Spring Integration 是 SOA 实践的极佳促进者,但它远不及拥有分布式、单片 ESB 那么沉重,即使它共享一些相同的架构基础(如消息传递和终点)。典型的 ESB 实现往往会导致昂贵的 Web 服务调用以实现最微不足道的交换,并带有各种包袱,而 Spring Integration 允许完全控制将交换从线程内消息传递,通过多线程进程内轮询,一直扩展到进程间/机器间交换(如果需要)(有关此内容,请参阅本系列即将发布的文章)。
第一步是将 Spring Integration 库添加到我们的示例应用程序中。Grails 为应用程序直接依赖的库提供了特定于每个应用程序的位置。最简单的方法是将 Spring Integration 分发 dist 目录的内容复制到我们的 grails 应用程序的 lib 目录中。

复制 Spring Integration dist 目录的内容...

...到您的 Grails 应用程序根目录中的 lib 目录。
添加库使 Spring Integration 可用于您的 Grails 应用程序。现在您已经准备好创建 Spring Integration 管道,在您创建了将为该管道提供服务的组件之后。
> grails create-service DemoBean
执行此命令应该会给您类似以下内容的输出
正在运行脚本 /Applications/SpringSource/grails/grails-1.1-beta1/scripts/CreateService.groovy 环境设置为 development 为 DemoBean 创建了 Service 为 DemoBean 创建了测试
现在 Grails 已经在 grails-app/services 目录中创建了一个 DemoBeanService。它不是最有趣的服务,但它将用于说明 Spring Integration 机制。
要完成服务,请打开 grails-app/services 目录中的 DemoBeanService.groovy 文件,并按如下所示完成它

图 7. 完成的 DemoBeanService 类
该服务有一个方法,其目的是在返回之前将任何传入字符串转换为大写。现在您有了一个服务,当用户将字符串提交到控制器时您希望调用它,是时候将这两个连接起来了。
现在,我们可以直接使用 DI,但对于此示例,我们将通过使用 Spring Integration 来处理将传入字符串传递给我们配置的特定服务,从而为更灵活的架构奠定基础。
Grails 支持使用约定将现有的 Spring XML 配置添加到应用程序中,其中配置保存在 grails-app/conf/spring 目录中的 resources.xml 文件中。为了节省您的输入时间,请在此处下载该文件,然后将其放入您的 grails-app/conf/spring 目录中。
接下来,在您喜欢的文本编辑器中打开 resources.xml,让我们看看发生了什么。
首先,请注意我们正在引入 Spring Integration 命名空间,以便我们可以使用与 Spring Integration 基于 XML 的领域特定语言相关的元素和属性。实际上,Spring Integration 命名空间被设置为文档的默认命名空间,因此我们必须使用命名空间限定 bean 元素。
整个管道然后在此文档中表达。此管道中的主要组件以及这些组件所扮演的角色如下
注意:Grails 中的所有服务类默认都会生成该类的单例服务对象,该对象在 Spring 应用程序上下文中以类名的驼峰式命名版本进行索引。因此,当 service-activator 引用 demoBeanService bean 时,Grails 会自动将其解析为我们之前创建的 DemoBeanService 的单例实例。
就是这样,我们有了一个完整的消息传递管道,它将通过内部队列传递消息,将任何消息提供者与消费者强力解耦。您会注意到,任何创建消息并调用网关的消费者都无需知道消息最终会发送到哪里,甚至无需知道目标端点实现了什么接口。这里的契约是正在传递的消息类型,对于这个简单的示例,它们只是字符串。
为了解决这些松散的末端,您需要创建一个您乐意暴露给控制器的服务接口。请注意,端点实际引用的 bean 不一定需要实现相同的接口,这里重要的是消息参数。
要创建接口,请在 src/java 目录中创建一个名为 MessageService.java 的 Java 接口,位于适当的包结构中,如 com.russmiles.demo.grails.integration。完成接口,使其与以下内容匹配

图 8. MessageService 接口的定义
现在您已经定义了网关接口,最后的工作是将网关本身连接到您的控制器。在 grails 中,您可以根据对象的名称自动提供依赖项,因此要从控制器内部访问网关,您所需要做的就是声明一个名称与网关 ID 匹配的属性

图 9. 将对网关的依赖项添加到您的控制器中
现在您已将网关连接到控制器,您可以修改您的发送闭包以利用它

图 10. 您完成的控制器
就这样,是时候启动您使用 Spring Integration 的 Grails 应用程序了。
一切都连接好后,剩下的就是让您的应用程序运行起来。使用常用命令运行 Grails 应用程序
> grails run-app
现在访问您的浏览器中的 GreetingsMessageSenderController,如下图所示

图 11. 您的表单,已准备好输入以触发您的 Spring Integration 管道
在表单中输入一些文本并点击发送

图 12. 表单中输入了一些数据并已准备就绪
下图显示了接下来会发生什么

图 13. 您的消息,通过管道发送并返回处理后,作为闪存消息的一部分显示
重申一下,当您点击“发送”按钮时,会发生以下情况。
当您点击发送时,您的控制器将调用网关,网关将消息作为 Spring Integration 消息传递到适当的通道,服务激活器将获取该消息,解包并调用 demoBean 服务。服务获取字符串内容并将其转换为大写,并通过为此交换设置的临时通道返回结果。网关上的方法返回,解除控制器的阻塞,然后控制器使用闪存集合中的结果渲染视图。