JavaMail 是 Java 平台發送、接收電子郵件的標準 API。本文系統講解郵件協議基礎、核心 API 使用、文本/HTML/附件郵件發送、POP3/IMAP 接收、郵件轉發等完整實戰場景。
| 協議 | 全稱 | 用途 | 默認端口 | SSL 端口 |
|---|---|---|---|---|
| SMTP | Simple Mail Transfer Protocol | 發送郵件 | 25 | 465/587 |
| POP3 | Post Office Protocol v3 | 接收郵件(下載到本地) | 110 | 995 |
| IMAP | Internet Mail Access Protocol | 接收郵件(服務端管理) | 143 | 993 |
| MIME | Multipurpose Internet Mail Extensions | 郵件內容編碼標準 | - | - |
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>jakarta.mail</artifactId>
<version>2.0.1</version> <!-- Jakarta EE 9+ -->
</dependency>
<!-- 或舊版本 -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
// 核心類關係
Properties props → Session → Transport/Store → Message(MimeMessage) →
MimeBodyPart → MimeMultipart → Address(InternetAddress) → Authenticator
@Data
@ConfigurationProperties(prefix = "mail")
public class MailProperties {
private String host = "smtp.163.com";
private int port = 465;
private String username;
private String password;
private String protocol = "smtp";
private String encoding = "UTF-8";
private boolean ssl = true;
private Properties props = new Properties();
@PostConstruct
public void init() {
props.put("mail.smtp.host", host);
props.put("mail.smtp.port", port);
props.put("mail.smtp.auth", "true");
props.put("mail.transport.protocol", protocol);
if (ssl) {
props.put("mail.smtp.ssl.enable", "true");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
}
props.put("mail.debug", "false");
}
}
@Service
public class MailService {
@Autowired
private MailProperties mailProps;
private Session getSession() {
return Session.getInstance(mailProps.getProps(),
new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(mailProps.getUsername(),
mailProps.getPassword());
}
});
}
// 發送簡單文本郵件
public void sendText(String to, String subject, String content) {
sendMimeMessage(message -> {
message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
message.setSubject(subject);
message.setText(content);
});
}
// 發送 HTML 郵件(含內嵌圖片)
public void sendHtmlWithInlineImage(String to, String subject,
String htmlContent,
Map<String, File> inlineImages) {
sendMimeMessage(message -> {
message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
message.setSubject(subject);
MimeMultipart multipart = new MimeMultipart("related");
// HTML 正文
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(htmlContent, "text/html;charset=UTF-8");
multipart.addBodyPart(htmlPart);
// 內嵌圖片
for (Map.Entry<String, File> entry : inlineImages.entrySet()) {
MimeBodyPart imgPart = new MimeBodyPart();
imgPart.attachFile(entry.getValue());
imgPart.setContentID("<" + entry.getKey() + ">"); // CID 引用
multipart.addBodyPart(imgPart);
}
message.setContent(multipart);
});
}
// 發送帶附件郵件
public void sendWithAttachments(String to, String subject, String content,
List<File> attachments) {
sendMimeMessage(message -> {
message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
message.setSubject(subject);
MimeMultipart multipart = new MimeMultipart("mixed");
// 正文
MimeBodyPart textPart = new MimeBodyPart();
textPart.setContent(content, "text/html;charset=UTF-8");
multipart.addBodyPart(textPart);
// 附件
for (File file : attachments) {
MimeBodyPart attachPart = new MimeBodyPart();
attachPart.attachFile(file);
// 解決中文附件名亂碼
attachPart.setFileName(MimeUtility.encodeText(file.getName()));
multipart.addBodyPart(attachPart);
}
message.setContent(multipart);
});
}
// 通用發送邏輯
private void sendMimeMessage(Consumer<MimeMessage> messageConsumer) {
try {
MimeMessage message = new MimeMessage(getSession());
message.setFrom(new InternetAddress(mailProps.getUsername()));
messageConsumer.accept(message);
message.saveChanges(); // 重要:生成 Message-ID 等頭部
Transport.send(message);
} catch (MessagingException | UnsupportedEncodingException e) {
throw new MailSendException("郵件發送失敗", e);
}
}
}
public class Pop3Receiver {
public List<EmailDTO> receive(String host, String username, String password) {
Properties props = new Properties();
props.put("mail.store.protocol", "pop3");
props.put("mail.pop3.host", host);
props.put("mail.pop3.port", 995);
props.put("mail.pop3.ssl.enable", "true");
Session session = Session.getInstance(props);
List<EmailDTO> emails = new ArrayList<>();
try (Store store = session.getStore("pop3")) {
store.connect(username, password);
Folder inbox = store.getFolder("INBOX");
inbox.open(Folder.READ_ONLY);
Message[] messages = inbox.getMessages();
// 獲取最新 20 封
for (int i = messages.length - 1; i >= Math.max(0, messages.length - 20); i--) {
emails.add(parseMessage(messages[i]));
}
inbox.close(false);
} catch (MessagingException | IOException e) {
throw new MailReceiveException("接收郵件失敗", e);
}
return emails;
}
private EmailDTO parseMessage(Message msg) throws MessagingException, IOException {
EmailDTO dto = new EmailDTO();
dto.setSubject(msg.getSubject());
dto.setFrom(Arrays.toString(msg.getFrom()));
dto.setSentDate(msg.getSentDate());
dto.setContent(extractContent(msg));
return dto;
}
private String extractContent(Part part) throws MessagingException, IOException {
if (part.isMimeType("text/plain")) {
return (String) part.getContent();
} else if (part.isMimeType("text/html")) {
return (String) part.getContent();
} else if (part.isMimeType("multipart/*")) {
Multipart mp = (Multipart) part.getContent();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mp.getCount(); i++) {
sb.append(extractContent(mp.getBodyPart(i)));
}
return sb.toString();
}
return "";
}
}
public class ImapIdleListener {
public void startListening(String host, String username, String password,
Consumer<Message> handler) {
Properties props = new Properties();
props.put("mail.store.protocol", "imaps");
props.put("mail.imaps.host", host);
Session session = Session.getInstance(props);
try (Store store = session.getStore("imaps")) {
store.connect(username, password);
Folder inbox = store.getFolder("INBOX");
inbox.open(Folder.READ_WRITE);
// 添加消息監聽器
inbox.addMessageCountListener(new MessageCountAdapter() {
@Override
public void messagesAdded(MessageCountEvent ev) {
Message[] msgs = ev.getMessages();
for (Message msg : msgs) {
handler.accept(msg);
}
}
});
// IDLE 保持長連接
while (true) {
if (inbox instanceof IMAPFolder) {
((IMAPFolder) inbox).idle();
} else {
Thread.sleep(30000); // 輪詢備選
}
}
} catch (Exception e) {
log.error("IMAP IDLE 異常", e);
}
}
}
public void forwardEmail(Message original, String to) {
MimeMessage forward = new MimeMessage(session);
forward.setFrom(new InternetAddress(username));
forward.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
forward.setSubject("Fwd: " + original.getSubject());
MimeBodyPart body = new MimeBodyPart();
body.setContent(original, "message/rfc822"); // 整個郵件作為附件
MimeMultipart mp = new MimeMultipart();
mp.addBodyPart(body);
forward.setContent(mp);
forward.saveChanges();
Transport.send(forward);
}
@Async("mailExecutor")
public CompletableFuture<Void> sendAsync(String to, String subject, String content) {
sendText(to, subject, content);
return CompletableFuture.completedFuture(null);
}
// 配置線程池
@Bean("mailExecutor")
public ThreadPoolTaskExecutor mailExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("mail-");
return executor;
}
// Thymeleaf 模板郵件
public void sendTemplate(String to, String subject, String templateName,
Map<String, Object> variables) {
Context context = new Context();
context.setVariables(variables);
String html = templateEngine.process(templateName, context);
sendHtml(to, subject, html);
}
| 問題 | 原因 | 解決方案 |
|---|---|---|
| 發送超時 | 網絡/防火牆/端口 | 檢查 465/587 端口;嘗試 STARTTLS (587) |
| 認證失敗 | 密碼錯誤/未開啟 SMTP | 使用授權碼而非登錄密碼;郵箱設置開啟 SMTP/IMAP |
| 進垃圾箱 | 內容/域名信譽/SPF/DKIM | 配置 SPF/DKIM/DMARC;避免敏感詞;使用企業郵箱 |
| 中文附件名亂碼 | RFC 2231 編碼 | MimeUtility.encodeText(filename) |
| 大附件發送失敗 | 郵箱大小限制 | 分卷壓縮/雲存儲鏈接/斷點續傳 |
| 連接泄漏 | 未關閉 Store/Folder/Transport | try-with-resources / finally 關閉 |
JavaMail 提供了完整的郵件收發能力。掌握 MIME 結構構建、異步發送、模板集成、安全配置等實戰技巧,能構建企業級郵件系統。現代應用建議:發送走 SMTP+異步+模板,接收走 IMAP+IDLE+事件驅動,並妥善處理編碼、大附件、垃圾箱規避等工程化問題。