邮件支持

本节介绍如何在 Spring Integration 中处理邮件消息。

您需要将此依赖项包含到您的项目中:

Maven
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-mail</artifactId>
    <version>5.5.13</version>
</dependency>
Gradle
compile "org.springframework.integration:spring-integration-mail:5.5.13"

javax.mail:javax.mail-api必须通过特定于供应商的实现来包含。

邮件发送通道适配器

Spring Integration 通过MailSendingMessageHandler. 它委托给 Spring 的配置实例JavaMailSender,如以下示例所示:

 JavaMailSender mailSender = context.getBean("mailSender", JavaMailSender.class);

 MailSendingMessageHandler mailSendingHandler = new MailSendingMessageHandler(mailSender);

MailSendingMessageHandler有各种使用 SpringMailMessage抽象的映射策略。如果接收到的消息的负载已经是一个MailMessage实例,则直接发送。因此,我们通常建议您在此消费者之前使用变压器,以满足非平凡的MailMessage构造要求。但是,Spring Integration 支持一些简单的消息映射策略。例如,如果消息有效负载是字节数组,则映射到附件。对于简单的基于文本的电子邮件,您可以提供基于字符串的消息负载。在这种情况下,将MailMessage创建aString作为文本内容。如果您使用其toString()方法返回适当的邮件文本内容的消息有效负载类型,请考虑添加 Spring Integration 的ObjectToStringTransformer在出站邮件适配器之前(有关更多详细信息,请参见使用 XML 配置转换器中的示例)。

您还可以使用 中的MailMessage某些值配置出站MessageHeaders。如果可用,值将映射到出站邮件的属性,例如收件人(收件人、抄送和密送)、发件人、回复人和主题。标头名称由以下常量定义:

 MailHeaders.SUBJECT
 MailHeaders.TO
 MailHeaders.CC
 MailHeaders.BCC
 MailHeaders.FROM
 MailHeaders.REPLY_TO
MailHeaders还允许您覆盖相应MailMessage的值。例如,如果MailMessage.to设置为 ' [email protected] ' 并且MailHeaders.TO提供了邮件头,则它优先并覆盖 中的相应值MailMessage

收信通道适配器

Spring Integration 还通过MailReceivingMessageSource. 它委托给 Spring Integration 自己的MailReceiver接口的配置实例。有两种实现方式:Pop3MailReceiverImapMailReceiver。实例化其中任何一个的最简单方法是将邮件存储的“uri”传递给接收者的构造函数,如以下示例所示:

MailReceiver receiver = new Pop3MailReceiver("pop3://usr:[email protected]/INBOX");

接收邮件的另一个选项是 IMAPidle命令(如果您的邮件服务器支持)。Spring Integration 提供了ImapIdleChannelAdapter,它本身就是一个消息生成端点。它委托给一个实例,ImapMailReceiver但允许异步接收邮件消息。下一节有在“邮件”模式中使用 Spring Integration 的命名空间支持配置两种类型的入站通道适配器的示例。

通常,当IMAPMessage.getContent()调用该方法时,会呈现某些标题和正文(对于简单的文本电子邮件),如以下示例所示:

To: [email protected]
From: [email protected]
Subject: Test Email

something

使用 simple MimeMessagegetContent()返回邮件正文(something在前面的示例中)。

从 2.2 版开始,框架急切地获取 IMAP 消息并将它们公开为MimeMessage. getContent()这具有改变行为的不良副作用。4.3 版中引入的邮件映射增强功能进一步加剧了这种不一致,因为当提供标头映射器时,该IMAPMessage.getContent()方法会呈现有效负载。这意味着 IMAP 内容会有所不同,具体取决于是否提供了标头映射器。

从版本 5.0 开始,来自 IMAP 源的消息会根据IMAPMessage.getContent()行为呈现内容,而不管是否提供了标头映射器。如果您不使用标头映射器并且希望恢复到仅呈现正文的先前行为,请将simpleContent邮件接收器上的布尔属性设置为true. 无论是否使用标头映射器,此属性现在都控制渲染。现在,当提供标头映射器时,它允许仅渲染正文。

从版本 5.2 开始,autoCloseFolder邮件接收器上提供了该选项。将其设置为false不会在获取后自动关闭文件夹,而是将一个IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE标头(有关更多信息,请参阅MessageHeaderAccessorAPI)填充到从通道适配器发送给生产者的每条消息中。这不起作用,Pop3MailReceiver因为它依赖于打开和关闭文件夹来获取新消息。目标应用程序有责任close()在下游流中需要时调用此标头:

