多樣化郵件功能實戰指南,你學會了嗎?
                    前言
在當今數位化的時代,郵件作為一種重要的通訊方式,廣泛應用於各類系統。無論是系統通知、使用者交互,還是檔案傳輸等場景,郵件都發揮著不可或缺的作用。
本文將探討如何實現文字、附件、HTML、圖片類型郵件的發送,並在此基礎上增加一些實用功能,如大量發送郵件、動態郵件模板渲染等,助力開發者打造更強大的郵件服務。
實現
依賴引入
<dependencies>
    <!-- Spring Boot Web支持,用于后续可能的Web接口开发 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Boot邮件启动器 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    <!-- JavaMail API -->
    <dependency>
        <groupId>com.sun.mail</groupId>
        <artifactId>javax.mail</artifactId>
    </dependency>
    <!-- Thymeleaf模板引擎,用于邮件模板渲染 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <!-- 测试依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- Lombok简化代码编写 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>- 1.
 - 2.
 - 3.
 - 4.
 - 5.
 - 6.
 - 7.
 - 8.
 - 9.
 - 10.
 - 11.
 - 12.
 - 13.
 - 14.
 - 15.
 - 16.
 - 17.
 - 18.
 - 19.
 - 20.
 - 21.
 - 22.
 - 23.
 - 24.
 - 25.
 - 26.
 - 27.
 - 28.
 - 29.
 - 30.
 - 31.
 - 32.
 - 33.
 - 34.
 
配置資訊
spring:
  mail:
    host: smtp.163.com
    port: 465
    username: your_email@163.com
    password: your_password
    properties:
      mail:
        debug: true
        smtp:
          auth: true
          starttls.enable: true
          socketFactoryClass: javax.net.ssl.SSLSocketFactory
    default-encoding: UTF-8
    protocol: smtps
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    cache: false
from:
  mail:
    address: your_email@163.com- 1.
 - 2.
 - 3.
 - 4.
 - 5.
 - 6.
 - 7.
 - 8.
 - 9.
 - 10.
 - 11.
 - 12.
 - 13.
 - 14.
 - 15.
 - 16.
 - 17.
 - 18.
 - 19.
 - 20.
 - 21.
 - 22.
 - 23.
 - 24.
 
請將your_email@163.com替換為實際的郵件地址,your_password替換為郵箱的授權碼(非登入密碼)。若使用其他郵件伺服器,需相應修改spring.mail.host等設定。
核心程式碼
public interface MailService {
    void sendSimpleMail(String to, String subject, String content);
    void sendHtmlMail(String to, String subject, String content);
    void sendAttachmentsMail(String to, String subject, String content, String filePath);
    void sendInlineResourceMail(String to, String subject, String content, String rscPath, String rscId);
    void sendBatchSimpleMail(String[] tos, String subject, String content);
    void sendDynamicTemplateMail(String to, String subject, String templateName, Object model);
}- 1.
 - 2.
 - 3.
 - 4.
 - 5.
 - 6.
 - 7.
 - 8.
 
實作類別
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.yian.service.MailService;
import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@Service
@Slf4j
public class MailServiceImpl implements MailService {
    @Resource
    private JavaMailSender mailSender;
    @Resource
    private TemplateEngine templateEngine;
    @Value("${from.mail.address}")
    private String from;
    @Override
    public void sendSimpleMail(String to, String subject, String content) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom(from);
        message.setTo(to);
        message.setSubject(subject);
        message.setText(content);
        try {
            mailSender.send(message);
            log.info("文本邮件已经发送");
        } catch (Exception e) {
            log.error("发生发送文本邮件错误!", e);
        }
    }
    @Override
    public void sendHtmlMail(String to, String subject, String content) {
        MimeMessage message = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);
            mailSender.send(message);
            log.info("html邮件发送成功");
        } catch (MessagingException e) {
            log.error("发生发送html邮件错误!", e);
        }
    }
    @Override
    public void sendAttachmentsMail(String to, String subject, String content, String filePath) {
        MimeMessage message = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);
            FileSystemResource file = new FileSystemResource(new File(filePath));
            String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
            helper.addAttachment(fileName, file);
            mailSender.send(message);
            log.info("带附件的邮件已经发送");
        } catch (MessagingException e) {
            log.error("发生发送带附件邮件错误!", e);
        }
    }
    @Override
    public void sendInlineResourceMail(String to, String subject, String content, String rscPath, String rscId) {
        MimeMessage message = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);
            FileSystemResource res = new FileSystemResource(new File(rscPath));
            helper.addInline(rscId, res);
            mailSender.send(message);
            log.info("嵌入静态图片的邮件已经发送");
        } catch (MessagingException e) {
            log.error("发生发送嵌入静态图片邮件错误!", e);
        }
    }
    @Override
    public void sendBatchSimpleMail(String[] tos, String subject, String content) {
        for (String to : tos) {
            sendSimpleMail(to, subject, content);
        }
        log.info("批量文本邮件已发送完成");
    }
    @Override
    public void sendDynamicTemplateMail(String to, String subject, String templateName, Object model) {
        Context context = new Context();
        if (model instanceof Map) {
            context.setVariables((Map<String, Object>) model);
        } elseif (model != null) {
            Map<String, Object> map = new HashMap<>();
            Field[] fields = model.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                try {
                    map.put(field.getName(), field.get(model));
                } catch (IllegalAccessException e) {
                    log.error("转换对象为Map时出错", e);
                }
            }
            context.setVariables(map);
        }
        String emailContent = templateEngine.process(templateName, context);
        sendHtmlMail(to, subject, emailContent);
        log.info("动态模板邮件已发送");
    }
}- 1.
 - 2.
 - 3.
 - 4.
 - 5.
 - 6.
 - 7.
 - 8.
 - 9.
 - 10.
 - 11.
 - 12.
 - 13.
 - 14.
 - 15.
 - 16.
 - 17.
 - 18.
 - 19.
 - 20.
 - 21.
 - 22.
 - 23.
 - 24.
 - 25.
 - 26.
 - 27.
 - 28.
 - 29.
 - 30.
 - 31.
 - 32.
 - 33.
 - 34.
 - 35.
 - 36.
 - 37.
 - 38.
 - 39.
 - 40.
 - 41.
 - 42.
 - 43.
 - 44.
 - 45.
 - 46.
 - 47.
 - 48.
 - 49.
 - 50.
 - 51.
 - 52.
 - 53.
 - 54.
 - 55.
 - 56.
 - 57.
 - 58.
 - 59.
 - 60.
 - 61.
 - 62.
 - 63.
 - 64.
 - 65.
 - 66.
 - 67.
 - 68.
 - 69.
 - 70.
 - 71.
 - 72.
 - 73.
 - 74.
 - 75.
 - 76.
 - 77.
 - 78.
 - 79.
 - 80.
 - 81.
 - 82.
 - 83.
 - 84.
 - 85.
 - 86.
 - 87.
 - 88.
 - 89.
 - 90.
 - 91.
 - 92.
 - 93.
 - 94.
 - 95.
 - 96.
 - 97.
 - 98.
 - 99.
 - 100.
 - 101.
 - 102.
 - 103.
 - 104.
 - 105.
 - 106.
 - 107.
 - 108.
 - 109.
 - 110.
 - 111.
 - 112.
 - 113.
 - 114.
 - 115.
 - 116.
 - 117.
 - 118.
 - 119.
 - 120.
 - 121.
 - 122.
 - 123.
 - 124.
 - 125.
 - 126.
 - 127.
 - 128.
 - 129.
 
