9.3 创建Email集成流
我们决定Taco Cloud应该允许客户通过Email提交taco设计和创建订单。我们发放传单并在报纸上刊登外卖广告,邀请每个人通过Email发送taco订单。这非常成功!但是,令人遗憾的是,它过于成功了。有太多的Email涌了进来,我们不得不申请临时帮助,让别人阅读所有的Email并将订单提交到订单系统中。
在本节中,我们将会实现一个集成流,轮询Taco Cloud的taco订单Email的收件箱、解析Email中的订单细节并将订单提交给Taco Cloud来进行处理。简而言之,在我们所创建的集成流中,入站通道适配器将会使用Email端点模块摄取TacoCloud收件箱中的Email到集成流中。
集成流的下一步会将Email解析为订单对象,这些订单对象会被传递给另一个处理器,从而将订单提交至Taco Cloud的REST API中,在这里我们会像其他订单那样处理它们。首先,我们定义一个简单的配置属性类,它会捕获处理Taco CloudEmail的特定信息:
1 |
|
我们可以看到,EmailProperties会捕获生成IMAP URL的属性。这个流会使用这个URL连接Taco Cloud Email服务器并轮询Email。在捕获的属性中包括Email用户的用户名和密码以及IMAP服务器的主机、要轮询的邮箱以及邮箱轮询的频率(默认为30秒)。
EmailProperties在类级别使用了@ConfigurationProperties
注解,并将prefix属性设置为tacocloud.email。这意味着,我们可以在application.yml文件中按照下述方式配置消费Email的详细信息:
1 | tacocloud: |
现在,我们使用EmailProperties来配置集成流。我们想要创建的流大致如图9.10所示。
我们有两种方案来定义这个流。
- 在Taco Cloud应用中进行定义:在流的结束点,服务激活器要调用我们之前定义的创建订单的repository。
- 在单独的应用中进行定义:在流的结束点,服务激活器要发送POST请求到Taco Cloud API以提交taco订单。
不管选择哪种方式,除了服务激活器的实现方式之外,对流的本身影响并不大。但是,因为我们需要一些表示taco、订单和配料的类型,它们与Taco Cloud主应用可能会略微有所差异,所以我们会在一个单独的应用中定义集成流,避免与已有的领域类型相混淆。
我们还可以选择使用XML配置、Java配置或者Java DSL来定义流。我更喜欢DSL的优雅,所以在这里将会使用这种方案。如果你想要一些额外的挑战,也可以选择其他配置风格编写流的定义。现在,我们看一下taco Email订单流的Java DSL配置,如程序清单9.5所示。
程序清单9.5 定义接收Email并将其提交为订单的集成流
1 | package tacos.email; |
根据tacoOrderEmailFlow()方法的定义,taco Email订单流由3个不同的组件组成。
- MAP Email入站通道适配器:使用IMP URL创建,而URL是根据EmailProperties的getImapUrl()方法创建的,并且会根据EmailProperties中设置的pollRate属性进行轮询。传入的Email会传递给一个通道,然后连接到转换器。
- 将Email转换成订单对象的转换器:转换器是通过EmailToOrderTransformer实现的,它会注入tacoOrderEmailFlow()方法中。转换所形成的订单会通过另外一个通道传递给最后一个组件。
- 处理器(作为出站通道适配器):处理器接受订单对象并将其提交至TacoCloud的REST API。
我们只有将Email端点模块作为依赖项添加到项目构建文件中,才能调用Mail.imap InboundAdapter()。Maven依赖如下所示:
1 | <dependency> |
EmailToOrderTransformer是Spring Integration Transformer接口的实现,扩展了AbstractMailMessageTransformer(如程序清单9.6所示)。
程序清单9.6 使用集成转换器将传入的Email转换为taco订单
1 |
|
AbstractMailMessageTransformer是一个很便利的基类,适用于载荷为Email的消息。它会抽取传入消息Email的信息,并将它放到一个Message对象中,传递给doTransform()方法。在doTransform()方法中,我们将Message对象传递给一个名为processPayload()的private方法,将Email解析为Order对象。这个Order对象尽管和主Taco Cloud应用中的Order对象有些相似,但是并不完全相同,这里更加简单一些:
1 | package tacos.email; |
这个Order类不包含客户完整的投递信息和账单信息,而是只携带了客户的Email地址(通过传入的Email获取的)。
将Email解析成订单是一项非常重要的任务。实际上,即便最简单的实现也需要几十行代码。这些代码对于进一步讨论Spring Integration和如何实现转换器并没有任何助益。所以,为了节省空间,我在这里省略了processPayload()方法的细节。
EmailToOrderTransformer做的最后一件事情就是返回一个MessageBuilder,让消息的载荷中包含Order对象。MessageBuilder所生成的消息会发送至集成流的最后一个组件:将订单提交至Taco Cloud API的消息处理器。OrderSubmitMessageHandler实现了Spring Integration的GenericHandler,它会处理带有Order载荷的消息,如程序清单9.7所示。
程序清单9.7 通过消息处理器将订单提交至Taco Cloud API
1 | package tacos.email; |
为了满足GenericHandler接口的要求,OrderSubmitMessageHandler重写了handle()方法,这个方法接收传入的Order对象,并使用注入的RestTemplate利用POST请求将Order提交至ApiProperties对象指定的URL。最后,handle()方法返回null,表明这个处理器是流的终点。
这里使用ApiProperties避免在postForObject()时硬编码URL。它是一个配置属性类,如下所示:
1 |
|
在application.yml中,Taco Cloud API的URL可能会配置如下:
1 | tacocloud: |
为了让这个应用能够使用RestTemplate,并自动注入OrderSubmitMessageHandler中,我们需要在项目的构建文件中添加SpringBoot web starter依赖:
1 | <dependency> |
这不仅会将RestTemplate添加到类路径中,还会触发Spring MVC的自动配置功能。作为独立的Spring Integration流,这个应用并不需要Spring MVC,更不需要自动配置所提供的嵌入式Tomcat。所以,我们可以在application.yml中通过如下的配置条目禁用Spring MVC的自动配置:
1 | spring: |
spring.main.web-application-type属性可以设置为servlet、reactive或none。当Spring MVC位于类路径之中时,自动配置功能会将其设置为servlet。我们在这里将其重写为none,所以Spring MVC和Tomcat将不会进行自动配置(我们将会在第11章介绍反应式Web应用是什么样子的)。