Closeable closeableResource = StaticMessageHeaderAccessor.getCloseableResource(mailMessage);
if (closeableResource != null) {
    closeableResource.close();
}

在解析带有附件的电子邮件的多部分内容期间需要与服务器通信的情况下,保持文件夹打开很有用。如果分别在. _ close()_ _IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCEAbstractMailReceiverexpungeshouldDeleteMessagesAbstractMailReceiver

从 5.4 版开始,现在可以返回MimeMessage原样而无需任何转换或急切的内容加载。使用以下选项组合启用此功能:未headerMapper提供,simpleContent属性是falseautoCloseFolder属性是false。它MimeMessage作为生成的 Spring 消息的有效负载存在。在这种情况下,唯一填充的标题是上面提到IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE的在处理完成时必须关闭的文件夹MimeMessage

从版本 5.5.11 开始,如果没有收到任何消息或所有消息都被独立于标志AbstractMailReceiver.receive()过滤掉,该文件夹将自动关闭。autoCloseFolder在这种情况下,对于标头周围的可能逻辑,没有什么可以产生下游IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE

入站邮件消息映射

默认情况下,入站适配器生成的消息负载是原始的MimeMessage. 您可以使用该对象来询问标题和内容。从 4.3 版开始,您可以提供HeaderMapper<MimeMessage>将标头映射到MessageHeaders. 为方便起见,Spring IntegrationDefaultMailHeaderMapper为此提供了一个。它映射以下标头:

  • mail_from:地址的String表示from

  • mail_bcc:包含地址的String数组。bcc

  • mail_cc:包含地址的String数组。cc

  • mail_to:包含地址的String数组。to

  • mail_replyTo:地址的String表示replyTo

  • mail_subject: 邮件主题。

  • mail_lineCount:行数(如果可用)。

  • mail_receivedDate:接收日期(如果有)。

  • mail_size:邮件大小(如果有)。

  • mail_expunged: 一个布尔值,指示消息是否被删除。

  • mail_raw: AMultiValueMap包含所有邮件头及其值。

  • mail_contentType:原始邮件消息的内容类型。

  • contentType:有效负载内容类型(见下文)。

启用消息映射后,有效负载取决于邮件消息及其实现。电子邮件内容通常DataHandlerMimeMessage.

