Spring Email(邮件)发送指南

1、概览

本文将带你了解如何通过标准 Spring 应用及 Spring Boot 应用发送邮件。前者基于 JavaMail 库实现,后者则使用 spring-boot-starter-mail 依赖。

2、Maven 依赖

首先需在 pom.xml 中添加依赖。

2.1、Spring

以下是标准 Spring 框架所需的依赖配置:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>6.1.5</version>
</dependency>

最新版本可在 此处 获取。

2.2、Spring Boot

而 Spring Boot 则需要添加:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
    <version>3.1.5</version>
</dependency>

最新版本可在 Maven 中央仓库 获取。

3、邮件服务器配置属性

Spring 框架中邮件支持的接口与类按以下结构组织:

  1. MailSender 接口:顶层接口,提供发送简单邮件的基础功能。
  2. JavaMailSender 接口:MailSender 的子接口,支持 MIME 消息,通常与。MimeMessageHelper 类配合创建 MimeMessage。建议通过 MimeMessagePreparator 机制使用该接口。
  3. JavaMailSenderImpl 类:实现 JavaMailSender 接口,支持 MimeMessageSimpleMailMessage
  4. SimpleMailMessage 类:用于创建包含发件人、收件人、抄送、主题和文本内容的简单邮件。
  5. MimeMessagePreparator 接口:为 MIME 消息提供准备机制的回调接口。
  6. MimeMessageHelper 类:创建 MIME 消息的辅助类,支持图片、典型邮件附件及 HTML 格式的文本内容。

以下章节将演示如何使用这些接口与类。

3.1、Spring 邮件服务器配置属性

指定 SMTP 服务器等必需的邮件属性可通过 JavaMailSenderImpl 进行定义。

Gmail 为例,配置如下:

@Bean
public JavaMailSender getJavaMailSender() {
    JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
    mailSender.setHost("smtp.gmail.com");
    mailSender.setPort(587);
    
    mailSender.setUsername("my.gmail@gmail.com");
    mailSender.setPassword("password");
    
    Properties props = mailSender.getJavaMailProperties();
    props.put("mail.transport.protocol", "smtp");
    props.put("mail.smtp.auth", "true");
    props.put("mail.smtp.starttls.enable", "true");
    props.put("mail.debug", "true");
    
    return mailSender;
}

3.2、Spring Boot 邮件服务器配置属性

添加依赖后,下一步是在 application.properties 文件中使用 spring.mail.* 命名空间配置邮件服务器属性。

Gmail SMTP 服务器的配置示例如下:

spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=<login user to smtp server>
spring.mail.password=<login password to smtp server>
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true

部分 SMTP 服务器要求 TLS 加密连接,此时需通过 spring.mail.properties.mail.smtp.starttls.enable 属性启用 TLS 保护。

3.2.1、Gmail SMTP 配置属性

可通过 Gmail SMTP 服务器发送邮件,具体出站邮件服务器属性请参阅 文档

我们的 application.properties 文件已配置为使用 Gmail SMTP(参见前文配置)。

注意:账户密码需使用为 Google 账户生成的专用应用密码,而非普通密码。详情及密码生成方法请参考 此链接

3.2.2、SES SMTP 配置属性

使用 Amazon SES 发送邮件时,需按以下方式配置 application.properties

spring.mail.host=email-smtp.us-west-2.amazonaws.com
spring.mail.username=username
spring.mail.password=password
spring.mail.properties.mail.transport.protocol=smtp
spring.mail.properties.mail.smtp.port=25
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true

注意:Amazon 要求在使用凭证前完成验证。请访问 此链接 验证你的用户名和密码。

4、发送邮件

完成依赖管理与配置后,即可使用前述的 JavaMailSender 发送邮件。

由于标准 Spring 框架与 Spring Boot 版本处理邮件构建和发送的方式相似,下文将不再区分二者。

4.1、发送简单邮件

首先演示如何构建并发送不含附件的简单邮件:

@Component
public class EmailServiceImpl implements EmailService {

    @Autowired
    private JavaMailSender emailSender;

    public void sendSimpleMessage(
      String to, String subject, String text) {
        ...
        SimpleMailMessage message = new SimpleMailMessage(); 
        message.setFrom("noreply@baeldung.com");
        message.setTo(to); 
        message.setSubject(subject); 
        message.setText(text);
        emailSender.send(message);
        ...
    }
}

