ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

JavaMail电子邮件系统设计完整指南:SMTP/IMAP收发与MIME解析

JavaMail电子邮件系统设计完整指南:SMTP/IMAP收发与MIME解析 简介基于JavaMail的电子邮件系统课程设计报告PDF格式完整梳理了从邮件协议到JavaMail API实现的系统设计过程面向计算机相关专业学生及Java网络编程初学者可作为课程设计或毕业设计的直接参考。资源共包含1个PDF文件压缩包大小1.97MB内容紧凑便于离线阅读。已有415人浏览学习。报告系统讲解了SMTP、POP3、IMAP三大协议及MIME标准并围绕Session、Store、Folder、Message、Transport等核心类给出了邮件发送、接收与管理功能的设计思路与实现要点同时通过总体框图和服务器交互模型展示了电子邮件系统的工作机制还提供了客户端登录、附件处理、邮件夹管理等关键模块的分析内容覆盖系统分析、总体设计到关键代码逻辑的完整链路。读者可据此搭建电子邮件系统原型理解底层协议交互细节并具备独立完成同类Mail客户端设计的能力。1. 基于 JavaMail 电子邮件系统的设计一个课设题目的完整落地方案选「基于javaMail电子邮件系统的设计(含源文件)」当课题的人第一周会搜到一大堆能发邮件的 Demo第三周才会发现 Demo 只覆盖了 20% 的工作量。真正的电子邮件系统要把 SMTP 发信、IMAP 收信、MIME 附件解析、中文乱码、数据保存和界面串成一条完整链路任何一个环节断掉答辩现场都会冷场。这个题目交付物是一份设计文档加一套可运行源码适合正在做 Java 课程设计、毕业设计或者想把手写邮件协议的经历写进简历的开发者。下面按我实际做过的方案来讲先定依赖和协议选型再给收发模块完整代码列透翻车点最后说怎么把课设做成一个能自证结果的完整系统。2. 技术底座选型javax.mail 还是 jakarta.mail协议与系统分层怎么定很多人在这个题目上走弯路不是代码写不出来而是开头没把两件事定死用哪一代 JavaMail 依赖收信用 POP3 还是 IMAP。这两件事后面改起来伤筋动骨开头花十分钟想清楚省下的是整周的返工时间。2.1 先定依赖javax.mail 与 jakarta.mail 的区别JavaMail 这个 API 经历过一次改名。早期版本包名是 javax.mail属于 Java EE 规范的一部分最后的版本停在 1.6.x。Oracle 把 Java EE 捐给 Eclipse 基金会之后规范改名 Jakarta Mail包名也全部变成 jakarta.mail。网上搜到的老教程、老课设代码基本都是 javax.mail新工程用 jakarta.mail。我的选择标准很简单先看本机 JDK 版本再定坐标。JDK 8 及以下的教学环境老老实实用 javax.mail兼容性最好JDK 9 引入了模块系统javax.mail 虽然没有立刻报废但要额外处理 activation 的依赖新装的 JDK 17 机器上直接用 jakarta.mail 更顺。使用场景推荐坐标说明JDK 8 课设 / 老工程com.sun.mail:javax.mail:1.6.2网上老代码最多遇到问题好搜JDK 11 新工程jakarta.mail:jakarta.mail-api org.eclipse.angus:angus-mail前者是 API后者是运行时实现如果你不用 Maven就把 mail.jar 和 activation.jar 放进项目 lib 目录在 IDE 里 add to build path。这步是很多含源文件课设包的标准做法代码本身不复杂坑在依赖路径后面避坑章节会专门讲。2.2 SMTP、POP3、IMAP 三个协议在系统里各管什么做邮件系统至少要知道三个协议的分工。SMTP 负责发信POP3 和 IMAP 负责收信但它们的行为差异很大。协议默认端口SSL 端口在本系统中的角色SMTP25 / 587465发信必须用POP3110995收信取走邮件服务器端可删除IMAP143993收信和服务端同步状态推荐用我一般推荐收信用 IMAP 而不是 POP3。原因很实际POP3 的逻辑是把邮件下载到本地服务器上可以删掉它没有一个干净的已读/未读状态管理第二次轮询收信会把邮件重复拉下来。IMAP 的已读、未读、删除状态都保存在服务器端程序端可以查询和修改这些标志演示时你在网页邮箱标记一封未读客户端立刻能看到状态变化效果比 POP3 好一个档次。如果课题手册里白纸黑字写了采用 POP3 协议那代码里把协议字符串从 imap 换成 pop3 即可分层不需要变动。端口选择有个容易被忽略的细节。465 是 SSL 直连端口587 是 STARTTLS 端口二者的 JavaMail 配置不一样。后文避坑部分会展开这里先记住163 和 QQ 邮箱的 SMTP 都提供 465测试时优先用 465 加 SSL 直连。2.3 系统分层收发、解析、存储、展示分开很多同学拿到题目的第一反应是写一个 MailUtil 类里面放 send 和 receive 两个静态方法能跑就交差。这种做法对 Demo 没问题但对系统设计四个字是减分的。我习惯把工程拆成四层哪怕规模很小也拆coreSession 工厂、账号配置、连接参数所有协议入口。send发信模块封装普通文本和带附件两种发信方式。receive收信模块封装连接、拉取、定时轮询。parseMIME 解析递归处理 multipart保存附件处理编码。store数据持久化把收到的邮件写进数据库。ui控制台、Swing/JavaFX 窗口或者一个简单的 Web 页面。目录结构可以长这样mail-system/ ├── pom.xml ├── src/main/java/ │ ├── core/ # Session 工厂、连接参数 │ ├── send/ # 发信模块 │ ├── receive/ # 收信模块 │ ├── parse/ # MIME 解析 │ ├── store/ # 数据库持久化 │ └── ui/ # 展示层 ├── src/main/resources/ │ ├── mail.properties │ └── logback.xml └── attachments/ # 附件保存目录分层带来的直接好处是答辩时你能对着包结构讲这是协议层负责和邮件服务器打交道这是解析层负责把 MIME 树解开。每一层都可以单独测试。另一个好处是后续扩展不用伤筋动骨比如从控制台输出换成 Swing 界面只需要动 ui 层收发核心一行不改。这个题目的标题里没有Web字样我不建议硬上 Spring Boot 加网页壳Java SE 加一个嵌入式数据库足够把系统撑起来。3. 发信模块落地Maven 工程 SMTP 带附件发送的完整代码这一章做完你的系统就能发信了。建工程、写 POM、写发信代码、调参数四步走。3.1 新建 Maven 工程与最小 POM先建一个标准目录mkdir -p mail-system/src/main/java/com/example/mail cd mail-systemPOM 文件内容如下project xmlnshttp://maven.apache.org/POM/4.0.0 modelVersion4.0.0/modelVersion groupIdcom.example/groupId artifactIdmail-system/artifactId version1.0.0/version packagingjar/packaging properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties dependencies dependency groupIdcom.sun.mail/groupId artifactIdjavax.mail/artifactId version1.6.2/version /dependency /dependencies /project这个 POM 只声明了一个依赖JavaMail 的 jar 自带 SMTP、IMAP、POP3 三套协议实现不需要额外引入 native 库。maven.compiler.source 和 target 设成 1.8 是适配 JDK 8如果你的机器是 JDK 17把坐标换成上一章表格里的 jakarta.mail 组合就行。Maven 拉依赖偶尔会因为仓库访问慢卡住思路和配置 Linux 的 yum 源一样改 settings.xml把中央仓库指向国内镜像源改动很小但能省掉大量等待时间。3.2 发送纯文本邮件的核心代码package com.example.mail.send; import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import java.util.Properties; public class MailSender { private final Session session; public MailSender(String host, int port, String username, String password) { Properties props new Properties(); props.put(mail.smtp.host, host); props.put(mail.smtp.port, port); props.put(mail.smtp.auth, true); // 465 端口必须开 SSL587 端口则改用 starttls props.put(mail.smtp.ssl.enable, true); props.put(mail.smtp.connectiontimeout, 10000); props.put(mail.smtp.timeout, 10000); props.put(mail.smtp.writetimeout, 10000); this.session Session.getInstance(props, new Authenticator() { Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password); } }); } public void sendText(String from, String to, String subject, String content) throws Exception { MimeMessage message new MimeMessage(session); message.setFrom(new InternetAddress(from)); message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to)); message.setSubject(subject, UTF-8); message.setText(content, UTF-8); Transport.send(message); } }代码逻辑不复杂Session 是会话对象包含所有连接参数Authenticator 是认证回调JavaMail 在需要登录时自动调用它取用户名密码不强制你在连接时手动传。MimeMessage 负责构建邮件头setSubject 和 setText 都带上 UTF-8中文不会乱码。这里有三组参数必须在工程里出现否则演示时会卡到怀疑人生参数作用建议值mail.smtp.connectiontimeoutTCP 建连超时10000mail.smtp.timeout读取服务器响应超时10000mail.smtp.writetimeout写入数据超时10000网上大量示例代码不写这三个参数导致服务器没响应时客户端会傻等好几分钟才报错。把超时统一设成 10 秒失败立刻暴露。还有一个调试利器session.setDebug(true)。打开后 JavaMail 会把整个 SMTP 会话打印出来你会看到服务器的 220 欢迎语、250 OK、354 输入正文、535 认证失败这些返回码。平时关掉排障时打开比盲猜快得多。3.3 发送带附件的邮件Multipart 和 FileDataSourcepublic void sendWithAttachment(String from, String to, String subject, String content, File attachment) throws Exception { MimeMessage message new MimeMessage(session); message.setFrom(new InternetAddress(from)); message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to)); message.setSubject(subject, UTF-8); // 正文部分 MimeBodyPart textPart new MimeBodyPart(); textPart.setText(content, UTF-8); // 附件部分FileDataSource 负责把本地文件转成 MIME 数据源 MimeBodyPart filePart new MimeBodyPart(); FileDataSource source new FileDataSource(attachment); filePart.setDataHandler(new DataHandler(source)); filePart.setFileName(source.getName()); Multipart multipart new MimeMultipart(); multipart.addBodyPart(textPart); multipart.addBodyPart(filePart); message.setContent(multipart); Transport.send(message); }这段的关键是理解 MIME 的树形结构。一封带附件的邮件最外层是 multipart/mixed里面至少有两个 MimeBodyPart一个是纯文本正文一个是附件。DataHandler 负责把文件内容包装成 JavaMail 能理解的数据源setFileName 决定收件人看到的文件名。如果附件不是本地文件而是数据库里的字节数组就不要用 FileDataSource改成 ByteArrayDataSourceByteArrayDataSource bads new ByteArrayDataSource(byteArray, application/octet-stream); filePart.setDataHandler(new DataHandler(bads));这样可以避免把临时文件先写到磁盘再读一遍收件箱系统里很常见。3.4 发信失败时看什么异常分类与排查顺序发信报错并不可怕可怕的是对着堆栈乱试。我一般按这张表的顺序排查现象 / 异常含义优先排查项AuthenticationFailedException认证失败账号或授权码不对SendFailedException收件人被服务器拒绝邮件地址格式、收件人数量Could not connect to SMTP host连不上服务器端口、SSL 配置、防火墙SocketTimeoutException服务器不响应超时参数、出口网络限制另外Transport.send 返回只能说明 SMTP 服务器收下了这封信不代表收件人已经看到。要验证整条链路真正通得靠最后一章说的发给自己再收回来的回环测试这也是这个课设最容易加分的验证手段。4. 收信模块落地IMAP 拉取、MIME 递归解析与附件保存发信只是单向链路收信模块才是把发件箱变成邮件系统的关键。这一章做完程序能自动收信、解析正文、保存附件。4.1 用 IMAP 拉取未读邮件Store、Folder、Messagepackage com.example.mail.receive; import javax.mail.Folder; import javax.mail.Flags; import javax.mail.Message; import javax.mail.Session; import javax.mail.Store; import javax.mail.internet.InternetAddress; import javax.mail.search.FlagTerm; import java.util.Properties; public class MailReceiver { private final Session session; public MailReceiver() { Properties props new Properties(); props.put(mail.imap.ssl.enable, true); props.put(mail.imap.connectiontimeout, 10000); props.put(mail.imap.timeout, 10000); this.session Session.getInstance(props); } public void receiveUnread(String host, String username, String password) throws Exception { // imaps 表示走 SSL 的 IMAP对应 993 端口 Store store session.getStore(imaps); store.connect(host, username, password); try { Folder folder store.getFolder(INBOX); folder.open(Folder.READ_WRITE); // 服务端未读标志位为 false 的邮件 FlagTerm unread new FlagTerm(new Flags(Flags.Flag.SEEN), false); Message[] messages folder.search(unread); for (Message message : messages) { System.out.println(主题: message.getSubject()); System.out.println(发件人: InternetAddress.toString(message.getFrom())); } folder.close(false); // false 表示不删除邮件 } finally { store.close(); } } }这里几个参数值得单独说明。getStore(imaps) 是带 SSL 的 IMAP 简写如果你在 Properties 里已经设了 mail.imap.ssl.enabletrue也可以写 getStore(imap)效果等价。folder.open(Folder.READ_WRITE) 允许程序修改邮件的已读标志如果只读后面想标记已读会抛异常。folder.close(false) 的第二个参数是 expunge传 true 会把服务器上带删除标记的邮件真正删掉演示场景一律 false。搜索条件用 FlagTerm 包了一下它查的是服务器上的 SEEN 标志位。这也是 IMAP 和 POP3 最大的区别状态不在本地在服务器端。4.2 递归解析 multipart正文和附件分头处理package com.example.mail.parse; import javax.mail.BodyPart; import javax.mail.Message; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.internet.MimeBodyPart; import java.io.File; public class MailParser { public void handlePart(Part part) throws Exception { String disposition part.getDisposition(); String fileName part.getFileName(); if (part.isMimeType(multipart/*)) { // 遇到容器就递归进去邮件体是树形结构 Multipart multipart (Multipart) part.getContent(); for (int i 0; i multipart.getCount(); i) { handlePart(multipart.getBodyPart(i)); } } else if (Part.ATTACHMENT.equalsIgnoreCase(disposition) || (fileName ! null !inline.equalsIgnoreCase(disposition))) { // disposition 为空的带名 part在很多客户端里也是附件 if (part instanceof MimeBodyPart) { String safeName sanitize(fileName); ((MimeBodyPart) part).saveFile(attachments File.separator safeName); } } else { // 非附件部分按正文处理 Object content part.getContent(); if (content instanceof String) { System.out.println(正文: content); } } } private String sanitize(String name) { return name.replaceAll([\\\\/:*?\|], _); } }递归不能省。你用 QQ 邮箱或 163 邮箱发一封带附件的信实际产生的 MIME 结构可能是 multipart/mixed 套 multipart/alternative正文部分可能再加一个 multipart/related 内嵌图片。如果只处理第一层附件会漏掉内嵌图片会变成一串 base64 乱码。判断附件有两个通用条件一是 disposition 等于 attachment二是 disposition 为 null 但文件名存在且不是 inline。许多客户端发送的附件只有文件名没有显式 disposition第二个条件就是为了兼容这部分。saveFile 默认覆盖同名文件轮询收信时同名附件会互相覆盖我通常会在文件名前拼时间戳。4.3 中文主题与附件名乱码MimeUtility 解码邮件头里的非 ASCII 字符不走 UTF-8 明文而是按 RFC 2047 编码成类似?UTF-8?B?5L2g5aW9?的格式。主题字段 getSubject() 大多会自动解码但附件名不一定。import javax.mail.internet.MimeUtility; // 附件文件名统一解码 String rawName part.getFileName(); String niceName MimeUtility.decodeText(rawName); // 如果遇到 filename*UTF-8xxx 格式 String finalName niceName; if (rawName ! null rawName.contains(filename*)) { String encoded rawName.split()[1]; finalName java.net.URLDecoder.decode(encoded, UTF-8); }这段代码处理了两种主流编码方式。第一种是传统的 RFC 2047MimeUtility.decodeText 直接解第二种是 RFC 2231 的 filename* 格式需要先从键值对里抠出编码部分再做 URL 解码。只处理一种在某个特定客户端发的邮件上就会翻车。正文乱码的问题通常不是 JavaMail 的锅而是字符集判断错了。text/plain 类型的 part 用 getContent() 时如果声明的 charset 是 UTF-8JavaMail 会按 UTF-8 解开返回 String如果拿到的是 InputStream就需要自己从 Content-Type 头里解析 charset 参数再手动转码。排障时先把 content-type 头打印出来一眼就能看出问题。4.4 定时收信ScheduledExecutorService 实现自动拉取import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; ScheduledExecutorService scheduler Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(() - { try { receiver.receiveUnread(host, username, password); } catch (Exception e) { // 实际项目中这里记日志演示时打印堆栈即可 e.printStackTrace(); } }, 0, 30, TimeUnit.SECONDS);这段代码让系统每 30 秒自动进一次收件箱效果非常接近真实客户端。演示时你只要在网页邮箱里发一封新邮件程序端最多半分钟就会打印出来。要注意 IMAP 连接不适合无限复用。服务器对空闲连接有超时策略长时间不通信会被掐断。课设规模下最简单可靠的做法是每次轮询新建连接、用完关闭不要维护一个全局 Store 硬撑长连接。邮件量大了再考虑连接池或 IMAP IDLE 推送那是后话30 秒轮询足够撑起演示。5. 邮件系统避坑指南五个翻车点与排查方法下面五个问题是我见过这个题目里出现频率最高的坑。每一条都按现象、原因、解决来写遇到问题直接对号入座。5.1 报 AuthenticationFailedException授权码被当成登录密码现象调用 SMTP 发送时报 AuthenticationFailedExceptionSMTP 返回码 535 或 551。打开 debug 能看到服务器明文返回认证失败。原因163、QQ 这些邮箱的 SMTP/IMAP 服务不是用登录密码认证的。你得先在网页邮箱里手动开启 SMTP 服务然后单独生成一个授权码。代码里如果填的是登录密码服务器永远拒绝。解决登录网页邮箱进入设置找到账户或安全设置开启 SMTP/IMAP 服务并生成授权码。代码里的 password 字段填授权码不是登录密码。另外我强烈建议把授权码放到 resources 下的配置文件里不要写死在源码中。授权码一旦跟着源文件一起交付泄露就得去网页端重置这是典型的课设答辩现场改代码事故源头。提示账号、授权码、端口这类环境相关配置全部放进 mail.properties源码中只保留读取逻辑。这样源文件怎么分发都不怕。5.2 465 端口连不上或 Connection reset加密模式和端口没对上现象连接 465 端口时 JavaMail 报 Connection reset 或 SSLHandshakeException或者连接 25 端口一直卡到超时。原因465 是 SSL 直连端口必须设置 mail.smtp.ssl.enabletrue。如果只写了端口没开 SSL客户端会用明文去和服务器握手服务器直接断开连接。反过来 587 端口是 STARTTLS需要打开 mail.smtp.starttls.enabletrue 而不是 ssl.enable。解决先查邮箱服务商给出的端口清单。163 和 QQ 的 SMTP 都提供 465实测用 465 加 SSL 直连最省事。连不上时不要只盯 Java 配置先用命令验证端口通不通telnet smtp.qq.com 465如果 telnet 都连不上说明是防火墙或网络出口对端口的放行问题和代码无关。把端口放行后再回来调 JavaMail避免在错误的方向上浪费时间。5.3 附件文件名乱码或后缀丢失解码时机不对现象附件下载后文件名是一串?UTF-8?B?...?或者变成???.png后缀丢失导致文件打不开。原因文件名写在 Content-Disposition 头里部分邮件服务器对 filename 的编码处理不同JavaMail 对附件名的自动解码不如主题字段彻底。除此之外有些客户端使用 filename* 格式兼容代码必须把 RFC 2047 和 RFC 2231 两种格式都处理掉。解决参考 4.3 节的 MimeUtility.decodeText 方案保存附件之前统一解码。一个很容易犯的错是在打印时解码一次、保存时又解一次已经解好的中文文件名二次解码反而变成乱码。正确做法是只解一次把解码结果保存起来复用。5.4 演示时收信模块一封未读都拉不到已读状态被自己手动搞乱了现象程序用 FlagTerm 搜未读邮件返回空数组但网页邮箱里明明躺着新邮件。原因IMAP 的已读/未读标志存储在服务器端。演示前你很可能手动登录网页邮箱看过信甚至本地客户端已经连过一次邮件的 SEEN 标志早就置位了。FlagTerm 未读条件自然搜不到。解决演示流程里固定一个先发一封新邮件给自己几秒后再点收信的脚本保证收件箱里一定有一封未读邮件。另一个更稳妥的方案是收信条件改成按日期拉取import javax.mail.search.SentDateTerm; import java.util.Calendar; Calendar cal Calendar.getInstance(); cal.add(Calendar.DAY_OF_MONTH, -7); // 最近 7 天 SentDateTerm term new SentDateTerm(javax.mail.search.ComparisonTerm.GE, cal.getTime()); Message[] recent folder.search(term);我一般两个方案都做默认拉最近 N 封未读标志作为附加展示字段。这样演示现场不会出现一封邮件都收不到的尴尬因为收件箱里只要有任何一封 7 天内的邮件程序就有内容可展示。5.5 导入含源文件压缩包直接红叉classpath 失效、编译输出混进源码现象解压课设包用 IDE 导入后项目树一片红叉提示无法打开源文件、程序包 javax.mail 不存在或者点击运行按钮执行的是另一个主类和当前打开的文件对不上。原因老课设包大多是 Eclipse 工程lib 里 jar 的路径写在 .classpath 文件中。压缩包解压到新目录后相对路径全部失效IDE 找不到依赖。更常见的问题是打包的人把 target 或 out 编译输出目录也塞进了压缩包class 文件数量多、体积比源文件大得多IDE 导入时又被误认为源码目录的一部分导致整个工程结构混乱。解决不用 IDE 的导入已有项目硬扛而是新建一个 Maven 工程把压缩包里 src 目录下的 java 文件复制进去在 pom.xml 中声明依赖重新加载。同时清理编译输出目录只保留源文件。交付含源文件的工程包时我一般只保留 src、pom.xml、README 文档和必要的配置文件target 目录一份都不带。多个 main 类并存时IDE 右上角的运行按钮默认只运行上一个执行过的类要运行当前文件就在编辑区右键选择 Run网上经常有人问为什么运行不了第二个源文件八成就是运行目标没切换过来不是源码问题。6. 把课设做出工程感数据库、日志与回环验证6.1 加分项一收信结果落库在 store 层加一张表存收到的邮件字段不需要复杂字段说明id自增主键from_addr发件人subject主题content正文attachment_name附件文件名receive_time收信时间read_flag已读标志用 H2 或 SQLite 这种嵌入式数据库一个 jar 就能跑不用让评委装 MySQL。收信模块每解析完一封邮件写入一条记录系统就有了数据层。6.2 加分项二日志替掉 System.outsession.setDebug(true) 只适合排障不适合正式运行。用 slf4j 加 logback把发信结果、收信数量、解析异常都记到日志文件。答辩现场打开日志文件按时间逐条说明哪封邮件几点几分发出、哪封几点几分收回比空口说系统能跑有说服力得多。6.3 加分项三回环验证发一封带附件的邮件给自己最硬核的自检是回环。程序先发一封带中文名附件的邮件给自己等 30 秒再走收信模块把邮件拉回来保存附件计算原文件和下载文件的 SHA-256sha256sum attachments/测试附件.pdf sha256sum download/测试附件.pdf两条哈希值一致说明 SMTP 发送、IMAP 接收、MIME 编码、附件解码整条链路全部正确。这个验证方式比口头说能发能收有力得多也容易在演示时制造亮点先算一次哈希收信后再算一次让评委亲眼看到数值相同。测试用例就两条纯文本邮件一条带中文名附件一条足够覆盖核心链路。这套方案我带人改过好几版最深的教训是授权码、服务器地址、端口这些环境配置永远不要写死在业务代码里。我当年答辩前临时换邮箱被硬编码的授权码坑了一次只能现场改源码重新编译从那以后所有配置都走资源文件加环境变量覆盖再没出过这种低级事故。照着上面的顺序把发信、收信、解析、落库跑通你的基于 JavaMail 电子邮件系统就不再是一个只有 Demo 的课设而是一条能自证结果的完整邮件链路希望帮到你。本文还有配套的精品资源点击获取
返回列表