对于text/*电子邮件,有效负载是 aString并且contentType标头与 相同mail_contentType

对于带有嵌入javax.mail.Part实例的消息,DataHandler通常会呈现一个Part对象。这些对象Serializable不适合也不适合使用替代技术进行序列化,例如Kryo. 因此,默认情况下,启用映射时,此类有效负载将呈现为byte[]包含数据的原始Part数据。PartMessage和的例子Multipart。在这种情况下,contentType标题是。application/octet-stream要更改此行为并接收Multipart对象有效负载,请设置embeddedPartsAsBytesfalseon MailReceiver。对于 未知的内容类型,内容将呈现DataHandlerbyte[]带有.contentTypeapplication/octet-stream

当您不提供标头映射器时,消息有效负载MimeMessagejavax.mail. 该框架提供了 a MailToStringTransformer,您可以使用该策略通过将邮件内容转换为 a 的策略来转换消息String

Java DSL
   ...
   .transform(Mail.toStringTransformer())
   ...
java
@Bean
@Transformer(inputChannel="...", outputChannel="...")
public Transformer transformer() {
    return new MailToStringTransformer();
}
科特林
   ...
   transform(Mail.toStringTransformer())
   ...
XML
<int-mail:mail-to-string-transformer ... >

从版本 4.3 开始,转换器处理嵌入式Part实例(以及Multipart之前处理的实例)。转换器是AbstractMailTransformer映射前面列表中的地址和主题标头的子类。如果您希望对消息执行一些其他转换,请考虑子类化AbstractMailTransformer

从版本 5.4 开始,当headerMapper提供 no、autoCloseFolderisfalsesimpleContentisfalse时,在MimeMessage生成的 Spring 消息的有效负载中按原样返回。这样,MimeMessage在流程稍后被引用时,将按需加载 的内容。上述所有转换仍然有效。

邮件命名空间支持

Spring Integration 为邮件相关的配置提供了一个命名空间。要使用它,请配置以下架构位置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:int-mail="http://www.springframework.org/schema/integration/mail"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration/mail
    https://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd">

要配置出站通道适配器,请提供从中接收的通道和 MailSender,如以下示例所示:

<int-mail:outbound-channel-adapter channel="outboundMail"
    mail-sender="mailSender"/>

或者,您可以提供主机、用户名和密码,如以下示例所示:

<int-mail:outbound-channel-adapter channel="outboundMail"
    host="somehost" username="someuser" password="somepassword"/>

从版本 5.1.3 开始,如果提供了host, usernameane mail-sender,则可以省略java-mail-properties。但是hostusername必须配置适当的 Java 邮件属性,例如 SMTP:

[email protected]
mail.smtp.host=smtp.gmail.com
mail.smtp.port=587
与任何出站通道适配器一样,如果引用的通道是 a PollableChannel,您应该提供一个<poller>元素(请参阅Endpoint Namespace Support)。

当您使用命名空间支持时,您还可以使用header-enricher消息转换器。这样做简化了在发送到邮件出站通道适配器之前将前面提到的标头应用于任何消息。

下面的示例假设有效负载是一个 Java bean,具有用于指定属性的适当 getter,但您可以使用任何 SpEL 表达式:

<int-mail:header-enricher input-channel="expressionsInput" default-overwrite="false">
	<int-mail:to expression="payload.to"/>
	<int-mail:cc expression="payload.cc"/>
	<int-mail:bcc expression="payload.bcc"/>
	<int-mail:from expression="payload.from"/>
	<int-mail:reply-to expression="payload.replyTo"/>
	<int-mail:subject expression="payload.subject" overwrite="true"/>
</int-mail:header-enricher>

或者,您可以使用该value属性来指定文字。您还可以指定default-overwrite单个overwrite属性来控制现有标题的行为。

要配置入站通道适配器,您可以在轮询或事件驱动之间进行选择(假设您的邮件服务器支持 IMAP idle — 如果不支持,那么轮询是唯一的选择)。轮询通道适配器需要存储 URI 和向其发送入站消息的通道。pop3URI 可以以或开头imap。以下示例使用imapURI:

<int-mail:inbound-channel-adapter id="imapAdapter"
      store-uri="imaps://[username]:[password]@imap.gmail.com/INBOX"
      java-mail-properties="javaMailProperties"
      channel="receiveChannel"
      should-delete-messages="true"
      should-mark-messages-as-read="true"
      auto-startup="true">
      <int:poller max-messages-per-poll="1" fixed-rate="5000"/>
</int-mail:inbound-channel-adapter>

如果您确实有 IMAPidle支持,则可能需要配置该imap-idle-channel-adapter元素。由于该idle命令启用事件驱动的通知,因此此适配器不需要轮询器。一旦收到新邮件可用的通知,它就会向指定的频道发送一条消息。以下示例配置 IMAPidle邮件通道:

<int-mail:imap-idle-channel-adapter id="customAdapter"
      store-uri="imaps://[username]:[password]@imap.gmail.com/INBOX"
      channel="receiveChannel"
      auto-startup="true"
      should-delete-messages="false"
      should-mark-messages-as-read="true"
      java-mail-properties="javaMailProperties"/>

您可以javaMailProperties通过创建和填充常规java.utils.Properties对象来提供——例如,通过使用utilSpring 提供的命名空间。

如果您的用户名包含“@”字符,请使用“%40”而不是“@”以避免来自底层 JavaMail API 的解析错误。

以下示例显示了如何配置java.util.Properties对象:

<util:properties id="javaMailProperties">
  <prop key="mail.imap.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
  <prop key="mail.imap.socketFactory.fallback">false</prop>
  <prop key="mail.store.protocol">imaps</prop>
  <prop key="mail.debug">false</prop>
</util:properties>

默认情况下,ImapMailReceiver搜索消息基于 default SearchTerm,即所有邮件消息:

  • 是最近的(如果支持)

  • 没有回答

  • 未被删除

  • 没有看到

  • h尚未被此邮件接收者处理(通过使用自定义 USER 标志启用,如果不支持,则简单地不标志)

自定义用户标志是spring-integration-mail-adapter,但您可以对其进行配置。从 2.2 版开始,SearchTerm使用的ImapMailReceiver是完全可配置的SearchTermStrategy,您可以使用search-term-strategy属性注入。ASearchTermStrategy是一个策略接口,只有一个方法,可以让您SearchTerm创建ImapMailReceiver. 以下清单显示了该SearchTermStrategy界面:

public interface SearchTermStrategy {

    SearchTerm generateSearchTerm(Flags supportedFlags, Folder folder);

}

以下示例依赖TestSearchTermStrategy于而不是默认值SearchTermStrategy

<mail:imap-idle-channel-adapter id="customAdapter"
			store-uri="imap:something"
			…
			search-term-strategy="searchTermStrategy"/>

<bean id="searchTermStrategy"
  class="o.s.i.mail.config.ImapIdleChannelAdapterParserTests.TestSearchTermStrategy"/>

有关消息标记的信息,请参阅在不支持时标记 IMAP 消息\Recent

重要:IMAP PEEK

从版本 4.1.1 开始,IMAP 邮件接收器使用mail.imap.peekmail.imaps.peekJavaMail 属性(如果指定)。以前,接收者忽略该属性并始终设置PEEK标志。现在,如果您将此属性显式设置为,则无论 的设置如何,都会false将消息标记为。如果未指定,则保留先前的行为(peek 为)。\SeenshouldMarkMessagesReadtrue

IMAPidle和丢失的连接

使用 IMAPidle通道适配器时,与服务器的连接可能会丢失(例如,由于网络故障),并且由于 JavaMail 文档明确指出实际的 IMAP API 是实验性的,因此了解 API 中的差异以及如何使用非常重要在配置 IMAPidle适配器时处理它们。目前,Spring Integration 邮件适配器使用 JavaMail 1.4.1 和 JavaMail 1.4.3 进行了测试。根据使用哪一个,您必须特别注意一些需要针对自动重新连接设置的 JavaMail 属性。

在 Gmail 中观察到以下行为,但应该为您提供一些关于如何解决与其他提供商重新连接问题的提示。然而,我们总是欢迎反馈。同样,以下注释基于 Gmail。

在 JavaMail 1.4.1 中,如果您将该mail.imaps.timeout属性设置为相对较短的时间段(在我们的测试中约为 5 分钟),则会在此超时后IMAPFolder.idle()抛出。FolderClosedException但是,如果未设置此属性(它应该是不确定的),则该IMAPFolder.idle()方法永远不会返回并且永远不会引发异常。但是,如果连接在短时间内丢失(在我们的测试中不到 10 分钟),它会自动重新连接。但是,如果长时间(超过 10 分钟)断开连接,IMAPFolder.idle()则 , 不会抛出FolderClosedException,也不会重新建立连接,并且无限期保持阻塞状态,因此您无法在不重新启动的情况下重新连接适配器。因此,使用 JavaMail 1.4.1 重新连接的唯一方法是设置mail.imaps.timeout属性明确地设置为某个值,但这也意味着该值应该相对较短(小于 10 分钟)并且应该相对快速地重新建立连接。同样,它可能与 Gmail 以外的提供商不同。JavaMail 1.4.3 对 API 进行了重大改进,确保始终存在强制IMAPFolder.idle()方法返回StoreClosedExceptionFolderClosedException简单返回的条件,从而让您继续进行自动重新连接。当前自动重新连接无限运行,每十秒尝试重新连接。

在这两种配置中,channelshould-delete-messages都是必需的属性。你应该明白为什么should-delete-messages需要。问题出在 POP3 协议上,它不知道已读取的消息。它只能知道在单个会话中已读取的内容。这意味着,当您的 POP3 邮件适配器运行时,电子邮件会被成功使用,因为它们在每次轮询期间可用,并且不会多次发送单个电子邮件。但是,一旦您重新启动适配器并开始新会话,所有可能已在前一个会话中检索到的电子邮件消息都会再次检索到。这就是 POP3 的本质。有些人可能会争辩说should-delete-messages应该true默认。换句话说,有两种有效且互斥的用法使得很难选择一个最佳默认值。您可能希望将适配器配置为唯一的电子邮件接收器,在这种情况下,您希望能够重新启动适配器,而不必担心以前传递的消息不会再次传递。在这种情况下,设置should-delete-messagestrue最有意义。但是,您可能还有另一个用例,您可能希望多个适配器监控电子邮件服务器及其内容。换句话说,你想“偷看但不碰”。然后设置should-delete-messagesfalse更合适。因此,由于很难选择正确的默认值should-delete-messages属性,我们使它成为您设置的必需属性。把它留给你也意味着你不太可能最终出现意外行为。
配置轮询电子邮件适配器的should-mark-messages-as-read属性时,您应该了解您为检索消息而配置的协议。例如,POP3 不支持此标志,这意味着将其设置为任一值均无效,因为邮件未标记为已读。

在静默断开连接的情况下,一个空闲的取消任务会定期在后台运行(通常会立即处理一个新的 IDLE)。为了控制这个间隔,cancelIdleInterval提供了一个选项;默认 120(2 分钟)。RFC 2177 建议间隔不超过 29 分钟。

您应该了解这些操作(将消息标记为已读和删除消息)是在收到消息之后但在处理之前执行的。这可能会导致消息丢失。

您可能希望考虑改用事务同步。请参阅事务同步

还接受“<imap-idle-channel-adapter/>错误通道”属性。如果抛出下游异常并指定了“错误通道”,则会将MessagingException包含失败消息和原始异常的消息发送到此通道。否则,如果下游通道是同步的,则通道适配器会将任何此类异常记录为警告。

从 3.0 版本开始,IMAP适配器会在发生异常时idle发出应用程序事件(特别是实例)。ImapIdleExceptionEvent这允许应用程序检测并处理这些异常。您可以通过使用一个<int-event:inbound-channel-adapter>或任何ApplicationListener配置为接收一个ImapIdleExceptionEvent或一个其超类来获取事件。

\Recent不支持时标记 IMAP 消息

如果shouldMarkMessagesAsRead为真,则 IMAP 适配器设置该\Seen标志。

此外,当电子邮件服务器不支持该标志时,只要服务器支持用户标志\Recent,IMAP 适配器就会使用用户标志(默认为 )标记邮件。spring-integration-mail-adapter如果不是,Flag.FLAGGED则设置为true。无论设置如何,都会应用这些标志shouldMarkMessagesRead

[search-term]中所述,默认SearchTermStrategy忽略如此标记的消息。

从版本 4.2.2 开始,您可以setUserFlag使用MailReceiver. 这样做可以让多个接收者使用不同的标志(只要邮件服务器支持用户标志)。该user-flag属性在使用命名空间配置适配器时可用。

电子邮件过滤

很多时候,您可能会遇到过滤传入消息的要求(例如,您只想阅读Subject行中包含“Spring Integration”的电子邮件)。您可以通过将入站邮件适配器与基于表达式的Filter. 虽然它会起作用,但这种方法有一个缺点。由于消息在通过入站邮件适配器后会被过滤,因此所有此类消息将被标记为已读 ( SEEN) 或未读(取决于should-mark-messages-as-read属性的值)。SEEN但是,实际上,将消息标记为仅在它们通过过滤条件时才更有用。这类似于在预览窗格中滚动浏览所有邮件时查看您的电子邮件客户端,但仅将实际打开并阅读为SEEN.

Spring Integration 2.0.4 引入了on和的mail-filter-expression属性。此属性允许您提供由 SpEL 和正则表达式组合而成的表达式。例如,如果您只想阅读主题行中包含“Spring Integration”的电子邮件,您可以按如下方式配置属性:.inbound-channel-adapterimap-idle-channel-adaptermail-filter-expressionmail-filter-expression="subject matches '(?i).Spring Integration."

由于javax.mail.internet.MimeMessage是 SpEL 评估上下文的根上下文,因此您可以过滤任何可用的值MimeMessage,包括消息的实际正文。这一点特别重要,因为阅读邮件正文通常会导致此类邮件SEEN默认标记为。但是,由于我们现在将PEEK每条传入消息的标志设置为“真”,因此只有明确标记为SEEN的消息才被标记为已读。

因此,在以下示例中,此适配器仅输出与过滤器表达式匹配的消息,并且仅将这些消息标记为已读:

<int-mail:imap-idle-channel-adapter id="customAdapter"
	store-uri="imaps://some_google_address:${password}@imap.gmail.com/INBOX"
	channel="receiveChannel"
	should-mark-messages-as-read="true"
	java-mail-properties="javaMailProperties"
	mail-filter-expression="subject matches '(?i).*Spring Integration.*'"/>

在前面的示例中,由于该mail-filter-expression属性,此适配器仅生成主题行中包含“Spring Integration”的消息。

另一个合理的问题是下一次轮询或空闲事件会发生什么,或者当这样的适配器重新启动时会发生什么。可以过滤重复的按摩吗?换句话说,如果在最后一次检索中,您有 5 条新消息并且只有一条通过了过滤器,那么其他 4 条会发生什么情况?他们会在下一次轮询或空闲时再次通过过滤逻辑吗?毕竟,它们没有被标记为SEEN。答案是不。由于另一个标志(RECENT) 由电子邮件服务器设置并由 Spring Integration 邮件搜索过滤器使用。文件夹实现设置此标志以指示此消息是此文件夹的新消息。也就是说,它自上次打开此文件夹以来已到达。换句话说,虽然我们的适配器可能会偷看电子邮件,但它也让电子邮件服务器知道此类电子邮件已被触摸,因此应被RECENT电子邮件服务器标记为。

事务同步

入站适配器的事务同步允许您在事务提交或回滚后采取不同的操作。您可以通过将<transactional/>元素添加到被轮询的轮询器<inbound-adapter/><imap-idle-inbound-adapter/>. 即使不涉及“真正的”事务,您仍然可以通过将 aPseudoTransactionManager<transactional/>元素一起使用来启用此功能。有关详细信息,请参阅事务同步

由于许多不同的邮件服务器,特别是某些服务器的限制,目前我们只提供这些事务同步的策略。您可以将消息发送到其他一些 Spring Integration 组件或调用自定义 bean 来执行某些操作。例如,要在事务提交后将 IMAP 消息移动到不同的文件夹,您可以使用类似于以下内容的内容:

<int-mail:imap-idle-channel-adapter id="customAdapter"
    store-uri="imaps://something.com:[email protected]/INBOX"
    channel="receiveChannel"
    auto-startup="true"
    should-delete-messages="false"
    java-mail-properties="javaMailProperties">
    <int:transactional synchronization-factory="syncFactory"/>
</int-mail:imap-idle-channel-adapter>

<int:transaction-synchronization-factory id="syncFactory">
    <int:after-commit expression="@syncProcessor.process(payload)"/>
</int:transaction-synchronization-factory>

<bean id="syncProcessor" class="thing1.thing2.Mover"/>

以下示例显示了Mover该类的外观:

public class Mover {

    public void process(MimeMessage message) throws Exception{
        Folder folder = message.getFolder();
        folder.open(Folder.READ_WRITE);
        String messageId = message.getMessageID();
        Message[] messages = folder.getMessages();
        FetchProfile contentsProfile = new FetchProfile();
        contentsProfile.add(FetchProfile.Item.ENVELOPE);
        contentsProfile.add(FetchProfile.Item.CONTENT_INFO);
        contentsProfile.add(FetchProfile.Item.FLAGS);
        folder.fetch(messages, contentsProfile);
        // find this message and mark for deletion
        for (int i = 0; i < messages.length; i++) {
            if (((MimeMessage) messages[i]).getMessageID().equals(messageId)) {
                messages[i].setFlag(Flags.Flag.DELETED, true);
                break;
            }
        }

        Folder somethingFolder = store.getFolder("SOMETHING"));
        somethingFolder.appendMessages(new MimeMessage[]{message});
        folder.expunge();
        folder.close(true);
        somethingFolder.close(false);
    }
}
为了使消息在事务后仍可用于操作,必须将should-delete-messages设置为“false”。

使用 Java DSL 配置通道适配器

为了在 Java DSL 中配置邮件邮件组件,框架提供了一个o.s.i.mail.dsl.Mail工厂,可以这样使用:

@SpringBootApplication
public class MailApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(MailApplication.class)
            .web(false)
            .run(args);
    }

    @Bean
    public IntegrationFlow imapMailFlow() {
        return IntegrationFlows
                .from(Mail.imapInboundAdapter("imap://user:[email protected]:port/INBOX")
                            .searchTermStrategy(this::fromAndNotSeenTerm)
                            .userFlag("testSIUserFlag")
                            .simpleContent(true)
                            .javaMailProperties(p -> p.put("mail.debug", "false")),
                    e -> e.autoStartup(true)
                            .poller(p -> p.fixedDelay(1000)))
                .channel(MessageChannels.queue("imapChannel"))
                .get();
    }

    @Bean
    public IntegrationFlow sendMailFlow() {
        return IntegrationFlows.from("sendMailChannel")
                .enrichHeaders(Mail.headers()
                        .subjectFunction(m -> "foo")
                        .from("[email protected]")
                        .toFunction(m -> new String[] { "[email protected]" }))
                .handle(Mail.outboundAdapter("gmail")
                            .port(smtpServer.getPort())
                            .credentials("user", "pw")
                            .protocol("smtp")),
                    e -> e.id("sendMailEndpoint"))
                .get();
    }
}

1. see XML Configuration