邮件支持
本节介绍如何在 Spring 集成中使用邮件消息。
您需要将此依赖项包含到您的项目中
-
Maven
-
Gradle
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mail</artifactId>
<version>6.3.5</version>
</dependency>
compile "org.springframework.integration:spring-integration-mail:6.3.5"
必须通过供应商特定的实现包含jakarta.mail:jakarta.mail-api
。
邮件发送通道适配器
Spring 集成通过MailSendingMessageHandler
提供对出站电子邮件的支持。它委托给 Spring 的已配置JavaMailSender
实例,如下例所示
JavaMailSender mailSender = context.getBean("mailSender", JavaMailSender.class);
MailSendingMessageHandler mailSendingHandler = new MailSendingMessageHandler(mailSender);
MailSendingMessageHandler
具有各种映射策略,这些策略使用 Spring 的MailMessage
抽象。如果接收到的消息的有效负载已经是MailMessage
实例,则会直接发送。因此,我们通常建议您在该使用者之前添加一个转换器,以满足非平凡的MailMessage
构建要求。但是,Spring 集成支持一些简单的消息映射策略。例如,如果消息有效负载是字节数组,则将其映射到附件。对于简单的基于文本的电子邮件,您可以提供基于字符串的消息有效负载。在这种情况下,将使用该String
作为文本内容创建一个MailMessage
。如果您使用的是其toString()
方法返回适当邮件文本内容的消息有效负载类型,请考虑在出站邮件适配器之前添加 Spring 集成的ObjectToStringTransformer
(有关详细信息,请参阅使用 XML 配置转换器中的示例)。
您还可以使用MessageHeaders
中的某些值配置出站MailMessage
。如果可用,则将值映射到出站邮件的属性,例如收件人(To、Cc 和 BCc)、from
、reply-to
和subject
。标题名称由以下常量定义
MailHeaders.SUBJECT
MailHeaders.TO
MailHeaders.CC
MailHeaders.BCC
MailHeaders.FROM
MailHeaders.REPLY_TO
MailHeaders 还可以让您覆盖相应的MailMessage 值。例如,如果MailMessage.to 设置为'[email protected]'并且提供了MailHeaders.TO 消息头,则它将优先并覆盖MailMessage 中的相应值。 |
邮件接收通道适配器
Spring 集成还通过MailReceivingMessageSource
提供对入站电子邮件的支持。它委托给 Spring 集成自己的MailReceiver
接口的已配置实例。有两个实现:Pop3MailReceiver
和ImapMailReceiver
。实例化这两个实现的最简单方法是绕过邮件存储的“uri”到接收器的构造函数,如下例所示
MailReceiver receiver = new Pop3MailReceiver("pop3://usr:pwd@localhost/INBOX");
接收邮件的另一个选项是 IMAP idle
命令(如果您的邮件服务器支持)。Spring 集成提供ImapIdleChannelAdapter
,它本身就是一个消息生成端点。它委托给ImapMailReceiver
的实例。下一节提供了使用 Spring 集成的命名空间支持在“mail”模式下配置这两种类型的入站通道适配器的示例。
通常,当调用 |
To: [email protected]
From: [email protected]
Subject: Test Email
something
对于简单的MimeMessage
,getContent()
返回邮件正文(在上例中为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
标头(有关详细信息,请参阅MessageHeaderAccessor
API)填充到通道适配器生成的每个消息中。这与Pop3MailReceiver
不兼容,因为它依赖于打开和关闭文件夹以获取新消息。目标应用程序有责任在必要时在下游流中调用此标头上的close()
。
Closeable closeableResource = StaticMessageHeaderAccessor.getCloseableResource(mailMessage);
if (closeableResource != null) {
closeableResource.close();
}
在需要在解析带有附件的电子邮件的多部分内容期间与服务器进行通信的情况下,保持文件夹打开非常有用。如果shouldDeleteMessages
在AbstractMailReceiver
上分别配置,则IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE
标头上的close()
将委托给AbstractMailReceiver
以使用expunge
选项关闭文件夹。
从 5.4 版开始,现在可以按原样返回MimeMessage
,无需任何转换或急切的内容加载。此功能通过以下选项组合启用:不提供headerMapper
,simpleContent
属性为false
,autoCloseFolder
属性为false
。MimeMessage
作为生成的 Spring 消息的有效负载存在。在这种情况下,唯一填充的标头是上面提到的IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE
,用于必须在完成MimeMessage
处理时关闭的文件夹。
从 5.5.11 版开始,如果未收到任何消息或所有消息都被独立于autoCloseFolder
标志过滤掉,则在AbstractMailReceiver.receive()
之后会自动关闭文件夹。在这种情况下,没有要向下游生成的内容,以便进行IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE
标头的可能逻辑。
从 6.0.5 版开始,ImapIdleChannelAdapter
不再执行异步消息发布。这是为了阻塞空闲侦听器循环以进行下游消息处理(例如,带有大型附件)所必需的,因为邮件文件夹必须保持打开状态。如果需要异步传递,可以使用ExecutorChannel
作为此通道适配器的输出通道。
入站邮件消息映射
默认情况下,入站适配器生成的消息的有效负载是原始MimeMessage
。您可以使用该对象来查询标头和内容。从 4.3 版开始,您可以提供HeaderMapper<MimeMessage>
将标头映射到MessageHeaders
。为了方便起见,Spring 集成为此目的提供了一个DefaultMailHeaderMapper
。它映射以下标头
-
mail_from
:from
地址的String
表示形式。 -
mail_bcc
:包含bcc
地址的String
数组。 -
mail_cc
:包含cc
地址的String
数组。 -
mail_to
:包含to
地址的String
数组。 -
mail_replyTo
:replyTo
地址的String
表示形式。 -
mail_subject
:邮件主题。 -
mail_lineCount
:行数(如果可用)。 -
mail_receivedDate
:接收日期(如果可用)。 -
mail_size
:邮件大小(如果可用)。 -
mail_expunged
:指示邮件是否已删除的布尔值。 -
mail_raw
:包含所有邮件标头及其值的MultiValueMap
。 -
mail_contentType
:原始邮件消息的内容类型。 -
contentType
:有效负载内容类型(见下文)。
启用消息映射时,有效负载取决于邮件消息及其实现。电子邮件内容通常由MimeMessage
中的DataHandler
呈现。
对于text/*
电子邮件,有效负载是String
,contentType
标头与mail_contentType
相同。
对于包含嵌入式jakarta.mail.Part
实例的消息,DataHandler
通常呈现Part
对象。这些对象不是Serializable
,不适合与Kryo
等替代技术一起序列化。因此,默认情况下,启用映射时,此类有效负载将呈现为包含Part
数据的原始byte[]
。Part
的示例包括Message
和Multipart
。在这种情况下,contentType
标头为application/octet-stream
。要更改此行为并接收Multipart
对象有效负载,请将MailReceiver
上的embeddedPartsAsBytes
设置为false
。对于DataHandler
未知的内容类型,内容将呈现为byte[]
,其contentType
标头为application/octet-stream
。
如果不提供标头映射器,则消息有效负载是由jakarta.mail
呈现的MimeMessage
。框架提供了一个MailToStringTransformer
,您可以使用它通过使用策略将邮件内容转换为String
来转换邮件。
-
Java DSL
-
Java
-
Kotlin
-
XML
...
.transform(Mail.toStringTransformer())
...
@Bean
@Transformer(inputChannel="...", outputChannel="...")
public Transformer transformer() {
return new MailToStringTransformer();
}
...
transform(Mail.toStringTransformer())
...
<int-mail:mail-to-string-transformer ... >
从 4.3 版开始,转换器处理嵌入式Part
实例(以及以前处理的Multipart
实例)。转换器是AbstractMailTransformer
的子类,它从前面的列表中映射地址和主题标头。如果您希望对邮件执行其他转换,请考虑创建AbstractMailTransformer
的子类。
从 5.4 版开始,如果没有提供headerMapper
,autoCloseFolder
为false
并且simpleContent
为false
,则生成的 Spring 消息的有效负载中将按原样返回MimeMessage
。这样,以后在流程中引用时,MimeMessage
的内容将按需加载。所有上面提到的转换仍然有效。
邮件命名空间支持
Spring 集成提供用于邮件相关配置的命名空间。要使用它,请配置以下模式位置
<?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 版开始,如果提供java-mail-properties
,则可以省略host
、username
和mail-sender
。但是,必须使用适当的 Java 邮件属性配置host
和username
,例如对于 SMTP
[email protected]
mail.smtp.host=smtp.gmail.com
mail.smtp.port=587
与任何出站通道适配器一样,如果引用的通道是PollableChannel ,则应提供<poller> 元素(参见端点命名空间支持)。 |
当您使用命名空间支持时,您也可以使用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和用于发送入站消息的通道。URI可能以pop3
或imap
开头。以下示例使用imap
URI
<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>
如果您有IMAP idle
支持,则可能需要配置imap-idle-channel-adapter
元素。由于idle
命令启用事件驱动的通知,因此此适配器不需要轮询器。它在收到有新邮件可用通知后立即将消息发送到指定的通道。以下示例配置了一个IMAP idle
邮件通道
<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"/>
您可以通过创建和填充常规的java.utils.Properties
对象来提供javaMailProperties
——例如,通过使用Spring提供的util
命名空间。
如果您的用户名包含“@”字符,请使用“%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
根据默认的SearchTerm
搜索邮件,该SearchTerm
是所有满足以下条件的邮件:
-
是RECENT(如果支持)
-
不是ANSWERED
-
不是DELETED
-
不是SEEN
-
尚未被此邮件接收器处理(通过使用自定义USER标志启用,或者如果不受支持则 simply NOT FLAGGED)
自定义用户标志是spring-integration-mail-adapter
,但您可以对其进行配置。从2.2版本开始,ImapMailReceiver
使用的SearchTerm
可以通过SearchTermStrategy
完全配置,您可以使用search-term-strategy
属性注入它。SearchTermStrategy
是一个策略接口,它只有一个方法,允许您创建ImapMailReceiver
使用的SearchTerm
实例。下面的列表显示了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"/>
有关消息标记的信息,请参见当不支持Recent
时标记IMAP消息。
重要:IMAP PEEK
从4.1.1版本开始,如果指定了 |
IMAP idle
和连接丢失
使用IMAP idle
通道适配器时,与服务器的连接可能会丢失(例如,通过网络故障),并且由于JavaMail文档明确指出实际的IMAP API是实验性的,因此了解API的差异以及在配置IMAP idle
适配器时如何处理这些差异非常重要。目前,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()
方法返回StoreClosedException
或FolderClosedException
或简单地返回,从而允许您继续进行自动重新连接。目前,自动重新连接会无限期地运行,每十秒尝试一次重新连接。
在这两种配置中,channel 和should-delete-messages 都是必需的属性。您应该理解为什么should-delete-messages 是必需的。问题在于POP3协议,它不知道哪些邮件已被读取。它只能知道在一个会话中读取了什么。这意味着,当您的POP3邮件适配器运行时,电子邮件会在每次轮询期间按可用情况成功使用,并且不会将单个电子邮件消息传递多次。但是,一旦您重新启动适配器并开始新的会话,在先前会话中可能已检索到的所有电子邮件消息都会再次检索。这就是POP3的性质。有些人可能会认为should-delete-messages 应该默认为true 。换句话说,有两个有效且相互排斥的用途,这使得很难选择一个最佳的默认值。您可能希望将您的适配器配置为唯一的电子邮件接收器,在这种情况下,您希望能够重新启动适配器,而无需担心以前传递的消息不会再次传递。在这种情况下,将should-delete-messages 设置为true 是最有意义的。但是,您可能还有另一个用例,您可能希望有多个适配器监视电子邮件服务器及其内容。换句话说,您想“窥视但不触摸”。然后将should-delete-messages 设置为false 更为合适。因此,由于很难选择should-delete-messages 属性的正确默认值,我们将其设置为必须由您设置的必需属性。将其留给您也意味着您不太可能出现意外行为。 |
配置轮询电子邮件适配器的should-mark-messages-as-read 属性时,您应该了解您正在配置用于检索消息的协议。例如,POP3不支持此标志,这意味着将其设置为任何值都不会产生任何影响,因为邮件不会被标记为已读。 |
在静默断开连接的情况下,空闲取消任务会在后台定期运行(新的IDLE通常会立即处理)。为了控制此间隔,提供了cancelIdleInterval
选项;默认值为120(2分钟)。RFC 2177建议间隔不超过29分钟。
您应该了解,这些操作(标记邮件为已读和删除邮件)是在接收邮件后但在处理邮件之前执行的。这可能会导致邮件丢失。 您可能希望考虑改用事务同步。参见事务同步。 |
<imap-idle-channel-adapter/>
还接受“error-channel”属性。如果抛出下游异常并且指定了“error-channel”,则包含失败消息和原始异常的MessagingException
消息将发送到此通道。否则,如果下游通道是同步的,则通道适配器会将任何此类异常记录为警告。
从3.0版本开始,IMAP idle 适配器在发生异常时会发出应用程序事件(特别是ImapIdleExceptionEvent 实例)。这允许应用程序检测并处理这些异常。您可以使用<int-event:inbound-channel-adapter> 或任何配置为接收ImapIdleExceptionEvent 或其超类的ApplicationListener 来获取这些事件。 |
当\Recent
不受支持时标记IMAP消息
如果shouldMarkMessagesAsRead
为true,则IMAP适配器将设置\Seen
标志。
此外,当电子邮件服务器不支持\Recent
标志时,只要服务器支持用户标志,IMAP适配器就会使用用户标志(默认为spring-integration-mail-adapter
)标记邮件。如果不支持,则将Flag.FLAGGED
设置为true
。这些标志的应用与shouldMarkMessagesRead
设置无关。
如null中所述,默认的SearchTermStrategy
会忽略已标记的消息。
从4.2.2版本开始,您可以使用MailReceiver
上的setUserFlag
设置用户标志的名称。这样做可以让多个接收器使用不同的标志(只要邮件服务器支持用户标志)。在使用命名空间配置适配器时,可以使用user-flag
属性。
电子邮件消息过滤
很多时候,您可能会遇到过滤传入邮件的需求(例如,您只想阅读主题行中包含“Spring Integration”的电子邮件)。您可以通过将入站邮件适配器与基于表达式的Filter
连接来实现此目的。尽管它可以工作,但这方法有一个缺点。由于邮件会在通过入站邮件适配器后进行过滤,因此所有此类邮件都将标记为已读(SEEN
)或未读(取决于should-mark-messages-as-read
属性的值)。但是,实际上,只有在邮件通过过滤条件后才将其标记为SEEN
会更有用。这类似于在预览窗格中滚动浏览所有邮件时查看您的电子邮件客户端,但仅将实际打开和读取的邮件标记为SEEN
。
Spring Integration 2.0.4在inbound-channel-adapter
和imap-idle-channel-adapter
上引入了mail-filter-expression
属性。此属性允许您提供一个表达式,该表达式是SpEL和正则表达式的组合。例如,如果您只想读取主题行中包含“Spring Integration”的电子邮件,则应如下配置mail-filter-expression
属性:mail-filter-expression="subject matches '(?i).**Spring Integration.**"
。
由于jakarta.mail.internet.MimeMessage
是SpEL评估上下文的根上下文,因此您可以根据MimeMessage
中可用的任何值进行过滤,包括邮件的实际正文。这一点尤其重要,因为读取邮件正文通常会导致这些邮件默认被标记为SEEN
。但是,由于我们现在将每个传入邮件的PEEK
标志设置为“true”,因此只有明确标记为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”的邮件才会由此适配器生成。
另一个合理的问题是,在下次轮询或空闲事件发生时,或者当此类适配器重新启动时会发生什么。是否有可能重复过滤邮件?换句话说,如果在上次检索中您有五封新邮件,只有一封通过了过滤器,那么其他四封邮件会怎样?它们会在下次轮询或空闲时再次进行过滤逻辑吗?毕竟,它们没有被标记为SEEN
。答案是否定的。由于电子邮件服务器设置的另一个标志(RECENT
),它们不会受到重复处理,该标志被Spring Integration邮件搜索过滤器使用。文件夹实现设置此标志以指示此邮件对于此文件夹来说是新的。也就是说,它是在上次打开此文件夹之后到达的。换句话说,虽然我们的适配器可能会查看电子邮件,但它也会让电子邮件服务器知道此电子邮件已被访问,因此应该由电子邮件服务器标记为RECENT
。
事务同步
入站适配器的交易同步允许您在交易提交或回滚后采取不同的操作。您可以通过向轮询的<inbound-adapter/>
或<imap-idle-inbound-adapter/>
的轮询器添加<transactional/>
元素来启用事务同步。即使没有涉及“实际”事务,您仍然可以使用<transactional/>
元素中的PseudoTransactionManager
启用此功能。有关更多信息,请参阅事务同步。
由于不同的邮件服务器以及某些邮件服务器的限制,目前我们只提供这些事务同步的策略。您可以将邮件发送到其他一些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 IntegrationFlow
.from(Mail.imapInboundAdapter("imap://user:pw@host: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 IntegrationFlow.from("sendMailChannel")
.enrichHeaders(Mail.headers()
.subjectFunction(m -> "foo")
.from("foo@bar")
.toFunction(m -> new String[] { "bar@baz" }))
.handle(Mail.outboundAdapter("gmail")
.port(smtpServer.getPort())
.credentials("user", "pw")
.protocol("smtp"),
e -> e.id("sendMailEndpoint"))
.get();
}
}