<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
public class SmtpAuthenticator extends Authenticator {
private final String username, password;
public SmtpAuthenticator(String username, String password) {
this.username = username; this.password = password;
}
@Override protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
}
public class MailSender {
private final Session session;
private final String from;
public MailSender(String host, int port, String user, String pass, boolean ssl, String from) {
Properties props = new Properties();
props.put("mail.smtp.host", host);
props.put("mail.smtp.port", port);
props.put("mail.smtp.auth", "true");
if (ssl) {
props.put("mail.smtp.ssl.enable", "true");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.port", port);
} else {
props.put("mail.smtp.starttls.enable", "true");
}
this.session = Session.getInstance(props, new SmtpAuthenticator(user, pass));
this.from = from;
}
/** 發送純文本 */
public void sendText(String to, String subject, String text) throws MessagingException {
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(from));
msg.setRecipients(Message.RecipientType.TO, to);
msg.setSubject(subject, "UTF-8");
msg.setText(text, "UTF-8");
Transport.send(msg);
}
/** 發送 HTML */
public void sendHtml(String to, String subject, String html) throws MessagingException {
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(from));
msg.setRecipients(Message.RecipientType.TO, to);
msg.setSubject(subject, "UTF-8");
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(html, "text/html;charset=UTF-8");
MimeMultipart mp = new MimeMultipart();
mp.addBodyPart(htmlPart);
msg.setContent(mp);
Transport.send(msg);
}
/** 發送帶附件 */
public void sendWithAttachment(String to, String subject, String text,
File file, String fileName) throws MessagingException, UnsupportedEncodingException {
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(from));
msg.setRecipients(Message.RecipientType.TO, to);
msg.setSubject(subject, "UTF-8");
MimeBodyPart textPart = new MimeBodyPart();
textPart.setText(text, "UTF-8");
MimeBodyPart attachPart = new MimeBodyPart();
attachPart.attachFile(file);
attachPart.setFileName(MimeUtility.encodeText(fileName, "UTF-8", "B"));
MimeMultipart mp = new MimeMultipart("mixed");
mp.addBodyPart(textPart);
mp.addBodyPart(attachPart);
msg.setContent(mp);
Transport.send(msg);
}
/** 發送帶內嵌圖片 HTML */
public void sendWithInlineImage(String to, String subject, String html,
File imgFile, String contentId) throws MessagingException {
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(from));
msg.setRecipients(Message.RecipientType.TO, to);
msg.setSubject(subject, "UTF-8");
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(html, "text/html;charset=UTF-8");
MimeBodyPart imgPart = new MimeBodyPart();
imgPart.attachFile(imgFile);
imgPart.setContentID("<" + contentId + ">"); // HTML 中引用 cid:contentId
imgPart.setDisposition(MimeBodyPart.INLINE);
MimeMultipart related = new MimeMultipart("related");
related.addBodyPart(htmlPart);
related.addBodyPart(imgPart);
msg.setContent(related);
Transport.send(msg);
}
}
| 郵箱 | SMTP 主機 | SSL 端口 | STARTTLS 端口 | 備註 |
|---|---|---|---|---|
| QQ 郵箱 | smtp.qq.com | 465 | 587 | 需開啟 SMTP 服務獲取授權碼 |
| 163 郵箱 | smtp.163.com | 465/994 | 25/587 | 授權碼非登錄密碼 |
| Gmail | smtp.gmail.com | 465 | 587 | 需開啟「低安全性應用」或應用專用密碼 |
| Outlook | smtp.office365.com | — | 587 | 強制 STARTTLS |
@Service
public class AsyncMailService {
@Autowired private MailSender mailSender;
@Autowired private TaskExecutor mailExecutor; // 專用線程池
public void sendAsync(String to, String subject, String html) {
mailExecutor.execute(() -> {
int retry = 3;
while (retry-- > 0) {
try {
mailSender.sendHtml(to, subject, html);
return;
} catch (MessagingException e) {
if (retry == 0) log.error("郵件發送最終失敗: {}", to, e);
else { sleep(2000); }
}
}
});
}
}
spring:
mail:
host: smtp.163.com
port: 465
username: xxx@163.com
password: auth_code
protocol: smtps
properties:
mail:
smtp:
ssl:
enable: true
auth: true
timeout: 10000
connectiontimeout: 10000
writetimeout: 10000
直接注入 JavaMailSender 使用,無需手寫 MailSender。