需注意:虽然 from 地址非必填项,但多数 SMTP 服务器会拒收此类邮件。因此我们在 EmailService 实现中使用了 noreply@baeldung.com 作为发件地址。

4.2、发送带有附件的邮件

有时 Spring 的简单邮件功能无法满足需求。

例如发送带发票附件订单确认邮件时,需使用 JavaMail 库的 MIME Multipart 消息而非 SimpleMailMessage

Spring 通过 org.springframework.mail.javamail.MimeMessageHelper 类支持 JavaMail 消息处理。

首先在 EmailServiceImpl 中添加发送附件邮件的方法:

@Override
public void sendMessageWithAttachment(
  String to, String subject, String text, String pathToAttachment) {
    // ...
    
    MimeMessage message = emailSender.createMimeMessage();
     
    MimeMessageHelper helper = new MimeMessageHelper(message, true);
    
    helper.setFrom("noreply@baeldung.com");
    helper.setTo(to);
    helper.setSubject(subject);
    helper.setText(text);
    
    // 添加来自于文件系统中的附件
    FileSystemResource file 
      = new FileSystemResource(new File(pathToAttachment));
    helper.addAttachment("Invoice", file);

    emailSender.send(message);
    // ...
}

4.3、简单的邮件模板

SimpleMailMessage 类与字符串格式化配合良好。

可通过在配置类中定义模板 Bean 来创建邮件模板:

@Bean
public SimpleMailMessage templateSimpleMessage() {
    SimpleMailMessage message = new SimpleMailMessage();
    message.setText(
      "This is the test email template for your email:\n%s\n");
    return message;
}

现在可将此 Bean 作为邮件模板使用,只需向模板提供必要参数:

@Autowired
public SimpleMailMessage template;
...
String text = String.format(template.getText(), templateArgs);  
sendSimpleMessage(to, subject, text);

4.4、通过 InputStream 发送邮件附件

当需要发送动态生成或通过 InputStream 获取的附件时,可利用 Spring 框架的 MimeMessageHelper 类实现。

以下是在 EmailServiceImpl 类中添加通过 InputStream 发送附件的方法:

public void sendMessageWithInputStreamAttachment(
  String to, String subject, String text, String attachmentName, InputStream attachmentStream) {
    try {
        MimeMessage message = emailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true);

        helper.setFrom("noreply@baeldung.com");
        helper.setTo(to);
        helper.setSubject(subject);
        helper.setText(text);

        // 从 InputStream 添加附件
        helper.addAttachment(attachmentName, new InputStreamResource(attachmentStream));

        emailSender.send(message);
    } catch (MessagingException e) {
        e.printStackTrace();
    }
}

该方法首先通过 JavaMailSender 创建 MimeMessage 实例,随后初始化 MimeMessageHelper 以支持包含附件所需的 Multipart 消息结构。

调用 addAttachment() 时,使用 InputStreamResource 封装传入的 InputStream,从而支持附加非文件形式的动态数据。

现在可调用此方法:

InputStream attachmentStream = new ByteArrayInputStream("Hello World".getBytes(StandardCharsets.UTF_8));
emailService.sendMessageWithInputStreamAttachment(
  "recipient@example.com", 
  "Subject Here", 
  "Body of the email", 
  "attachment.txt", 
  attachmentStream);

本示例将字符串转换为 InputStream 后作为名为 "attachment.txt" 的附件发送。此方法无需预先将内容保存为磁盘文件,即可灵活发送各类附件。

5、处理发送异常

JavaMail 提供 SendFailedException 处理邮件发送失败的情况,但发送至错误地址时可能不会触发此异常。原因如下:

RFC 821 的 SMTP 协议规范要求服务器在发送至错误地址时返回 550 状态码,但多数公共 SMTP 服务器并未遵守 - 它们可能发送 “delivery failed”(投递失败)回执邮件或完全不提供反馈。

例如,Gmail SMTP 服务器会发送 “delivery failed” 回执邮件,但程序不会抛出异常。

针对此情况,可以有以下处理方案:

  1. 捕获可能永远不会抛出的 SendFailedException
  2. 在限定时间内检查发件箱中的 “delivery failed” 回执(此方式不直观且时间周期不确定)。
  3. 若邮件服务器无任何反馈,则没任何办法了。

6、总结

本文介绍了如何在 Spring 和 Spring Boot 应用中发送邮件。


Ref:https://www.baeldung.com/spring-email