首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JavaMail 郵件收發完整實戰指南

     編輯:Java 相關知識

前言

JavaMail 是 Java 平台發送、接收電子郵件的標準 API。本文系統講解郵件協議基礎、核心 API 使用、文本/HTML/附件郵件發送、POP3/IMAP 接收、郵件轉發等完整實戰場景。

一、郵件協議基礎

協議全稱用途默認端口SSL 端口
SMTPSimple Mail Transfer Protocol發送郵件25465/587
POP3Post Office Protocol v3接收郵件(下載到本地)110995
IMAPInternet Mail Access Protocol接收郵件(服務端管理)143993
MIMEMultipurpose Internet Mail Extensions郵件內容編碼標準--

關鍵區別:POP3 vs IMAP

  • POP3:下載後默認刪除服務端郵件,適合單設備使用
  • IMAP:郵件留在服務端,多設備同步,支持文件夾管理,現代首選

二、環境準備

Maven 依賴


<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>

三、核心 API 體系


// 核心類關係
Properties props → Session → Transport/Store → Message(MimeMessage) → 
    MimeBodyPart → MimeMultipart → Address(InternetAddress) → Authenticator

關鍵類職責

  • Session:郵件會話上下文,持有配置屬性,線程安全
  • Transport:發送協議實現(SMTP)
  • Store:接收協議實現(POP3/IMAP)
  • MimeMessage:MIME 格式郵件對象
  • MimeMultipart:混合內容容器(文本+HTML+附件+內嵌圖片)
  • Authenticator:認證器,提供用戶名密碼

四、發送郵件實戰

1. 基礎配置類


@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");
    }
}

2. 郵件發送服務


@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);
        }
    }
}

五、接收郵件實戰

1. POP3 接收示例


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 "";
    }
}

2. IMAP 監聽新郵件(IDLE 模式)


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);
        }
    }
}

六、進階場景

1. 郵件轉發


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);
}

2. 異步發送(生產環境必備)


@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;
}

3. 模板引擎集成


// 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/Transporttry-with-resources / finally 關閉

八、安全最佳實踐

  1. 憑證管理:配置文件加密、環境變量、密鑰管理系統
  2. 傳輸加密:強制 SSL/TLS,驗證服務器證書
  3. 發送限流:防止被判定為垃圾郵件發送者
  4. 內容過濾防止郵件注入攻擊(頭部注入)
  5. 審計日誌:記錄發送者、收件人、主題、時間、結果

總結

JavaMail 提供了完整的郵件收發能力。掌握 MIME 結構構建、異步發送、模板集成、安全配置等實戰技巧,能構建企業級郵件系統。現代應用建議:發送走 SMTP+異步+模板,接收走 IMAP+IDLE+事件驅動,並妥善處理編碼、大附件、垃圾箱規避等工程化問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved