Spring Integration 入门

阅读数:45411 2009 年 11 月 10 日

为什么使用 Spring Integration

Spring Integration 是 Spring 框架创建的又一个 API,面向企业应用集成(EAI)。说到集成,并不缺“解决办法”:硬编码的 Java 客户端、其它 ESB 产品,还有消息队列等更加传统的应用集成技术。Spring Integration 对以上各种解决方法都有所改进,改进的方式有时还颇具戏剧效果。Spring Integration 非常轻量、易于测试;几乎没有入门门槛,概念上比任何“自己编写”的解决方法都要简单。长远来看,它更为灵活、更具有适应性。一旦使用,你就会恋上它。Spring Integration 可以和 EJB、RMI、JMS 这些标准技术协同使用,能让你在一处对复杂的解决方法进行建模,从而对标准技术有所增强。这在很大程度上简化了这些技术的使用。由于 Spring Integration 非常轻量(与应用一起部署 Spring Integration 服务器,不用将应用部署到 Spring Integration 中去),而且很注重开发生命周期(方便配置的 XML schema、友好的 POJO 形式 API、与 Spring 框架和 JEE 的强大集成),所以你会发现跟其它很多的 ESB 产品相比,Spring Integration 要更加适用。

Spring Integration 本身就很强大,毋庸置疑,它从 Spring 框架中得到了强大的支持。比如说,配置格式无非还是 Spring schema,这些配置格式反过来又为你抽象出了 bean 示例。Spring Integration 的使用没什么神奇之处,你可以自信地编写 main(String [] args) 方法来完成 XML 配置所做的一切。Spring Integration 中很多对 RPC 和消息的可用支持都以 Spring 框架的支持为基础。Spring Integration 配置文件中的所有内容仍是标准的 Spring 应用上下文,和通常的 Spring bean 一样,它也受益于依赖注入和运行时可用的方面(Aspect)。使用 Spring Integration,应用上下文就是总线了。比如这能使依赖于应用上下文事件的解决方案成为可能。这是没有独立“运行时”的另一个原因,因为只要上下文可用,总线就存在。

背景

企业应用集成(EAI)是集成应用之间数据和服务的一种应用技术。它解决无限的问题,解决方案也几乎没有穷尽。工程师们已经为这些解决方案的创建努力了数十年。就在最近,我们才确定了原则的最佳实践,并对这些方案进行了分类。

现代 EAI 的模式通常要归功于 Gregor Hohpe 等人编著的《企业集成模式》[1],该书对集成解决方案共有的很多集成模式进行了分类和阐述。Hohpe 等人列出了四种集成风格:

  1. 文件传输:两个系统生成文件,文件的有效负载就是由另一个系统处理的消息。该类风格的例子之一是针对文件轮询目录或 FTP 目录,并处理该文件。
  2. 共享数据库:两个系统查询同一个数据库以获取要传递的数据。一个例子是你部署了两个 EAR 应用,它们的实体类(JPA、Hibernate 等)共用同一个表。
  3. 远程过程调用:两个系统都暴露另一个能调用的服务。该类例子有 EJB 服务,或 SOAP 和 REST 服务。
  4. 消息:两个系统连接到一个公用的消息系统,互相交换数据,并利用消息调用行为。该风格的例子就是众所周知的中心辐射式的(hub-and-spoke)JMS 架构。

这些风格迥然不同,因为没有一种解决办法能在任何情况下都良好运转。这导致整个中间件领域都在基于这些模式寻求可用的解决办法,通常被称为企业服务总线(ESB)。ESB 是最终的中间人:它知道如何使用各种语言在各种协议上调解传递的消息。

这些架构风格是不同的,它们各有所长。通常,JEE 标准存在着不足(坦率地说,现今的任何开发平台都是一样),与其它系统集成时这些标准并不能提供解决方案。考虑到很多项目都是维护项目,新平台中又有多少技术会使用旧服务或功能呢?少之又少,这太令人惊讶了。

JEE 以及后来的 Spring 在简化企业编程模型方面都有了长足的进步。JEE 进行了标准化并商业化了常见企业问题——数据库访问、远程过程调用、事务、认证、目录服务等等。除了基本的 RPC 和消息,JEE 中并没有对 EAI 解决方案的直接支持。

