如何在 Spring 集成中使用邮件消息
本节介绍如何在 Spring 集成中使用邮件消息。
您需要将此依赖项包含在项目中:
必须通过特定于供应商的实现来包含这些内容。jakarta.mail:jakarta.mail-api
邮件发送通道适配器
Spring 集成为带有 . 它委托给 Spring 的配置实例,如以下示例所示:MailSendingMessageHandler
JavaMailSender
MailSendingMessageHandler
具有使用Spring抽象的各种映射策略。 如果收到的消息的有效负载已经是实例,则会直接发送。 因此,我们通常建议您在此消费者之前使用变压器,以满足非平凡的结构要求。 但是,Spring 集成支持一些简单的消息映射策略。 例如,如果邮件有效负载是映射到附件的字节数组。 对于简单的基于文本的电子邮件,您可以提供基于字符串的消息有效负载。 在这种情况下,将创建 a 作为文本内容。 如果您使用的消息有效负载类型,其方法返回适当的邮件文本内容,请考虑在出站邮件适配器之前添加 Spring 集成(有关更多详细信息,请参阅使用 XML 配置转换器中的示例)。MailMessage
MailMessage
MailMessage
MailMessage
String
toString()
ObjectToStringTransformer
您还可以使用中的某些值配置出站。 如果可用,值将映射到出站邮件的属性,例如收件人(收件人、抄送和密件抄送)、、 和 . 标头名称由以下常量定义:MailMessage
MessageHeaders
from
reply-to
subject
|
邮件接收通道适配器
Spring 集成还为入站电子邮件提供了对 . 它委托给 Spring 集成自己的接口的配置实例。 有两种实现:和 。 实例化其中任何一个的最简单方法是绕过邮件存储的“uri”到接收方的构造函数,如以下示例所示:MailReceivingMessageSource
MailReceiver
Pop3MailReceiver
ImapMailReceiver
接收邮件的另一个选项是 IMAP 命令(如果邮件服务器支持)。 Spring Integration 提供了 ,它本身就是一个生成消息的端点。 它委托给 的实例,但启用邮件消息的异步接收。 下一节提供了在“邮件”模式中使用 Spring 集成的命名空间支持配置这两种类型的入站通道适配器的示例。idle
ImapIdleChannelAdapter
ImapMailReceiver
通常,调用该方法时,会呈现某些标头以及正文(对于简单的文本电子邮件),如以下示例所示:
|
使用简单的 ,返回邮件正文(在前面的示例中)。MimeMessage
getContent()
something
从版本 2.2 开始,框架急切地获取 IMAP 消息,并将它们公开为 . 这产生了改变行为的不良副作用。 版本 4.3 中引入的邮件映射增强功能进一步加剧了这种不一致,因为当提供标头映射器时,有效负载由该方法呈现。 这意味着 IMAP 内容会有所不同,具体取决于是否提供了标头映射器。MimeMessage
getContent()
IMAPMessage.getContent()
从版本 5.0 开始,源自 IMAP 源的消息会根据行为呈现内容,而不管是否提供了标头映射器。 如果不使用标头映射器,并且希望恢复到以前仅呈现正文的行为,请将邮件接收方上的布尔属性设置为 。 此属性现在控制呈现,而不考虑是否使用标头映射器。 它现在允许在提供标头映射器时仅呈现正文。IMAPMessage.getContent()
simpleContent
true
从版本 5.2 开始,邮件接收器上提供了该选项。 将其设置为 不会在提取后自动关闭文件夹,而是将标头(有关详细信息,请参阅 MessageHeaderAccessor API)填充到从通道适配器发送给生产者的每条消息中。 这不起作用,因为它依赖于打开和关闭文件夹来获取新邮件。 目标应用程序负责在下游流中在需要时调用此标头上的 :autoCloseFolder
false
IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE
Pop3MailReceiver
close()
在分析带有附件的电子邮件的多部分内容期间需要与服务器通信的情况下,保持文件夹打开非常有用。 在标头上委派给 关闭文件夹 选项(如果分别在 上配置)。close()
IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE
AbstractMailReceiver
expunge
shouldDeleteMessages
AbstractMailReceiver
从版本 5.4 开始,现在可以按原样返回 a,而无需任何转换或预先加载内容。 此功能通过以下选项组合启用:未提供、属性为 和属性为 。 作为生成的 Spring 消息的有效负载存在。 在这种情况下,填充的唯一标头是上面提到的文件夹,该文件夹在处理完成时必须关闭。MimeMessage
headerMapper
simpleContent
false
autoCloseFolder
false
MimeMessage
IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE
MimeMessage
从版本 5.5.11 开始,如果未收到任何邮件或所有邮件都独立于标志过滤掉,则文件夹将自动关闭。 在这种情况下,没有任何东西可以产生下游的可能逻辑围绕标头。AbstractMailReceiver.receive()
autoCloseFolder
IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE
入站邮件消息映射
默认情况下,入站适配器生成的消息的有效负载为 raw 。 您可以使用该对象查询标头和内容。 从版本 4.3 开始,您可以提供 以将标头映射到 。 为了方便起见,Spring Integration为此提供了一个。 它映射以下标头:MimeMessage
HeaderMapper
MessageHeaders
DefaultMailHeaderMapper
-
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
:包含所有邮件头及其值的 A。MultiValueMap
-
mail_contentType
:原始邮件的内容类型。 -
contentType
:有效负载内容类型(见下文)。
启用消息映射后,有效负载取决于邮件消息及其实现。 电子邮件内容通常由 中的 .DataHandler
MimeMessage
对于电子邮件,有效负载为 a,标头与 相同。text/*
String
contentType
mail_contentType
对于具有嵌入式实例的消息,通常会呈现一个对象。 这些对象不是,也不适合使用替代技术(如 )进行序列化。 因此,默认情况下,启用映射后,此类有效负载将呈现为包含数据的原始负载。 的示例为 和 。 在这种情况下,标头。 要更改此行为并接收对象负载,请设置为 on 。 对于 未知的内容类型,内容呈现为 标头为 。jakarta.mail.Part
DataHandler
Part
Serializable
Kryo
byte[]
Part
Part
Message
Multipart
contentType
application/octet-stream
Multipart
embeddedPartsAsBytes
false
MailReceiver
DataHandler
byte[]
contentType
application/octet-stream
如果未提供标头映射器,则消息负载由 提供。 该框架提供了一个,您可以使用该策略将邮件内容转换为:MimeMessage
jakarta.mail
MailToStringTransformer
String
从版本 4.3 开始,转换器处理嵌入式实例(以及以前处理的实例)。 转换器是映射上述列表中的地址和主题标头的子类。 如果要对消息执行其他转换,请考虑子类化。Part
Multipart
AbstractMailTransformer
AbstractMailTransformer
从版本 5.4 开始,当提供 no 时,is 和 is ,则在生成的 Spring 消息的有效负载中按原样返回。 这样,在稍后的流中引用时,将按需加载 的内容。 上述所有转换仍然有效。headerMapper
autoCloseFolder
false
simpleContent
false
MimeMessage
MimeMessage
邮件命名空间支持
Spring 集成为与邮件相关的配置提供了一个命名空间。 若要使用它,请配置以下架构位置:
若要配置出站通道适配器,请提供要从中接收的通道和 MailSender,如以下示例所示:
或者,您可以提供主机、用户名和密码,如以下示例所示:
从版本 5.1.3 开始,如果提供了 ,则可以省略 、ane。 但是,必须使用适当的 Java 邮件属性配置 和,例如对于 SMTP:host
username
mail-sender
java-mail-properties
host
username
与任何出站通道适配器一样,如果引用的通道是 ,则应提供一个元素(请参阅终结点命名空间支持)。 |
使用命名空间支持时,还可以使用消息转换器。 这样做可以简化前面提到的标头在发送到邮件出站通道适配器之前对任何邮件的应用。header-enricher
以下示例假定有效负载是一个 Java Bean,其中包含指定属性的适当 getter,但您可以使用任何 SpEL 表达式:
或者,可以使用该属性指定文本。 您还可以指定和单个属性来控制现有标头的行为。value
default-overwrite
overwrite
要配置入站通道适配器,您可以在轮询或事件驱动之间进行选择(假设您的邮件服务器支持 IMAP — 如果没有,则轮询是唯一的选择)。 轮询通道适配器需要存储 URI 和要将入站消息发送到的通道。 URI 可能以 或 开头。 以下示例使用 URI:idle
pop3
imap
imap
如果您确实支持 IMAP,则可能需要改为配置该元素。 由于该命令启用事件驱动的通知,因此此适配器不需要轮询器。 它在收到新邮件可用的通知后立即向指定通道发送消息。 以下示例配置 IMAP 邮件通道:idle
imap-idle-channel-adapter
idle
idle
您可以通过创建和填充常规对象来提供 - 例如,通过使用 Spring 提供的命名空间。javaMailProperties
java.utils.Properties
util
如果您的用户名包含“@”字符,请使用“%40”而不是“@”以避免从底层 JavaMail API 解析错误。 |
以下示例演示如何配置对象:java.util.Properties
默认情况下,根据默认值搜索邮件,该默认值是符合以下条件的所有邮件:ImapMailReceiver
SearchTerm
- 是最近的(如果支持)
- 未回答
- 未删除
- 未看到
- h尚未被此邮件收件人处理(通过使用自定义用户标志启用,或者如果不支持,则干脆不标记)
自定义用户标志为 ,但您可以对其进行配置。 从版本 2.2 开始,由 使用的 完全可配置为 ,您可以使用该属性注入该属性。 A 是具有单个方法的策略接口,允许您创建 使用的实例。 以下清单显示了该接口:spring-integration-mail-adapter
SearchTerm
ImapMailReceiver
SearchTermStrategy
search-term-strategy
SearchTermStrategy
SearchTerm
ImapMailReceiver
SearchTermStrategy
以下示例依赖于而不是默认值:TestSearchTermStrategy
SearchTermStrategy
有关邮件标记的信息,请参阅在不支持\最近时标记 IMAP 邮件。
重要:IMAP 聚醚醚 从版本 4.1.1 开始,IMAP 邮件接收器使用 或 JavaMail 属性(如果指定)。 以前,接收器忽略该属性并始终设置标志。 现在,如果将此属性显式设置为 ,则无论 的设置如何,消息 ise 都会标记为 。 如果未指定,则保留以前的行为(速览为 )。
|
IMAP 和丢失的连接idle
使用 IMAP 通道适配器时,与服务器的连接可能会丢失(例如,由于网络故障),并且由于 JavaMail 文档明确指出实际的 IMAP API 是实验性的,因此了解 API 中的差异以及在配置 IMAP 适配器时如何处理这些差异非常重要。 目前,Spring Integration 邮件适配器已使用 JavaMail 1.4.1 和 JavaMail 1.4.3 进行了测试。 根据使用哪一个,您必须特别注意一些需要设置的有关自动重新连接的 JavaMail 属性。idle
idle
在 Gmail 中观察到以下行为,但应该为您提供一些有关如何解决与其他提供商重新连接问题的提示。 但是,始终欢迎反馈。 同样,以下注释基于 Gmail。 |
在 JavaMail 1.4.1 中,如果将属性设置为相对较短的时间(在我们的测试中约为 5 分钟),则会在此超时之后引发。 但是,如果未设置此属性(它应该是无限期的),则该方法永远不会返回,并且永远不会引发异常。 但是,如果连接在短时间内丢失(在我们的测试中不到 10 分钟),它会自动重新连接。 但是,如果连接丢失了很长时间(超过 10 分钟),则 不会引发,也不会重新建立连接,并且无限期地保持阻塞状态,因此如果不重新启动适配器,就无法重新连接。 因此,使用 JavaMail 1.4.1 进行重新连接的唯一方法是将属性显式设置为某个值,但这也意味着该值应该相对较短(少于 10 分钟),并且应该相对较快地重新建立连接。 同样,它可能与Gmail以外的提供商不同。 在 JavaMail 1.4.3 中,对 API 进行了重大改进,确保始终存在强制方法返回或仅返回的条件,从而允许您继续进行自动重新连接。 目前,自动重新连接无限运行,每十秒尝试重新连接一次。mail.imaps.timeout
IMAPFolder.idle()
FolderClosedException
IMAPFolder.idle()
IMAPFolder.idle()
FolderClosedException
mail.imaps.timeout
IMAPFolder.idle()
StoreClosedException
FolderClosedException
在这两种配置中,并且都是必需属性。 您应该了解为什么需要。 问题出在 POP3 协议上,该协议对已读取的邮件一无所知。 它只能知道在单个会话中读取了什么。 这意味着,当您的 POP3 邮件适配器运行时,电子邮件将成功使用,因为它们在每次轮询期间都可用,并且不会多次传递任何电子邮件。 但是,一旦重新启动适配器并开始新会话,将再次检索在上一个会话中可能已检索的所有电子邮件。 这就是持久性有机污染物3的本质。 有些人可能会争辩说,这应该是默认的。 换句话说,有两种有效且相互排斥的用途使得很难选择单个最佳默认值。 您可能希望将适配器配置为唯一的电子邮件收件人,在这种情况下,您希望能够重新启动适配器,而不必担心以前传递的消息不会再次传递。 在这种情况下,设置为 将最有意义。 但是,您可能有另一个用例,您可能希望让多个适配器监视电子邮件服务器及其内容。 换句话说,你想“偷看但不触摸”。 然后设置为 更合适。 因此,由于很难选择属性的正确默认值,因此我们将其设置为必需属性,由您设置。 把它留给你也意味着你不太可能最终出现意外行为。 |
配置轮询电子邮件适配器的属性时,应注意要配置的用于检索邮件的协议。 例如,POP3 不支持此标志,这意味着将其设置为任一值都不起作用,因为邮件未标记为已读。 |
在连接静默断开的情况下,会定期在后台运行空闲取消任务(通常会立即处理新的 IDLE)。 为了控制此间隔,提供了一个选项;默认值 120(2 分钟)。 RFC 2177 建议间隔不超过 29 分钟。cancelIdleInterval
您应该了解,这些操作(将消息标记为已读和删除消息)是在收到消息之后但在处理消息之前执行的。 这可能会导致消息丢失。
您可能希望考虑改用事务同步。 请参阅事务同步。
|
还接受“错误通道”属性。 如果引发下游异常并指定了“错误通道”,则会向此通道发送包含失败消息和原始异常的消息。 否则,如果下游通道是同步的,则通道适配器会将任何此类异常记录为警告。
MessagingException
|
从 3.0 版本开始,IMAP 适配器在发生异常时发出应用程序事件(特别是实例)。 这允许应用程序检测并处理这些异常。 可以使用配置为接收一个或其中一个超类来获取事件。 |
在不支持时标记 IMAP 邮件\Recent
如果为 true,则 IMAP 适配器设置标志。shouldMarkMessagesAsRead
\Seen
此外,当电子邮件服务器不支持该标志时,只要服务器支持用户标志,IMAP 适配器就会使用用户标志(默认情况下为 )标记邮件。 如果不是,则设置为 。 无论设置如何,都会应用这些标志。\Recent
spring-integration-mail-adapter
Flag.FLAGGED
true
shouldMarkMessagesRead
如 [搜索词] 中所述,默认会忽略如此标记的邮件。SearchTermStrategy
从版本 4.2.2 开始,您可以通过在 上使用 来设置用户标志的名称。 这样做允许多个收件人使用不同的标志(只要邮件服务器支持用户标志)。 使用命名空间配置适配器时,该属性可用。setUserFlag
MailReceiver
user-flag
电子邮件筛选
很多时候,您可能会遇到过滤传入邮件的要求(例如,您只想阅读行中包含“Spring 集成”的电子邮件)。 您可以通过将入站邮件适配器与基于表达式的 . 虽然它会起作用,但这种方法有一个缺点。 由于邮件在通过入站邮件适配器后将被过滤,因此所有此类邮件都将标记为已读 () 或未读(取决于属性的值)。 但是,实际上,将邮件标记为仅当邮件通过筛选条件时会更有用。 这类似于在滚动浏览预览窗格中的所有邮件时查看电子邮件客户端,但只标记实际打开并读取为 .Subject
Filter
SEEN
should-mark-messages-as-read
SEEN
SEEN
Spring Integration 2.0.4 引入了 on 和 的属性。 此属性允许您提供由 SpEL 和正则表达式组合的表达式。 例如,如果您只想阅读主题行中包含“Spring 集成”的电子邮件,则可以按如下方式配置属性:。mail-filter-expression
inbound-channel-adapter
imap-idle-channel-adapter
mail-filter-expression
mail-filter-expression="subject matches '(?i).Spring Integration."
由于是 SpEL 评估上下文的根上下文,因此可以通过 筛选任何可用的值,包括消息的实际正文。 这一点尤其重要,因为读取消息正文通常会导致此类消息被标记为默认。 但是,由于我们现在将每个传入消息的标志设置为“true”,因此只有明确标记为已读的消息才会被标记为已读。jakarta.mail.internet.MimeMessage
MimeMessage
SEEN
PEEK
SEEN
因此,在以下示例中,此适配器仅输出与筛选器表达式匹配的消息,并且只有这些消息标记为已读:
在前面的示例中,由于该属性,此适配器仅生成主题行中包含“Spring 集成”的消息。mail-filter-expression
另一个合理的问题是在下一次轮询或空闲事件上会发生什么,或者重新启动此类适配器时会发生什么。 可以过滤重复的按摩吗?换句话说,如果在最后一次检索时,您有五条新邮件,只有一条通过了过滤器,那么其他四封邮件会发生什么? 他们会在下一次轮询或空闲时再次通过过滤逻辑吗? 毕竟,它们没有被标记为. 答案是否定的。 由于电子邮件服务器设置并由 Spring 集成邮件搜索过滤器使用的另一个标志 (),它们不会受到重复处理。 文件夹实现设置此标志以指示此邮件是此文件夹的新邮件。 也就是说,自上次打开此文件夹以来,它已到达。 换句话说,虽然我们的适配器可能会偷看电子邮件,但它也会让电子邮件服务器知道此类电子邮件已被触摸,因此应由电子邮件服务器标记为。SEEN
RECENT
RECENT
事务同步
入站适配器的事务同步允许您在事务提交或回滚后执行不同的操作。 您可以通过向轮询器或 . 即使不涉及“真实”事务,您仍然可以通过使用 with 元素来启用此功能。 有关详细信息,请参阅事务同步。
PseudoTransactionManager
由于邮件服务器不同,特别是某些邮件服务器的限制,目前我们仅提供这些事务同步的策略。 您可以将消息发送到其他一些 Spring 集成组件,或者调用自定义 Bean 来执行某些操作。 例如,若要在事务提交后将 IMAP 邮件移动到其他文件夹,可以使用类似于以下内容的内容:
下面的示例演示类的外观:Mover
为了使消息在事务后仍可用于操作,必须将 should-delete-messages 设置为 'false'。 |
使用 Java DSL 配置通道适配器
To configure mail component in Java DSL, the framework provides a factory, which can be used like this:o.s.i.mail.dsl.Mail