使用入门 企业应用集成速成
我们实际构建一个非常普通的应用,以此来演示开发周期。该示例易于理解,但却没有斧凿之嫌。此外,它还是一个很酷的工具。需求是:允许通过电子邮件将博客发送到一个已知地址,然后由该地址发布博客。这么做有一些好的理由。
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元素只定义了一次,这样,上下文中需要轮询器的所有内容都可以利用这个缺省的轮询器了。