| 類/介面 | 包 | 角色 | 關鍵方法 |
| Session | javax.mail | 郵件會話上下文,連接屬性、認證器 | getInstance(props, authenticator) |
| Message / MimeMessage | javax.mail / .inernet | 郵件對象(主題、發件人、收件人、內容) | setFrom/setRecipients/setSubject/setContent |
| Address / InternetAddress | javax.mail / .internet | 郵箱地址封裝 | new InternetAddress("a@b.com", "Name") |
| Transport | javax.mail | 發送傳輸(SMTP 實現) | send(msg) / connect/sendMessage/close |
| Store / Folder | javax.mail | 接收存儲(POP3/IMAP 實現) | store.getFolder("INBOX") |
| MimeMultipart / MimeBodyPart | javax.mail.internet | 多部件郵件容器/部件(文本+附件+內嵌) | addBodyPart / setContent |
依賴坐標(Jakarta Mail 2.0+ / JavaMail 1.6)
<!-- Jakarta Mail (Spring Boot 3 / Jakarta EE 9+) -->
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>jakarta.mail</artifactId>
<version>2.0.1</version>
</dependency>
<!-- 兼容舊版 -->
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
實戰一:純文本郵件(兩種認證方式)
方式 A:Session.getInstance(props) + Transport 手動連接
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.163.com");
props.put("mail.smtp.port", "465");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.ssl.enable", "true"); // SSL 隱式
props.put("mail.transport.protocol", "smtp");
Session session = Session.getInstance(props);
session.setDebug(true); // 開啟調試輸出
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress("sender@163.com", "發件人", "UTF-8"));
msg.setRecipient(Message.RecipientType.TO, new InternetAddress("receiver@qq.com"));
msg.setSubject("測試郵件", "UTF-8");
msg.setText("你好,這是純文本郵件。", "UTF-8");
try (Transport transport = session.getTransport("smtp")) {
transport.connect("smtp.163.com", "sender@163.com", "授權碼"); // 注意:授權碼非登錄密碼
transport.sendMessage(msg, msg.getAllRecipients());
}
方式 B:Session.getInstance(props, Authenticator) + Transport.send 靜態發送
Session session = Session.getInstance(props, new Authenticator() {
@Override protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("sender@163.com", "授權碼");
}
});
MimeMessage msg = new MimeMessage(session);
// ... 設置內容同方式 A
Transport.send(msg); // 內部自動連接、發送、關閉
實戰二:HTML 富文本郵件
msg.setContent("<h1 style='color:#2c3e50'>HTML 郵件</h1><p>支援 <b>粗體</b>、<a href='https://example.com'>鏈接</a></p>", "text/html;charset=UTF-8");
實戰三:帶附件郵件(Multipart 混合模式)
MimeMultipart multipart = new MimeMultipart("mixed"); // mixed: 附件模式
// 部件 1:正文
MimeBodyPart textPart = new MimeBodyPart();
textPart.setContent("請查收附件。", "text/plain;charset=UTF-8");
multipart.addBodyPart(textPart);
// 部件 2:附件
MimeBodyPart filePart = new MimeBodyPart();
filePart.attachFile(new File("D:/report.pdf")); // 自動設置 Content-Disposition: attachment
// 解決中文文件名亂碼
filePart.setFileName(MimeUtility.encodeText("季度報告.pdf", "UTF-8", "B"));
multipart.addBodyPart(filePart);
msg.setContent(multipart);
實戰四:內嵌圖片郵件(Related 模式 + CID 引用)
MimeMultipart root = new MimeMultipart("related"); // related: 內嵌資源模式
// 部件 1:HTML 正文,圖片用 cid:imageId 引用
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent("<h1>內嵌圖片</h1><img src='cid:logo' />", "text/html;charset=UTF-8");
root.addBodyPart(htmlPart);
// 部件 2:圖片資源,設置 Content-ID 對應 cid
MimeBodyPart imagePart = new MimeBodyPart();
imagePart.attachFile(new File("D:/logo.png"));
imagePart.setContentID("<logo>"); // 必須包含尖括號
imagePart.setDisposition(MimeBodyPart.INLINE);
root.addBodyPart(imagePart);
msg.setContent(root);
實戰五:混合場景(HTML 正文 + 內嵌圖片 + 附件)
MimeMultipart mixed = new MimeMultipart("mixed");
// 子層 related:HTML + 內嵌圖片
MimeMultipart related = new MimeMultipart("related");
MimeBodyPart html = new MimeBodyPart();
html.setContent("...<img src='cid:chart' />...", "text/html;charset=UTF-8");
related.addBodyPart(html);
MimeBodyPart chart = new MimeBodyPart();
chart.attachFile(new File("chart.png"));
chart.setContentID("<chart>");
chart.setDisposition(MimeBodyPart.INLINE);
related.addBodyPart(chart);
// 將 related 包裝為 mixed 的一個部件
MimeBodyPart relatedWrapper = new MimeBodyPart();
relatedWrapper.setContent(related);
mixed.addBodyPart(relatedWrapper);
// 附件部件直接加入 mixed
MimeBodyPart attachment = new MimeBodyPart();
attachment.attachFile(new File("data.xlsx"));
attachment.setFileName(MimeUtility.encodeText("數據.xlsx", "UTF-8", "B"));
mixed.addBodyPart(attachment);
msg.setContent(mixed);
常見問題與解決
| 問題 | 原因 | 解決 |
| 535 Error: authentication failed | 用戶名/授權碼錯誤、未開啟 SMTP 服務 | 郵箱設置開啟 SMTP、生成專用授權碼 |
| Could not connect to SMTP host | 端口/SSL 模式不匹配、防火牆攔截 | 465=SSL、587=STARTTLS、25=平文(多被封) |
| 中文附件名亂碼 | RFC 2231 編碼缺失 | `MimeUtility.encodeText(filename, "UTF-8", "B")` |
| 內嵌圖片不顯示 | Content-ID 格式錯誤、cid 引用不匹配 | `setContentID("")` + HTML `src='cid:id'` |
| 大附件發送失敗 | 郵箱限制(通常 20-50MB)、超時 | 分卷壓縮、雲存儲鏈接、調大 timeout |
生產級最佳實踐
- 異步發送:`@Async` + 線程池,或消息隊列(RabbitMQ/Kafka)削峰填谷。
- 模板引擎:Thymeleaf/FreeMarker 渲染 HTML,分離業務與視圖。
- 配置外部化:Spring Boot `spring.mail.*` 自動配置 `JavaMailSender`,注入即用。
- 重試與熔斷:Resilience4j 重試 3 次、指數退避,熔斷保護下游。
- 日誌審計:記錄 Message-ID、收件人、發送耗時、狀態,便於追蹤。
Spring Boot 整合一鍵啟用
spring:
mail:
host: smtp.163.com
port: 465
username: sender@163.com
password: auth_code
protocol: smtp
properties:
mail:
smtp:
ssl:
enable: true
auth: true
starttls:
enable: false
debug: false
// 使用
@Autowired JavaMailSender mailSender;
public void sendSimple(String to, String subject, String text) {
SimpleMailMessage msg = new SimpleMailMessage();
msg.setTo(to); msg.setSubject(subject); msg.setText(text);
mailSender.send(msg);
}
- 上一篇文章: Java 中文排序完全指南:Collator 實現漢字拼音/筆畫/部首多維度排序
- 下一篇文章: 23 種設計模式趣味通識:用追 MM 故事秒懂創建型、結構型、行為型模式核心思想
Copyright ©
程式碼之家 All Rights Reserved