JMS、REST 和 SOAP 都与平台无关,但这是假设有单一的消息协议。比如说,有一个旧的主机应用,其输入、输出都是存放在一些 FTP 端点上的批处理文件,解决方案要求集成该应用就是不可能的。简单来说:现今的中间件能很好地处理常见问题,但在特殊情况的处理上就有所不足了。对大多数公告板或电子邮件列表,还是采取订阅流程。通常,用户给应用发送电子邮件,应用最终接收电子邮件、针对“订阅”解析邮件、提取发送的邮件,然后在邮件列表中登记用户后发送响应。第一反应可能就是基于 CRON 或通过 Quartz 构建定时器应用,以轮询电子邮件,或是为稍后使用的批处理文件去检查 FTP。这种办法很快就会变得单调而脆弱。

之后复杂性会急剧上升。随着时间的推移,应用变得更为重要,与商业伙伴、其它应用、其它平台集成的负担也变得更加昂贵。对必须进行维护的系统来说,每次集成都增加了系统间点对点的新通道。最终,集成各个端点的通道就会成为一个维持不了的烂摊子、复杂的架构。

SpringSource 的 Spring Integration[2] 简化了编程方式,以此改进了标准的 ESB。

如何巩固、梳理架构

企业应用集成有很多模式,同样有很多需要处理的协议。Spring Integration 提供 ESB 风格解决方案的建模能力,但使用方法及其便利性与 Spring 框架并无二致。ESB 不仅能提供消息解决方案的建模能力,还有其它不同的技术 / 协议。

ESB 中的服务

大多数 ESB 产品都有一些共性。其中最重要的有:

  1. 路由:能按条件逻辑或配置好的路由规则路由消息。
  2. 消息传递:对任何复杂的解决方案来说,将消息的有效负载从一种类型转换为另一种类型都至关重要。在消息传递中,标准数据模型 [3] 模式要求系统提供通用的格式。处理消息自然也是 ESB 的另一个重要组成部分。
  3. 调解:适配器和服务映射。
  4. 复杂事件处理(CEP):根据相关性将总线上的事件处理为聚集的能力。
  5. 调用:这应该是最明显的一个。每个 ESB 都要能消费、提供服务。

除了上面列出的服务,ESB 通常还要包括记日志、审计、认证(安全)和管理等机制。

如果你的需求更加复杂,那 ESB 会提供很大的价值。对比你在 JEE 平台中已经获得的东西和 ESB 能带给你的东西很有价值。下表比较了适合于 ESB、可使用 JEE 作为替代解决方案的常见用例。

 

ESB

传统 JEE 中间件

消息队列

可通过 XML 配置,支持所有常见的消息模式。

比如说,如果你使用 Spring JMS 支持就不会很复杂。

RPC

通常可以消费、提供和提取大部分 RPC 服务

同样有无限的可能性,但没有标准方法。

整合异构系统

ESB 旨在分离消费、标准化、提供不同消息过程中的不确定性。

JEE 只能很好地支持一些用例。但这些解决方案往往会很快让事情变得复杂。SOAP 消息到 FTP?批处理文件记录到 EJB 调用?JEE 对每一个都只提供了一半的解决方案。

安全

支持良好

支持良好

传统的主机系统

对 JEE 等支持的互操作习惯有非常好的支持,包括批处理文件

除了 CORBA,JEE 对旧系统没有更多的支持,除非这些系统的前端已经使用了 SOAP。

灵活的路由

路由决策在整个被支持的组件中尽可能延迟

JMS、EJB 等技术指定可用性和路由的配置不尽相同。很难找到一个鸟瞰图。

集成非标准需求的易难性

相对容易,特别是用 Spring Integration 作为所有内容的 POJO,也不用和应用服务器集成;相反,你的解决方案在所有 ESB 产品中并不是标准的

需要深入了解 JCA[4],或是类似于 BEA Tuxedo[5] 这样一个系统。解决方案在所有 JEE 应用服务器中都是标准的(尽管结果可能会有所不同)。

流行的解决方案

Spring Integration 是新生事物,当然会遭受质疑。Mule[6] 是一个非常受欢迎的的 ESB 产品,值得密切关注。Mule 似乎有很大的影响力,在解决方案的灵活性上也令人印象深刻。通过 MuleForge[7] 实现的开源扩展使其成为几乎所有问题令人信服的选择。它是一个标准的服务器:能部署并运行解决方案。Maven 插件有助于开发的生命周期。

ServiceMix[8] 也比较受欢迎。ServiceMix 原本基于 Java 业务集成规范(JBI;JSR 208[9])。JBI 是构建 ESB 产品的 JCP 规范。由于出身 JBI,ServiceMix 不如 Mule 那般灵活,但它正在进行改进。容器正转向 OSGi 基础设施。