在 src/main/resources/templates 目錄下建立 userInfoTemplate.html 檔案(Thymeleaf 預設會從該目錄載入範本),範例內容如下:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>用户信息模板</title>
</head>
<body>
    <h1>用户信息</h1>
    <p>姓名:<span th:text="${name}"></span></p>
    <p>年龄:<span th:text="${age}"></span></p>
</body>
</html>- 1.
 - 2.
 - 3.
 - 4.
 - 5.
 - 6.
 - 7.
 - 8.
 - 9.
 - 10.
 - 11.
 - 12.
 
單元測試
import cn.example.mail.service.MailService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MailBootTest {
    @Autowired
    private MailService mailService;
    @Test
    public void testSimpleMail() {
        mailService.sendSimpleMail("test@example.com", "测试简单文本邮件", "这是一封简单的文本邮件");
    }
    @Test
    public void testHtmlMail() {
        String content = "<html><body><h2>hello! 这是一封html邮件!</h2></body></html>";
        mailService.sendHtmlMail("test@example.com", "这是html邮件", content);
    }
    @Test
    public void sendAttachmentsMail() {
        String filePath = "C:\\example\\attachment.pdf";
        mailService.sendAttachmentsMail("test@example.com", "主题:带附件的邮件", "有附件,请查收!", filePath);
    }
    @Test
    public void sendInlineResourceMail() {
        String rscId = "exampleImage";
        String content = "<html><body>这是有图片的邮件:<img src='cid:" + rscId + "'></body></html>";
        String imgPath = "C:\\example\\image.jpg";
        mailService.sendInlineResourceMail("test@example.com", "主题:这是有图片的邮件", content, imgPath, rscId);
    }
    @Test
    public void sendBatchSimpleMail() {
        String[] tos = {"test1@example.com", "test2@example.com"};
        mailService.sendBatchSimpleMail(tos, "批量测试邮件", "这是批量发送的文本邮件");
    }
    @Test
    public void sendDynamicTemplateMail() {
        User user = new User("一安", 25);
        mailService.sendDynamicTemplateMail("test@example.com", "动态模板邮件测试", "userInfoTemplate", user);
    }
    // 测试用的用户类
    private static class User {
        private String name;
        private int age;
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
        // 省略getter和setter方法
    }
}- 1.
 - 2.
 - 3.
 - 4.
 - 5.
 - 6.
 - 7.
 - 8.
 - 9.
 - 10.
 - 11.
 - 12.
 - 13.
 - 14.
 - 15.
 - 16.
 - 17.
 - 18.
 - 19.
 - 20.
 - 21.
 - 22.
 - 23.
 - 24.
 - 25.
 - 26.
 - 27.
 - 28.
 - 29.
 - 30.
 - 31.
 - 32.
 - 33.
 - 34.
 - 35.
 - 36.
 - 37.
 - 38.
 - 39.
 - 40.
 - 41.
 - 42.
 - 43.
 - 44.
 - 45.
 - 46.
 - 47.
 - 48.
 - 49.
 - 50.
 - 51.
 - 52.
 - 53.
 - 54.
 - 55.
 - 56.
 - 57.
 - 58.
 - 59.
 - 60.
 
總結
在實際專案中,也可以進一步拓展郵件服務的功能,例如:
- 非同步發送郵件:使用 Spring 的非同步任務機制,將郵件發送任務非同步化,避免阻塞主線程,提高系統效能和回應速度。
 - 郵件發送狀態追蹤:透過郵件伺服器的回饋或自訂的追蹤機制,記錄郵件的發送狀態(如發送成功、失敗、已讀等),方便系統進行後續處理。