JavaMail 核心依賴
com.sun.mail
jakarta.mail
2.0.1
javax.mail
mail
1.4.7
核心發送流程
public class EmailSender {
private final String host;
private final String username;
private final String password;
private final String from;
private final Properties props;
public EmailSender(String host, int port, String username, String password, String from) {
this.host = host;
this.username = username;
this.password = password;
this.from = from;
this.props = new Properties();
props.put("mail.smtp.host", host);
props.put("mail.smtp.port", port);
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true"); // TLS 加密
props.put("mail.smtp.ssl.trust", host);
props.put("mail.debug", "false");
}
public void send(EmailMessage msg) throws MessagingException {
Session session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(msg.getTo()));
message.setSubject(msg.getSubject(), "UTF-8");
message.setSentDate(new Date());
// 構建內容
Multipart multipart = new MimeMultipart();
// HTML 正文
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(msg.getHtmlContent(), "text/html; charset=UTF-8");
multipart.addBodyPart(htmlPart);
// 附件(可選)
if (msg.getAttachments() != null) {
for (Attachment att : msg.getAttachments()) {
MimeBodyPart attPart = new MimeBodyPart();
attPart.attachFile(att.getFile());
attPart.setFileName(MimeUtility.encodeText(att.getFilename()));
multipart.addBodyPart(attPart);
}
}
message.setContent(multipart);
// 發送
Transport.send(message);
}
}
郵件模型類
public class EmailMessage {
private String to;
private String subject;
private String htmlContent;
private List attachments;
// getters/setters
}
public class Attachment {
private File file;
private String filename;
// getters/setters
}
常見 SMTP 服務商配置
| 服務商 | SMTP 主機 | 端口 | 加密 | 備註 |
| QQ 郵箱 | smtp.qq.com | 465/587 | SSL/TLS | 需開啟 SMTP 服務,用授權碼非密碼 |
| 163 郵箱 | smtp.163.com | 465/994 | SSL/TLS | 需開啟客戶端授權碼 |
| Gmail | smtp.gmail.com | 587 | TLS | 需開啟「低安全性應用」或應用專用密碼 |
| Outlook | smtp.office365.com | 587 | TLS | 企業郵箱可能不同 |
| 企業郵箱 | 自定義 | 25/465/587 | 視配置 | 諮詢 IT 部門 |
Spring Boot 整合(推薦生產用法)
配置 application.yml
spring:
mail:
host: smtp.qq.com
port: 465
username: your@qq.com
password: your_auth_code
protocol: smtp
properties:
mail:
smtp:
auth: true
ssl:
enable: true
starttls:
enable: false
debug: false
服務層封裝
@Service
public class MailService {
@Autowired private JavaMailSender mailSender;
@Value("${spring.mail.username}") private String from;
public void sendHtmlMail(String to, String subject, String html) {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(html, true); // true = HTML
mailSender.send(message);
}
public void sendWithAttachment(String to, String subject, String html, File file)
throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(html, true);
helper.addAttachment(MimeUtility.encodeText(file.getName()), file);
mailSender.send(message);
}
// 異步發送
@Async
public void sendAsync(String to, String subject, String html) {
sendHtmlMail(to, subject, html);
}
}
進階主題
批量發送優化
- 複用 Session 和 Transport 連接
- 異步線程池發送,避免阻塞主線程
- 速率限制,避免被判定為垃圾郵件
模板引擎整合
// Thymeleaf 模板
Context context = new Context();
context.setVariable("user", user);
context.setVariable("code", verificationCode);
String html = templateEngine.process("email/verify", context);
mailService.sendHtmlMail(user.getEmail(), "驗證碼", html);
發送狀態追蹤
- 同步發送:捕獲 MessagingException 判斷成功/失敗
- 異步發送:持久化發送記錄表,狀態:PENDING/SENT/FAILED
- 退信處理:配置郵件服務商回調或定期檢查 Bounce 郵箱
常見問題排查
| 現象 | 可能原因 | 解決 |
| Authentication failed | 密碼錯誤/未用授權碼 | 檢查郵箱設置,生成授權碼 |
| Could not connect to SMTP host | 端口/主機錯誤/防火牆 | telnet 測試連通性,確認端口 |
| 550 Mailbox unavailable | 收件箱不存在/滿/被封 | 驗證郵箱地址有效性 |
| 中文附件名亂碼 | 編碼未處理 | MimeUtility.encodeText(filename) |
| 發送緩慢 | 同步阻塞/DNS 解析慢 | 異步發送、配置本地 DNS 緩存 |
安全提醒
- **絕不**在代碼/配置文件中硬編碼密碼,使用環境變量/配置中心
- 生產環境強制啟用 TLS/SSL
- 發送頻率控制,避免 IP 被列入黑名單
- 敏感信息(驗證碼、鏈接)設置過期時間