这里没有列出其它所有有价值的 ESB 产品,要是有机会你还是要对它们多了解一下。有些非常有价值,值得研究。

Spring Integration 开箱即用的功能表现得很好,非常易于使用。开发用例非常引人注目:如果你已经被 POJO 和近年来测试友好的框架宠坏了,那这个框架也是你的拿手好戏。只要你愿意,你可以使用接口或标准框架类,你还可以完全为 POJO 和你的领域模型进行编码,或者,你可以将两者结合使用。本文中,我选择在解决方案中使用一些框架类,以让发生的事情明确,并提供一致性。有时候,太过不可思议反而会让人困惑,尽管对入门来说很有成效。

使用入门

企业应用集成速成

我们实际构建一个非常普通的应用,以此来演示开发周期。该示例易于理解,但却没有斧凿之嫌。此外,它还是一个很酷的工具。需求是:允许通过电子邮件将博客发送到一个已知地址,然后由该地址发布博客。这么做有一些好的理由。

Blogger([10]Google 的博客服务)已经有这个功能。但我在我博客上运行 Apache Roller([11]),所以我可以使用这个解决方案。写博客的惰性很大程度上是没时间进入“发布”模式的副作用。Roller 的软件强迫用户登录并撰写博客文章。这么做很麻烦,尤其是你每次都不得不对付 WYIWYG(所见即所得)编辑器。第二,构建该解决办法最好的理由是,它提供了了一种简单的集成,能与尽可能少的运转部分集成在一起。我们可以在本文中仔细分析该办法。尽管这是假设的,但很有实际用途。

构建集成非常简单,最好的“IDE”只是一张纸和一支笔。可以在很多工具中画图。将图转换成你喜欢的 ESB 配置格式或工具非常简单。ESB 使用相同的术语。了解术语要比了解任何一种工具或 ESB 更为重要。

让我们回顾一些 ESB 101 定义,权当补习了。消息是传递到端点的有效负载。端点是通道的终点。通道是两个端点间被命名的连接。通常,消息来自于消息系统,被分发到不了解消息系统的应用中。同样的事情或者会反过来以其它方式出现:应用可能会发送数据,但并不理解消息系统。针对这些问题,就需要通道适配器了。

就是这样!这些就是你需要了解的条目,以便讨论解决方案。其它条目都是这些条目之上的变异或详细说明,或者是关于集成模式的,而不是关于集成本身。

Spring Integration 概况

Spring Integration 应用就是使用 Spring schema 配置的简单 Java 程序。如果你倾向于用常规的 Spring 配置来配置一切,就可以不使用 schema。Schema 会使事情更为简单,这和用 schema 配置使用 Spring 中方面的事务功能会更加简单大致一样。Spring Integration 为一般概念(集成命名空间)提供了方便的 schema,还有适配器的具体配置,比如针对电子邮件或文件类型的配置。

Spring Integration 应用处理从通道传递过来的消息概念。消息的生命周期始于一个端点,通常是对适配器做出的反应。消息在经过处理管道的过程中,会在总线内被转化、路由至其它通道、分发、响应,或者被中断并发送到一个死消息通道中去。如果你使用 Spring Integration 接口编程——我们将在很大程度上尽可能保持内容一致并明确,那你要处理 Message 对象,Message 对象如图 1 所示。

Message 类是个包装器,包装被处理消息的有效负载。对它进行操作,很容易获得有效负载和消息头,你可以对有效负载进行类型转换,可以检查、改变消息头。Spring Integration 不要求你使用 Message 接口。你的应用可以暴露一些方法,这些方法的参数类型跟你消息有效负载的类型相同。比如说,来自于文件适配器(可以从文件系统发送消息的适配器)的消息可能会被改为 java.io.File 实例。

让我们来看一个集成例子,它把电子邮件发送到一个电子邮件地址,转换电子邮件后再将其发布到博客中。

示例的配置在 src/main/resources/integrationsContext.xml 中。全部源码可以从这里下载。integrationsContext.xml 文件乍看起来很平常。XML 顶端的 bean 负责将属性文件中的变量宏替换到该 XML 配置脚本中。后面导入了另一个 Spring 文件,该文件包含本集成中要使用的简单服务。再没什么特别的了。

