JavaMail 是 Java 平台發送/接收郵件的標準 API。本文完整演示:核心流程解析、認證器封裝、發送器核心類、工廠模式管理、觀察者模式業務整合,構建可複用的批量郵件發送組件。
核心流程五步驟
- 認證器:繼承
Authenticator,重寫 getPasswordAuthentication() 提供帳號密碼。
- Session 配置:
Properties 設定 SMTP 主機、端口、認證、加密(SSL/TLS)。
- 構建郵件:
MimeMessage 設定寄件者、收件者、主題、內容(文本/HTML/附件)。
- 發送:
Transport.send(message)。
- 異常處理:捕獲
MessagingException、AddressException。
1. 認證器封裝
public class MailAuthenticator extends Authenticator {
private String username;
private String password;
public MailAuthenticator(String username, String password) {
this.username = username;
this.password = password;
}
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
// getter/setter 略
}
2. 郵件發送核心類
public class SimpleMailSender {
private final Properties props = System.getProperties();
private MailAuthenticator authenticator;
private Session session;
// 構造器:自動解析 SMTP 主機 (smtp.域名)
public SimpleMailSender(String username, String password) {
String smtpHost = "smtp." + username.split("@")[1];
init(username, password, smtpHost);
}
// 構造器:指定 SMTP 主機
public SimpleMailSender(String smtpHost, String username, String password) {
init(username, password, smtpHost);
}
private void init(String username, String password, String smtpHost) {
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.host", smtpHost);
// SSL 支援(465 端口)
// props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
// props.put("mail.smtp.socketFactory.port", "465");
// TLS 支援(587 端口)
// props.put("mail.smtp.starttls.enable", "true");
authenticator = new MailAuthenticator(username, password);
session = Session.getInstance(props, authenticator);
}
// 單發
public void send(String recipient, String subject, Object content)
throws AddressException, MessagingException {
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(authenticator.getUsername()));
msg.setRecipient(Message.RecipientType.TO, new InternetAddress(recipient));
msg.setSubject(subject);
msg.setContent(content.toString(), "text/html;charset=utf-8");
Transport.send(msg);
}
// 群發(單郵件多收件人,收件人互見)
public void send(List recipients, String subject, Object content)
throws AddressException, MessagingException {
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(authenticator.getUsername()));
msg.setRecipients(Message.RecipientType.TO,
recipients.stream().map(InternetAddress::new).toArray(InternetAddress[]::new));
msg.setSubject(subject);
msg.setContent(content.toString(), "text/html;charset=utf-8");
Transport.send(msg);
}
// 重載:接受 SimpleMail POJO
public void send(String recipient, SimpleMail mail) throws AddressException, MessagingException {
send(recipient, mail.getSubject(), mail.getContent());
}
public void send(List recipients, SimpleMail mail) throws AddressException, MessagingException {
send(recipients, mail.getSubject(), mail.getContent());
}
}
3. 郵件 POJO
public class SimpleMail {
private String subject;
private String content;
// getter/setter
}
4. 工廠模式管理發送器單例
public class MailSenderFactory {
private static SimpleMailSender serviceSender;
public static SimpleMailSender getSender(MailSenderType type) {
if (type == MailSenderType.SERVICE) {
if (serviceSender == null) {
// 從配置檔讀取帳密
serviceSender = new SimpleMailSender("service@126.com", "password");
}
return serviceSender;
}
return null;
}
}
5. 觀察者模式業務整合:價格變動通知
public class ProductPriceObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
if (!(o instanceof Product)) return;
Product product = (Product) o;
if (!(arg instanceof Float)) return;
Float newPrice = (Float) arg;
Float decrease = product.getPrice() - newPrice;
if (decrease > 0) {
SimpleMailSender sender = MailSenderFactory.getSender(MailSenderType.SERVICE);
List recipients = Arrays.asList("user1@qq.com", "user2@gmail.com");
try {
for (String to : recipients) {
sender.send(to, "價格變動通知",
"關注商品 " + product.getName() + " 降價!" +
"原價 " + product.getPrice() + " → 現價 " + newPrice +
",降幅 " + decrease + " 元。");
}
} catch (MessagingException e) {
log.error("郵件發送失敗", e);
}
}
}
}
生產環境關鍵增強清單
| 增強點 | 實現方案 |
| 連線池 | 使用 Session.getInstance(props, authenticator) 複用 Session;或整合 Apache Commons Email 內建連線池 |
| 異步發送 | @Async + 執行緒池 / 消息佇列 (RabbitMQ/Kafka) 削峰 |
| 重試機制 | 指數退避重試(Spring Retry / Resilience4j) |
| 模板引擎 | Thymeleaf/FreeMarker 渲染 HTML 郵件模板 |
| 附件/內嵌圖片 | MimeMultipart + MimeBodyPart 組裝 |
| 退信/打開率追蹤 | 整合 SendGrid/Mailgun/Amazon SES 等雲郵件服務 |
| 配置外部化 | Spring @ConfigurationProperties 綁定 application.yml |
常見 SMTP 設定參考
| 郵箱服務商 | SMTP 主機 | 端口 | 加密 |
| 163/126 | smtp.163.com | 465/994 | SSL |
| QQ | smtp.qq.com | 465/587 | SSL/TLS |
| Gmail | smtp.gmail.com | 465/587 | SSL/TLS |
| Outlook | smtp.office365.com | 587 | TLS |
開啟「客戶端授權碼」而非登錄密碼,並啟用 SMTP 服務。
- 上一篇文章: Java 7 NIO.2 文件操作完全指南:創建、刪除、複製、移動與屬性設置
- 下一篇文章: 變數命名精準化指南:消除歧義、布林語義、符合預期三大原則
Copyright ©
程式碼之家 All Rights Reserved