ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

JavaMail电子邮件系统设计:从SMTP收发原理到实现与避坑

JavaMail电子邮件系统设计:从SMTP收发原理到实现与避坑 简介这是一份基于 JavaMail 的电子邮件系统课程设计报告面向软件工程及相关专业学生、Java 邮件开发入门者适合课程设计、毕业设计选题或自学参考。内容系统梳理了 SMTP、POP3、IMAP 三种主流邮件协议的工作原理与适用场景详解 MIME 标准及 JavaMail API 核心类Session、Store、Folder、Message、Transport 等的用途并给出客户端登录、邮件发送与接收、邮件夹管理等功能的实现思路可帮助读者快速掌握 Java 邮件系统的设计与编码要点。资源共 1 个 PDF 文件压缩包大小 1.97MB内含完整课设报告结构、系统分析与类框图说明便于按章节查阅。已有 415 人学习浏览适合用于课程设计参考、毕设选题启发或 JavaMail 技术复习。1. 拿到“电子邮件系统设计(含源文件)”之后JavaMail 到底解决你的什么问题如果你手里是一份《基于javaMail电子邮件系统的设计(含源文件).pdf》那大概率不是来读文档的而是要把这份PDF变成能跑的代码。PDF里通常是一套标准课设结构需求分析、模块划分、流程图、数据库表和结论真正能落地的其实就两件事——用JavaMail把邮件发出去再把收件箱里的邮件读出来。这个方向最简单的价值在于你不必自己去实现SMTP协议、不用碰Socket、不用理解BASE64编码细节只需要调用JavaMail封装好的API几百行代码就能做出一个完整的邮件收发工具适合课设快速成型也适合企业内部做通知类小系统。我见过不少人卡在同一个地方PDF读得通源码也拿到了结果一运行要么报ClassNotFoundException要么邮箱配置抄得不对发不出去要么中文乱码最后怀疑是“版本不对”又开始到处找jar包。其实JavaMail的核心链路非常固定只要把Session、Transport、Store、MimeMessage这几个角色搞清楚再按正确顺序组装就能稳定跑通。这篇笔记会从对象模型讲到参数配置再给你一份可以直接改的收发代码最后把容易翻车的几个点都摊开说。适合零基础照着做也适合已经能发邮件但收信链路没写过的读者补全能力。2. 先把协议和对象模型理清JavaMail 的三个核心 API 和你必须选的通信方式2.1 Transport、Store、Session 各管一段收发两条链路是分开的JavaMail 的 API 设计比很多人想的要清晰发邮件和收邮件是两条独立链路各走各的入口。你在代码里构建一个Session对象它负责保存邮件服务器的地址、端口、账号密码、是否加密这些全局配置发送时用Transport连接SMTP服务器把消息推出去接收时用Store连接POP3或IMAP服务器再打开某个Folder去读邮件。很多新手把Store当成“收件箱对象”其实它只是负责建立连接真正的邮件还在Folder里。协议选择会直接影响代码写法和运维成本。常见的组合是这样用途协议默认端口加密端口特点发信SMTP25465SSL/ 587STARTTLS只负责提交邮件不管收件收信POP3110995SSL邮件下载到本地服务器不保留收信IMAP143993SSL邮件留在服务器支持多文件夹课设和小型内部工具我一般推荐“SMTP POP3”的组合发信用SMTP收信用POP3逻辑简单代码量最少。IMAP虽然状态同步更好但多文件夹、未读标记这些功能会把代码量撑大如果不是项目明确要求“多个文件夹同步”就没有必要上。如果服务器同时支持IMAP你甚至可以把读取和发送都写在同一份代码里但要注意连接和关闭的时机别把Store连接挂在全局否则长跑的服务会一直被占用端口。2.2 MimeMessage 不是一个字符串拼接工具头部、正文、附件都在同一个对象里发送邮件时最容易出问题的不是加密配置而是MimeMessage的使用姿势。MimeMessage不是一个“把文本拼一拼再发给服务器”的工具它是一个完整的RFC 822邮件对象模型主题、发件人、收件人、邮件头、正文、附件全部由它统一组织。当你添加附件时必须把正文和附件组装成Multipart结构再塞回MimeMessage里如果你只是setText(你好)然后直接writeTo()附件是写不进去的。核心结构是这样MimeMessage代表一封完整的邮件Multipart代表邮件内容里可以拆分的多个部分比如“正文HTML 一个PDF附件”MimeBodyPart代表其中某一个部分一段文本叫BodyPart一个附件也叫BodyPart我见过一个很典型的翻车写法把附件路径拼进正文比如“详见附件C:/temp/report.pdf”然后只调用了setText()。这种邮件发出去对方看不到任何附件因为附件根本不是通过文本路径传递的而是要把文件读成数据流包进MimeBodyPart。理解了这个对象模型后面写代码基本不会乱。这个设计思路也可以延伸到HTML正文正文部分设置text/html字符集和Content-Type附件部分设置application/octet-stream二者互不干扰。2.3 先想清楚你的系统是“发件为主”还是“收件为主”做设计之前建议先看一眼PDF里的功能列表判断它到底重点在发还是在收。大多数课设题目叫“电子邮件系统”需求通常是登录、写邮件、添加附件、发送、收件箱列表、阅读邮件。其中“收件箱列表”看起来简单实际坑最多因为要从Message对象里取出主题、发件人、日期、是否已读、正文类型有时候还需要处理HTML邮件和附件下载。如果题目对收件的界面要求很高开发重心应该放在解析Message上如果只是“能发出去、能看到发送成功”那收件链路可以精简成一段读取测试代码不用做成完整收件箱。我一般会先画两个方法一个是sendEmail(EmailMessage emailMessage)一个是receiveEmails(int maxCount)。前者负责组装和发送后者负责连接收件服务器并枚举邮件。这样无论PDF里的模块图怎么画代码结构都能跟功能一一对应。很多人把收发逻辑全部堆在Servlet里类几百行最后改参数要找半天这就是设计阶段没拆服务导致的。直接把Session隔离成一个配置方法收发各成一个服务类后面替换邮件服务器时只需要改配置文件或者改一个方法不需要动业务逻辑。3. 跑通最小可运行示例从 mail.properties 到一封带附件的邮件3.1 工程依赖与目录骨架mail.jar 不是丢进 lib 就完事先用一个最小的工程跑起来。无论你从PDF里解压出来的源码是什么结构我都建议自己按这个骨架整理一份因为旧源码经常把jar包散落在磁盘上ClassPath一乱就报NoClassDefFoundError。如果用的是Maven加入下面坐标即可dependency groupIdjavax.mail/groupId artifactIdmail/artifactId version1.4.7/version /dependency dependency groupIdjavax.activation/groupId artifactIdactivation/artifactId version1.1.1/version /dependency如果你不想用Maven而是手搓一个普通Java工程那要把mail.jar和activation.jar同时放进lib目录并且在IDE的Project Structure里把这两个jar加进ClassPath。mail 1.4.7版本离不开activation否则创建MimeBodyPart时可能提示找不到javax.activation.DataHandler。另外强调一下不要只复制jar不点“Add to Build Path”这在Eclipse里是一个经典操作遗漏。目录结构我习惯按下面这样组织mail-system/ ├── lib/ │ ├── mail.jar │ └── activation.jar ├── resources/ │ └── mail.properties └── src/ └── com/example/mail/ ├── config/MailConfig.java ├── service/MailSenderService.java └── Main.java这样分目录不是为了好看而是为了让“配置文件”和“源码”分离。以后换邮箱、改密码只动resources/mail.properties不重新编译代码。很多电子邮件的课设源码把账号密码直接写在类里提交到Git或者发给别人的时候顺手就把密码带上这种习惯在企业里是要出事的。顺便说一个以前的教训编译时出现“目标对于源文件过大”的提示多半不是源码本身太大而是你把源码和编译输出放在同一个目录里IDE扫描范围失控。分开源目录和输出目录之后这个问题基本消失。这和用C语言时报“无法打开源文件”一样根源都是目录结构混乱不是代码语法错。3.2 Properties 和 Authenticator 的 5 个必调参数新建resources/mail.properties把服务器参数独立出来。配置内容如下mail.smtp.hostsmtp.qq.com mail.smtp.port465 mail.smtp.authtrue mail.smtp.ssl.enabletrue mail.smtp.socketFactory.classjavax.net.ssl.SSLSocketFactory mail.smtp.socketFactory.fallbackfalse # 收件服务器参数 mail.store.protocolpop3 mail.pop3.hostpop.qq.com mail.pop3.port995 mail.pop3.ssl.enabletrue这里有5个参数是发信链路的必调项mail.smtp.host指定SMTP服务器域名mail.smtp.port指定端口mail.smtp.auth声明需要账号密码认证mail.smtp.ssl.enable开启SSL加密mail.smtp.socketFactory.class配合SSL使用。很多人漏掉socketFactory.fallbackfalse结果程序没报错但连接超时其实是自动切换到了非加密通道。如果你用163邮箱把smtp.qq.com换成smtp.163.compop.qq.com换成pop.163.com即可其余逻辑不需要动。注意QQ邮箱和网易邮箱现在都要求使用“授权码”而不是登录密码去连接SMTP这个授权码需要在网页端邮箱设置里开启SMTP服务后生成。把授权码配到mail.properties时不要在代码里打日志否则会泄露账号凭据。3.3 核心发送代码Session、MimeMessage、Transport 的执行顺序读取配置并创建Session然后构建邮件内容最后Transport.send()。这段代码是最小可运行版本package com.example.mail.config; import javax.mail.*; import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class MailConfig { private final Properties props; public MailConfig(String resourcePath) throws IOException { props new Properties(); try (InputStream in MailConfig.class.getClassLoader() .getResourceAsStream(resourcePath)) { if (in null) { throw new IOException(找不到配置文件: resourcePath); } props.load(in); } } public Session createSession(String username, String authCode) { // 带上账号信息让 Authenticator 在需要认证时自动返回凭据 return Session.getInstance(props, new Authenticator() { Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, authCode); } }); } public Properties getProps() { return props; } }这段代码的逻辑是Session.getInstance(props, authenticator)把配置和认证器绑定到一个会话上后续创建MimeMessage和Transport都会自动读取这些配置。Authenticator是JavaMail在连接服务器时回调用的不用自己手动在每次连接前设置用户名密码。关键在于createSession方法要接收两个参数——邮箱账号和授权码不要把授权码硬编码在类里。接下来是发送部分package com.example.mail.service; import com.example.mail.config.MailConfig; import javax.mail.*; import javax.mail.internet.*; import java.util.Properties; public class MailSenderService { private final MailConfig config; public MailSenderService(MailConfig config) { this.config config; } public void sendTextEmail(String from, String authCode, String to, String subject, String content) throws Exception { Session session config.createSession(from, authCode); 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); } }逻辑说明MimeMessage先设置发件人、收件人、主题、正文最后Transport.send()负责连接服务器并投递。setSubject(subject, UTF-8)里的第二个参数是字符集不写的话中文标题在部分邮箱里显示乱码。InternetAddress.parse(to)支持用逗号分隔的多个收件人地址会自动切割成地址数组比手动一个InternetAddress去setRecipient要省事。这条链路跑通后发邮件这个模块就彻底闭环了。3.4 用 465 与 587 的差别验证你的配置对不对如果连不上SMTP服务器最有用的排查手段就是确认端口和加密方式匹配。465端口是SSL加密配置里必须同时开启mail.smtp.ssl.enabletrue否则握手失败587端口是STARTTLS配置里要写mail.smtp.starttls.enabletrue不能开着SSL硬连。这两个端口对应两种不同的机制端口机制配置要点465SSL/TLS直接加密mail.smtp.ssl.enabletrue587STARTTLS升级加密mail.smtp.starttls.enabletrue如果还是加不上可以先用telnet smtp.qq.com 465验证一下端口通不通这一步能直接区分是网络问题还是JavaMail配置问题。但要注意465端口是SSL协议telnet命令行是明文状态你只需要看端口通不通不要真的尝试手写SMTP指令否则看到的返回码会误导你。端口通了代码连不上那就是证书或配置问题端口不通那就是公司网络出站策略限制了你访问外网SMTP服务器这时候无论改多少配置都无效不如找一个测试环境先跑通。4. 从最小示例到“系统”源文件的目录拆分和收件读取怎么补全4.1 系统里的“源文件”应该是这个结构工具类、服务类、入口分开最小示例跑通之后PDF里的“系统”其实还差两层入口界面和收件链路。先别急着写界面先把服务层代码和服务分类理清楚界面后接不接都行。我建议把源码按职责拆成4个部分config放配置service放发送与接收逻辑entity放邮件实体Main只负责调用演示。这个结构不会因为换界面而推倒重来。src/ └── com/example/mail/ ├── config/MailConfig.java ├── entity/EmailMessage.java ├── service/MailSenderService.java ├── service/MailReceiverService.java └── Main.javaentity/EmailMessage.java定义一个简单实体字段包括发件人、收件人、抄送、主题、正文、附件路径列表、附件名称列表。所有发送方法统一接收这个实体界面层只负责把表单数据填进去不直接接触JavaMail对象。这样设计的好处是如果之后要把“写邮件窗口”从Swing改成网页service层一行不动。就像用VS Studio写C语言时运行不了第二个源文件问题往往不是第二个文件语法错而是没有把入口函数和源文件一一对应清楚。Java也一样一个工程里出现两个public static void main(String[] args)IDE不知道你要跑哪个有时点了执行按钮还是上一个类。所以这里的Main只放一个入口程序其他类一律不允许再写main方法。4.2 增加收件链路Store、Folder 和 Message 转文本发信只是半套系统。现在补全收件链路逻辑是连接POP3服务器打开收件箱预览最近若干封邮件。代码可以直接拷贝package com.example.mail.service; import com.example.mail.config.MailConfig; import javax.mail.*; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Properties; public class MailReceiverService { private final MailConfig config; public MailReceiverService(MailConfig config) { this.config config; } public ListString receiveRecentEmails(String username, String authCode, int maxCount) throws Exception { Properties props config.getProps(); Session session Session.getInstance(props); Store store session.getStore(props.getProperty(mail.store.protocol)); store.connect(props.getProperty(mail.pop3.host), Integer.parseInt(props.getProperty(mail.pop3.port)), username, authCode); Folder folder store.getFolder(INBOX); folder.open(Folder.READ_ONLY); Message[] messages folder.getMessages(); int start Math.max(0, messages.length - maxCount); ListString result new ArrayList(); for (int i start; i messages.length; i) { MimeMessage msg (MimeMessage) messages[i]; result.add([ msg.getSentDate() ] msg.getSubject() 来自: msg.getFrom()[0].toString()); } folder.close(false); store.close(); return result; } }逻辑说明Store按配置文件里的协议这里是pop3创建连接connect传入服务器、端口、账号和授权码。getFolder(INBOX)拿收件箱open(Folder.READ_ONLY)以只读模式打开有些邮件服务器如果以READ_WRITE模式打开会直接改掉邮件标志容易被误认为“系统把邮件删了”。getMessages()虽然返回全部对象但这个操作在POP3里不会真的把每封邮件正文都下载下来它只是拿到消息ID列表真正读取正文和附件还要调用getContent()。所以这里先取主题和发件人是比较轻量的。如果你需要显示正文再对每个msg单独处理。常见做法是判断内容是否属于Multipart类型是的话再遍历每个BodyPart如果是纯文本直接msg.getContent().toString()即可。这一步就是“阅读邮件”功能的全部逻辑不复杂但类型判断不能省否则HTML邮件会打印出一堆标签源码。4.3 多收件人、密送、抄送与 HTML 正文的写法PDF里如果要求支持“抄送”和“密送”代码只需要增加两个setRecipients调用。注意类型message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to)); message.setRecipients(Message.RecipientType.CC, InternetAddress.parse(cc)); message.setRecipients(Message.RecipientType.BCC, InternetAddress.parse(bcc));参数说明TO是正常收件人CC是抄送所有收件人都能看到抄送列表BCC是密送收件人之间互相不可见。这里最容易翻车的一个点是有些人图省事把多个收件人地址直接用逗号拼成字符串然后调用setRecipient单个类型这样在解析地址时有时能通过但BCC会暴露地址部分邮件服务商也会对格式不规范的邮件拒收。正确做法永远是用InternetAddress.parse去解析逗号分隔的字符串。HTML正文的写法与纯文本略有不同需要显式设置MimeBodyPart的ContentTypeMimeBodyPart htmlPart new MimeBodyPart(); htmlPart.setContent(h2你好/h2p这是HTML正文/p, text/html; charsetUTF-8);setContent的第一个参数是内容字符串第二个参数是ContentType。如果你直接在纯文本邮件里塞一堆br标签显示出来就是一坨标签源码。另外HTML邮件里嵌入图片也不能用普通的img srcC:/路径外部图片地址在多数邮件服务商那里会被拦截必须把图片作为MimeBodyPart加进Multipart并设置Content-ID再用img srccid:image001引用。课设做到这个程度基本算是加分项但要注意代码量会膨胀如果时间紧可以只做文本超链接。4.4 把 PDF 里的功能列表映射到源码包一个最简功能清单很多人的问题不是不会写某个API而是不知道怎么把PDF里的功能需求对应到类和方法上。下面这个表格是我自己整理的一套映射关系直接照着安排开发顺序即可PDF里的功能点对应源文件位置使用的JavaMail API登录验证MailConfig.createSession()Authenticator写邮件EmailMessage.java纯实体类不涉及API发送邮件MailSenderService.sendEmail()MimeMessage, Transport添加附件MailSenderService.addAttachment()MimeBodyPart, Multipart支持HTMLMailSenderService.sendHtmlEmail()setContent(text/html)收件箱列表MailReceiverService.receiveRecentEmails()Store, Folder, Message阅读正文MailReceiverService.parseContent()Multipart, BodyPart保存附件MailReceiverService.saveAttachment()BodyPart.getInputStream()这份清单也可以当成进度表先把“发送邮件”跑通再做“附件”再做“HTML”最后补收件每完成一项就是一个可演示的里程碑。PDF里如果还写到了数据库表通常是存已发送邮件记录或收件人流水这部分业务与JavaMail无关表结构按PDF设计即可不要反过来让JavaMail迁就数据库。如果遇到“C语言无法打开源文件怎么解决”那种情况——意思是IDE找不到源文件路径通常是工程导入时根目录选错了。从压缩包解开后应该选pom.xml所在目录或src的上一级作为工程根目录不要把src本身当成工程根目录否则包名和目录层级会错位。5. 避坑与常见问题让发不出去和收不到的邮件“开口说话”5.1 SMTP 连不上SSL 端口和 TLS 开关一起出问题现象程序抛出MessagingException: Could not connect to SMTP host或者一直卡到超时。原因一般是端口与加密机制不匹配最常见的是用了465端口却只设置了mail.smtp.port465没有打开mail.smtp.ssl.enabletrue或者反过来用了587端口却打开了ssl.enable没有打开starttls.enable。还有一个隐蔽问题公司内网或部分服务器防火墙会拦截常用SMTP端口很多云主机默认只放行80和443需要单独在安全组里放行465或587。解决办法是先看异常栈第二行如果停留在socket connect阶段基本是网络不通如果进入SSL handshake阶段才是配置问题。网络层面的问题通过telnet 服务器域名 端口最快定位。5.2 中文主题和发件人乱码编码要单独设置现象收到的邮件主题是一串?UTF-8?B?...?或者发件人名字显示成乱码。原因是用setSubject(subject)没带字符集JavaMail用了平台默认编码去编码非ASCII字符收到的邮箱再按照自己的默认编码解码就撞出一堆乱码。解决方法是统一在设置时指定字符集setSubject(subject, UTF-8)setText(content, UTF-8)发件人地址里的中文名用new InternetAddress(from, 名字, UTF-8)。另一个常见点InternetAddress构造器不传字符集会按系统编码处理Windows系统里常变成GBKLinux环境变成UTF-8同一个工程在两种环境发出的邮件行为不一致这个坑属于典型的“本地好的一上线就乱”。5.3 JDK 9 以上找不到 javax.mail 包JavaSE 里已经没有它了现象直接报java.lang.ClassNotFoundException: javax.mail.internet.MimeMessage。原因JavaMail从JDK 9开始被移出JavaSE标准库不再随JDK分发必须显式引入依赖。如果是Maven工程确认上面那组依赖坐标已经在pom.xml里如果是不用Maven的裸Java工程要检查lib目录下的jar包是否真的加入了ModulePath或ClassPath。有人把jar包下载到桌面又在IDE里手动指定了源码路径却忘了加库依赖看起来源码自带编译却通不过和配置Linux的yum源文件配置时“源配了但没生效”的情况很像本质是环境没重新加载。换成Maven后执行一次mvn clean compile这个问题通常一次就能暴露。5.4 发出去对方收不到被拒收或进垃圾箱代码没有异常现象Transport.send()正常返回代码没有任何报错但测试邮箱收不到或者只能在垃圾箱里找到。原因有三个方向第一发件人域名或邮箱没有SPF记录收件服务器判定伪造第二发送频率过高触发对方限流第三邮件内容里出现过强的营销词。解决办法是先用同一账号给自己发一封纯文本测试邮件确认基础链路没问题再检查邮箱设置里是否开启了SMTP服务的“允许此应用发送邮件”授权如果发给QQ邮箱被秒退常见报错是被服务器判定为垃圾。此时看一下被退回邮件里的错误码比如554 DT:SPM表示垃圾邮件拒绝需要降低发送频率并优化内容。很多老工程师不建议用个人刚注册的测试邮箱直接连发几十封做压测很容易让整个IP段进黑名单。5.5 用收信侧验证发信结果把测试邮件发给自己再拉一遍现象产品验收时需要“发出去并且能看到回执”但是测试环境用的是本地网络同事收不到又不想单独搭一套测试邮箱。解决办法是把发件人收件人都填成同一个测试账号发送后立即用MailReceiverService拉取最近一封邮件对比主题和发件人是否一致。这一步同时验证了发件和收件两条链路而且不依赖外部网络。要注意的是邮件服务器投递有延迟QQ邮箱和163邮箱一般在1~3秒内到位但内部自建邮件服务器有时会延迟几十秒所以验证代码里最好轮询等待几秒再拉取不要发完立刻读。如果拉取时是空的不一定代表发件失败也可能是POP3协议把上次拉取过的邮件标记成已下载换了协议或重新登录后才恢复。这一条算是血泪经验以前我总以为是代码问题后来才发现是测试策略太急。5.6 Message 读取时类型判断必须分清文本和 Multipart现象读取邮件正文时抛IOException: Unable to load BODYSTRUCTURE或者打印出来是一堆乱码。原因是对Message调用getContent()前没有判断内容类型直接强转成String遇到Multipart就崩了。解决办法是写一个解析方法Object content msg.getContent(); if (content instanceof Multipart) { Multipart multipart (Multipart) content; for (int i 0; i multipart.getCount(); i) { BodyPart part multipart.getBodyPart(i); String disposition part.getDisposition(); if (Part.ATTACHMENT.equalsIgnoreCase(disposition)) { // 这里是附件调用 part.getInputStream() 保存 } else { // 这是正文部分part.getContent().toString() } } } else { // 纯文本邮件直接转字符串 return content.toString(); }有一个很关键的细节有些带附件的邮件正文部分是text/html但Multipart里还有一个text/plain的降级版本。如果你遍历所有BodyPart并把第一个文本都当真正文可能会把纯文本和HTML都打印出来。正确做法是优先取text/plain没有再取text/html。另外附件如果没有显式setDisposition(Part.ATTACHMENT)系统可能把它当成INLINE内嵌资源这时拿getFileName()拿到的名字也可能是空的需要通过part.getContentType()判断内容类型来兜底。6. 验证与收尾用一条命令自测一封邮件从发出到收到的完整链路这里给你一个最有用的自测习惯把发送和接收写进一个测试类一条命令跑完整个链路。不要等界面做完再点按钮验证那样出了问题很难定位是邮件配置问题还是界面拼接问题。可以直接在Main里写一个带main的验证流程public class Main { public static void main(String[] args) throws Exception { MailConfig config new MailConfig(mail.properties); MailSenderService sender new MailSenderService(config); sender.sendTextEmail(your_accountqq.com, 授权码, your_accountqq.com, 链路自测 System.currentTimeMillis(), 如果你收到这封邮件说明SMTP发送配置正确。); Thread.sleep(3000); MailReceiverService receiver new MailReceiverService(config); ListString emails receiver.receiveRecentEmails( your_accountqq.com, 授权码, 3); emails.forEach(System.out::println); } }验证完成后检查三个点第一发送过程没有异常控制台没有MessagingException第二收件列表里出现了刚才发送的主题前缀“链路自测”第三主题不是乱码、发件人显示正确。三点全过说明从SMTP发送到POP3接收的完整链路已经通剩下的界面工作都是在这个链路上做包装。我自己维护类似项目时还会多做一个动作把mail.properties复制成mail.properties.template提交到代码库真正的配置文件留在本地并被.gitignore忽略。这样团队成员拿到代码后只需要复制模板、填授权码、改服务器域名即可运行不会把个人账号密码带到公共仓库里。另外jar包版本我会固定写死不写mail:1.4.7-SNAPSHOT之类的动态版本号邮件这种老牌依赖升级频率很低固定版本能避免其他人换依赖时连带出编码或SSL行为差异。如果你打算把这个课设继续做成简历项目建议再往两个方向延伸一下一是把MailSenderService的发送方法增加返回SendStatus成功、失败、超时而不是只抛异常这样业务层能对用户做友好提示二是把收件拉取改成定时任务每隔一段时间自动同步一次未读邮件。这两种改造都基于已有的MailSenderService和MailReceiverService不需要改协议也不需要引入新的库。做邮件系统最大的价值不是把邮件的API背得多熟而是把“配置、连接、组装、发送、接收、排错”这条链路跑扎实这套经验换到任何Java后端项目里都通用。希望帮到你。本文还有配套的精品资源点击获取
返回列表