接下来继续看相继定义的四个 bean,它们还是有意思的:服务催化器 newBlogPublishingServiceActivator、转换器 emailToBlogTransformer、处理器 outboundBlogPostHandler 和过滤器 emailsInFilterAgainstWhitelist。这四个 bean 会在配置的后面部分里用到。

实质配置的第一段是 poller 元素,它是文档的默认轮询器。确切来说,轮询器是一种机制,在有变化时轮询不同端点,并在感知到某些内容发生变化后让适配器对其做出响应,就像一个事件。出于简单,我们为整个 Spring Integration 配置一个默认的轮询器。见图 2。

接下来的三个元素是三个通道声明。他们毫无意义,只是命名的通道而已。没有端点或适配器的通道毫无用处。见图 3。

下一行配置了电子邮件适配器。电子邮件适配器查找发入系统的电子邮件,并将其放入名叫 emailsIn 的通道,该通道已经在上面定义了。电子邮件发进来时,电子邮件系统并不会播报事件。在出现异常的时候(比如 Spring Integration 也支持的 IMAP IDLE),你通常需要一些东西来轮询电子邮件帐户以查找新的电子邮件。只要有新消息,它们就一次阅读一条消息,并将该消息传递到处理管道的下一个组件。见图 4。

现在消息在 emailsIn 通道中传递,被传递到通道的下一个组件——转换器 bean。转换器获取所有给它的内容,以某种方式改变消息(我们稍后会详细讨论这一点),然后顺便发送出去。这种情况下,会给 Transformer 给定一个包含 MimeMessage 类型有效负载的 Message,转化器用于创建 BlogPost 类型的一个对象。BlogPost 是我们系统特定的一个领域类。我们在这里把它作为 DTO 来用,但很显然,它来自哪里并不重要 ——如果你愿意,根据你的领域去处理它也可以。Transformer 元素的最佳用法是:将消息格式转换为系统常见的格式。这跟标准数据模式相关。

由此产生的 BlogPost 封装在一条新消息中,其中包含原始消息的头信息。头信息正是你期望的那样:特定于请求的值,可以对其访问以获取更多的元数据。例如,从内部来说,Spring Integration 总线给每样内容分配一个 ID。该 ID 暴露为头信息值供你使用。该 ID 保证是唯一的。

然后消息被发送给过滤器 emailsInFilterAgainstWhitelist,该过滤器访问有效负载获取发送者的值。这样做是为了确保在发布到博客的过程中你不会收到垃圾邮件。这个例子跟整个解决方案一样,简单的有些不合常理。你可以想象是去查询 Spring 安全或 LDAP,或是比这个做得还要好的东西。见图 5:

XML 配置的最后一段内容是 service-activator 元素,它获取所有给它的内容并进行处理。在这种情况下,我们告知 Spring Integration 调用 ID 为 newBlogPublishingServiceActivator 的 bean 的 pubishIncomignBlogEntry 方法。该方法负责实际获取有效负载,并将有效负载发送给发布博客条目的服务。

然后我们就大功告成了。内容看起来很多,但实际上就是相关 XML 的四段内容。声明通道是为了讲清楚例子。poller 元素只定义了一次,这样,上下文中需要轮询器的所有内容都可以利用这个缺省的轮询器了。

可能的扩展

我们定义了三个通道、四个组件。通过这样设计构成解决方案的组件,我们能让这些组件在其它集成解决方案中可以重用。集成流程不仅仅限于此示例。我们可能会在更复杂的集成中重用转换器、过滤器,甚至是服务催化器。唯一需要重构的是 Spring Integration XML 文件。有很多可能性。一个好的 ESB 应该做到:尽可能晚地重构流程设计和配置。

这里我们很容易就能想象到可以在什么地方采用该解决方案。下面是一些可能的补充。

  1. 对于某些审计工作流,使用 jBPM([12])对系统增加支持。假设我们想在解决方案中添加业务流程管理(BPM),比如在消息传入时,以某种工作队列的分类存储该消息,以便编辑器进行核准。编辑过程可以处理得跟工作流引擎内部要求的一样。最终的博客内容核准后,编辑器把定稿发送到同样的电子邮件地址,带着某种关键字或识别出能用作相关 ID 的关键字。相关 ID 可用来让集成解决方案继续进行,主要表现在更新的条目。见 [13]。
  2. 使用 Spring Integration“组播”博客。当然,博客已经发布了。但还有一个问题,用旧的哲学问题来描述再合适不过了:“如果在 woods 中 RSS 订阅已经更新了,而没有人轮询到它,那博客算真正更新了吗?”呃,也许我只是讲了大概的意思!也可以通过 Twitter 传播博客标题,可以使用接收者列表模式([16])通知博客聚合器([14]、[15] 等)。
  3. 更新过滤器例子来验证某种真正授权的服务。也许能使用 LDAP 或 Spring 安全。
  4. 除了简单的电子邮件之外,多样化对发布的支持。FTP、 WebDav、文件系统(在源码中,电子邮件适配器的下面注释了一个简单的文件目录适配器配置)等都是可行的输入类型。更先进的例子则可以从移动客户端发送 SMS 消息。(当然,我不确定有多少人用他们的手机写博客。你永远也不会知道。不过只有一种方式能找到答案。)目前还没有对此的支持,但阅读了 Spring Integration 的源码后会发现,很容易构建自己的适配器。你可以使用 SMSLib[17] 类似的库。

Spring Integration 的不足之处

Spring Integration 是全新且强大的。你可以对其背后的 SpringSource 的价值及其自身的不断发展抱有信心。但这并不意味着它是完美的——它离完美还远着哩!应用集成不是一个新的领域,考虑解决方案和架构已经有数十年。应用集成的一些用法按惯例包含了重量级的适配器,比如那些与 SAP 集成的集成方法或 JDEdwards OneWorld。Spring Integration 不能直接支持这些具体情况。

尽管 Spring Integration 支持大量开箱即用的功能,但它对一些典型的适配器缺少支持,比如 SFTP、HTTPS 或 AS2。目前,一些专有的解决方案能更好地支持这些需求。有些解决方案非常昂贵,所以你可以为 Spring Integration 试着改造第三方库、编写自己的适配器。如果你有兴趣,你会因为为 Spring Integration 编写适配器相当简单而感到惊喜。你要想开始创建解决方案,只需要看看 jSch[18]、Jakarta Commons VFS[19] 或 Jakarta Commons Net [20]。

最终,Spring Integration 可能并不是你现在最佳的解决方案。如果不是,请不要担心。你在本文中学到的东西也能应用在其他方案中。来吧,集成!

结论

Spring Integration 是一个干净、简练的 EAI 手段,也是很好的 ESB 产品替代者。现在的 ESB 方案沉重且很难引入到一些环境中。Spring Integration 则是一个功能强大、低摩擦的替代方案,它能温和地解决一些最复杂的集成问题。

资源

1、Amazon.com 上的《企业集成模式》

2、Spring Source 的 Spring Integration 项目,http://www.springsource.org/spring-integration

3、企业应用集成中的“标准数据模型”,http://www.eaipatterns.com/CanonicalDataModel.html

4、JCA 规范,http://java.sun.com/j2ee/connector/

5、BEA 的 Tuxedo 产品,http://www.oracle.com/products/middleware/tuxedo/tuxedo.html

6、Mule 主页,http://mule.mulesource.org/display/MULE/Home

7、Mule Forge,Mule 扩展的铸造厂,http://www.muleforge.org

8、Apache ServiceMix,http://servicemix.apache.org/home.html

9、JSR 208,Java 业务集成,http://jcp.org/aboutJava/communityprocess/final/jsr208/index.html

10、Blogger,Google 的博客服务,https://www.blogger.com/start

11、Apache Roller,http://roller.apache.org/

12、JBoss.org 的 jBPM,http://www.jboss.org/jbossjbpm/

13、企业应用集成中的“聚合器”,http://www.eaipatterns.com/Aggregator.html

14、Dzone 博客聚合器,http://www.dzone.com

15、JavaBlogs 博客聚合器,http://www.javablogs.com

16、企业应用集成中的“接收者列表”,http://www.integrationpatterns.com/RecipientList.html

17、SMSLib,一个 Java SMS 库,http://smslib.org

18、jSch,http://www.jcraft.com/jsch/

19、Apache Commons VFS(Virtual File System,虚拟文件系统),http://commons.apache.org/vfs/

20、Jakarta Commons Net,http://commons.apache.org/net/

21、我的博客,http://www.joshlong.com

关于作者

Josh Long 是亚利桑那州菲尼克斯的一名企业架构师、宣讲者。闲暇时会出没于当地的 Java 用户组或咖啡馆。其博客是http://www.joshlong.com([21]),可以通过josh@joshlong.com联系他。

在这里下载源码。

查看英文原文:Getting Started With Spring Integration


感谢张龙对本文的审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家加入到InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

收藏

评论

微博

发表评论

注册/登录 InfoQ 发表评论