
我最早用JSP写邮件发送功能还是在学生时代做毕业设计那会儿。当时选了个“企业信息管理系统”的题目系统里需要一个“忘记密码后邮件重置”的小模块本以为就是几分钟的事结果在SMTP服务器配置、中文乱码、附件处理这些细节上整整折腾了两天。后来工作了接手的项目里依然不断有人用JSP写邮件发送踩的坑跟我当年几乎一模一样。这篇博文就把我这些年积累的JSP发送邮件经验一次性讲透涵盖设计思路、完整代码、参数配置和常见报错排查适合正在做课程设计、毕业设计或者在老项目中需要临时补一个邮件功能的Java Web开发者参考。只要你手里有一个可用的邮箱账号跟着文章一步步来基本都能跑通。1. 项目思路与方案选型为什么用JavaMail而不是其他方式JSP本身只是一个视图层技术它不具备直接发送邮件的底层能力所以凡是“JSP发送邮件”的需求本质上都是借助后端Java代码来完成。这里就成了第一个分岔路口用什么技术发邮件JavaWeb领域常见的选择有这么几种我按实际使用频率排个序JavaMail API也就是现在的Jakarta Mail是官方标准方案资料多、配置灵活、可控性强Spring的JavaMailSender封装适合用了Spring框架的项目代码更简洁Apache Commons Email在JavaMail之上又包了一层API更友好但灵活性略差直接调用第三方HTTP接口比如SendCloud、Brevo这类邮件服务商的REST API适合需要高送达率、要统计打开率的商业场景我的建议是如果你做的就是一个功能相对独立的JSP/Servlet传统项目或者想彻底搞懂邮件发送的原理用JavaMail API最合适。原因有三个它不依赖任何框架一个javax.mail.jar包就能跑对老项目最友好。JavaMail API暴露的底层概念Session、Transport、Message、Multipart本身就是邮件协议的工作模型学一遍之后换任何语言和框架都能举一反三。排查问题时可以直接拿JavaMail API的报错信息去搜索命中率极高反而是封装层次较多的方案一旦报错异常栈里全是封装类定位问题反而费劲。1.1 JSP发邮件的核心流程拆解不管代码怎么写一次邮件发送的完整链条是固定的。我习惯用“去邮局寄包裹”来理解这条链路Session相当于邮局的大厅它保存了连接邮件服务器需要的所有配置信息服务器地址、端口、是否需要认证等。大厅本身不干活但你干任何事都得先到大厅里。Message就是要寄的信封和信纸对应邮件主题、发件人、收件人、正文内容。Transport是邮局的运输车它负责把信从你的程序运到SMTP服务器。transport.send(message)这一步就是真正“发出去”的动作。SMTP服务器是收件的邮局分拣中心它验证你的身份用户名密码确认可以代发再把信路由到收件人所在的邮件服务器。收件人的邮件服务器最后把信投递到收件人的收件箱。从这个流程能看得出来JSP代码只负责前三段后面的事情我们看不到也控制不了。所以很多新手会困惑“明明代码返回发送成功为什么对方收不到”——因为send()方法返回成功只能说明你的邮件被你的SMTP服务器接收了后续的路由和投递是另一套体系在管。1.2 选型时的几个关键判断点做技术方案选型不能只盯着“能不能发出来”还要考虑后面这几个问题第一个是项目里有没有Spring。如果项目本身就是Spring Boot再手撸JavaMail API就有点绕了直接用JavaMailSenderImpl注入一个Bean配置都写进application.yml代码量能少三分之一。但如果是老旧的纯JSPServlet项目硬塞Spring进来就属于为了发封邮件大动干戈风险远大于收益。第二个是邮件的重要程度。内部系统告警邮件、验证码邮件对送达率要求没那么苛刻用SMTP协议免费发完全够用。但如果是营销邮件、注册确认邮件这直接关系到用户转化率那我强烈建议用第三方邮件服务商他们提供的DKIM签名、SPF记录配置、垃圾邮件评分优化是自建SMTP很难做好的。第三个是发件量级。每天发几十封和每天发几万封技术选型完全不同。前者随便一个免费邮箱的SMTP就能扛住后者要考虑发信频率限制、IP信誉度、退信处理这就超出了普通JavaWeb开发者的日常范围应该找专门的解决方案。2. 核心前置知识SMTP协议和JavaMail配置参数很多人写邮件发送代码上来就找“完整代码”复制粘贴跑不通就到处问。我见过太多这种情况根子在于不理解配置参数的含义。其实SMTP这块知识量不大把几个核心概念吃透代码不用背也能写出来。2.1 SMTP服务器地址和端口怎么选SMTP服务器地址一般由你的邮箱服务商决定QQ邮箱smtp.qq.com163邮箱smtp.163.com126邮箱smtp.126.com阿里云企业邮箱smtp.mxhichina.comGmailsmtp.gmail.comOutlooksmtp-mail.outlook.com端口这块很多初学者容易搞混。SMTP协议的标准端口是25但25端口在现在的网络环境下经常被运营商和云服务商屏蔽所以实际开发中不太推荐。安全起见优先用SSL加密端口465或者STARTTLS端口587。我的经验是哪怕你的邮件服务商支持25端口也不要直接用。很多服务器把25端口封得死死的本地能发、部署到云服务器上就超时排查起来特别浪费时间。换465或587基本不会遇到这种问题。QQ邮箱的配置示例这是我用过最多的测试邮箱mail.smtp.hostsmtp.qq.com mail.smtp.port465 mail.smtp.authtrue mail.smtp.ssl.enabletrue mail.smtp.socketFactory.classjavax.net.ssl.SSLSocketFactory163邮箱则要注意它默认可能不开放SMTP服务需要登录网页版邮箱在设置里手动开启“SMTP服务”并且获取一个独立授权码。这个授权码就是下面要说的密码。2.2 口令认证password到底填什么这是“JSP发送邮件”里最容易踩的坑没有之一。很多人在配置里老老实实填了自己的邮箱登录密码结果报AuthenticationFailedException然后开始怀疑代码写错了。实际上目前主流的免费邮箱服务商出于安全考虑都不允许第三方程序直接用登录密码进行SMTP认证。你需要做的事是登录邮箱网页版进入设置。找到“账户”或“客户端设置”相关选项。开启SMTP服务按照引导生成一个授权码。在JavaMail配置中password这个字段填的不是登录密码而是这个授权码。QQ邮箱的授权码是一串16位的字母组合生成之后只显示一次一定记得备份。163邮箱的授权码则是自己设置的一串专用密码与登录密码相互独立。提示如果你用的是公司自建的企业邮箱经常不需要授权码直接填AD域密码或者企业邮箱密码就能认证。这块没有统一标准最靠谱的办法是问公司的IT管理员或者去邮箱帮助中心查“SMTP授权码”相关的说明。2.3 JavaMail依赖和版本选择使用JavaMail API需要引入依赖。这里有个容易踩的版本坑甲骨文在JDK 9之后把JavaMail从JDK中移除了同时JavaMail项目也迁移到了Eclipse基金会改名为Jakarta Mail。如果你用的是JDK 8及以下的老环境用javax.mail坐标dependency groupIdcom.sun.mail/groupId artifactIdjavax.mail/artifactId version1.6.2/version /dependency如果你用的是JDK 9及以上并且项目是纯Servlet/JSP没有Spring Boot的依赖管理建议还是用javax.mail这个坐标。因为老版本的javax.mail在JDK 9上依然能跑而且网上资料最多遇到问题搜起来方便。如果你用的是Spring Boot 2.7或Spring Boot 3.x那就直接用Spring Boot自带的spring-boot-starter-mail它内部管理的Jakarta Mail版本会和你的Spring Boot版本自动匹配不用担心版本冲突。2.4 常见配置项速查表我把常用的配置项整理成一张表方便你对照检查配置项作用推荐值mail.smtp.hostSMTP服务器地址取决于邮件服务商mail.smtp.port端口465SSL或587STARTTLSmail.smtp.auth是否启用身份认证truemail.smtp.ssl.enable是否启用SSL加密truemail.smtp.timeout连接超时毫秒5000mail.smtp.connectiontimeout建立连接超时毫秒5000mail.smtp.writetimeout写入超时毫秒5000mail.smtp.socketFactory.classsocket工厂类javax.net.ssl.SSLSocketFactorySSL场景mail.debug是否打印调试日志true排查问题时这里有个实用小经验mail.debugtrue这个配置特别重要在开发阶段一定要打开。它会把SMTP会话的完整交互过程打印到控制台比如服务器返回的250表示成功、535表示认证失败等。报错的时候控制台里那些以数字开头的行才是真正有用的信息。3. 完整实现从普通文本邮件到带附件和HTML内容的邮件基础概念理清楚了下面进入实战环节。我从最简单的纯文本邮件开始逐步加功能最后给出一个在JSP页面中能直接调用的完整示例。3.1 环境准备和项目结构在动手编码前先确认环境JDK 1.8以上Tomcat 8.5以上Servlet 3.1以上即可一个支持SMTP的邮箱账号本文以QQ邮箱为例javax.mail依赖包如果是非Maven项目直接把javax.mail-1.6.2.jar放到WEB-INF/lib下即可如果你用的是非Maven的传统Web项目还需要注意另一个容易忽略的点JavaMail依赖Activator。早期版本的JavaMail在使用时需要配套activation.jarJDK 8及以前这个包是内置的但JDK 9就把JAF模块从JDK中移除了运行时会报ClassNotFoundException: javax.activation.DataHandler。解决方法是额外引入dependency groupIdcom.sun.activation/groupId artifactIdjavax.activation/artifactId version1.2.0/version /dependency这个坑我在用JDK 11跑老项目时踩过报错信息看起来莫名其妙但实际上就是少了一个依赖包。3.2 第一步写一个工具类封装发送逻辑我不建议把发邮件的逻辑直接写在JSP页面里页面里全是Java代码不仅丑维护起来也痛苦。正确做法是写一个独立的Java工具类JSP或者Servlet只负责调用。下面是一个完整的邮件发送工具类支持文本邮件和HTML邮件package com.example.util; import java.util.Properties; import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; public class MailUtil { // 配置邮箱信息 private static final String SMTP_HOST smtp.qq.com; private static final String SMTP_PORT 465; private static final String FROM_EMAIL 你的邮箱qq.com; private static final String AUTH_CODE 你的授权码; public static void sendTextEmail(String toEmail, String subject, String content) throws MessagingException { Properties props new Properties(); props.put(mail.smtp.host, SMTP_HOST); props.put(mail.smtp.port, SMTP_PORT); props.put(mail.smtp.auth, true); props.put(mail.smtp.ssl.enable, true); props.put(mail.smtp.socketFactory.class, javax.net.ssl.SSLSocketFactory); props.put(mail.debug, true); Session session Session.getInstance(props, new Authenticator() { Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(FROM_EMAIL, AUTH_CODE); } }); MimeMessage message new MimeMessage(session); message.setFrom(new InternetAddress(FROM_EMAIL)); message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail)); message.setSubject(subject, UTF-8); message.setText(content, UTF-8); Transport.send(message); } public static void sendHtmlEmail(String toEmail, String subject, String htmlContent) throws MessagingException { Properties props new Properties(); props.put(mail.smtp.host, SMTP_HOST); props.put(mail.smtp.port, SMTP_PORT); props.put(mail.smtp.auth, true); props.put(mail.smtp.ssl.enable, true); props.put(mail.smtp.socketFactory.class, javax.net.ssl.SSLSocketFactory); props.put(mail.debug, true); Session session Session.getInstance(props, new Authenticator() { Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(FROM_EMAIL, AUTH_CODE); } }); MimeMessage message new MimeMessage(session); message.setFrom(new InternetAddress(FROM_EMAIL)); message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail)); message.setSubject(subject, UTF-8); message.setContent(htmlContent, text/html; charsetUTF-8); Transport.send(message); } }这段代码里有两个细节值得注意第一个是message.setSubject(subject, UTF-8)和message.setText(content, UTF-8)。JSP页面默认编码如果是ISO-8859-1中文邮件标题和正文很容易乱码。显式指定UTF-8编码可以从源头解决这个问题。如果你在JSP页面里调用时还应当在JSP顶部用% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 %声明页面编码前后端编码保持一致。第二个是message.setContent(htmlContent, text/html; charsetUTF-8)。这就是HTML邮件与普通文本邮件的区别所在Content-Type从text/plain变成了text/html。邮件客户端收到后就会按HTML去渲染你可以在正文里放图片、链接、表格等富内容。3.3 第二步在JSP中调用并显示发送结果工具类写好后在JSP页面里的调用非常简单% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 % % page importcom.example.util.MailUtil % !DOCTYPE html html head meta charsetUTF-8 titleJSP发送邮件示例/title /head body h2发送测试邮件/h2 % request.setCharacterEncoding(UTF-8); String toEmail request.getParameter(toEmail); String resultMsg ; if (toEmail ! null !.equals(toEmail.trim())) { try { String subject request.getParameter(subject); String content request.getParameter(content); MailUtil.sendTextEmail(toEmail, subject, content); resultMsg 邮件发送成功; } catch (Exception e) { e.printStackTrace(); resultMsg 邮件发送失败 e.getMessage(); } } % form methodpost 收件人邮箱input typetext nametoEmail size40brbr 邮件主题input typetext namesubject size40brbr 邮件内容textarea namecontent rows6 cols50/textareabrbr input typesubmit value发送邮件 /form p% resultMsg %/p /body /html这里有一个非常常见的坑当你的JSP页面包含中文表单内容并通过POST提交时如果不在接收参数前调用request.setCharacterEncoding(UTF-8)取出来的中文就是乱码。这是因为Tomcat 8默认的POST编码是UTF-8但你无法保证每个版本的容器行为一致。显式设置一次能避免很多无谓的排查时间。3.4 进阶带附件的邮件核心代码接下来解决项目中更常见的需求——发送带附件的邮件。附件邮件的MIME结构比纯文本邮件复杂一些它使用MimeMultipart来组织多个部分正文和附件是相互独立的MIME节点。public static void sendEmailWithAttachment(String toEmail, String subject, String content, File attachment) throws MessagingException, IOException { Properties props new Properties(); props.put(mail.smtp.host, SMTP_HOST); props.put(mail.smtp.port, SMTP_PORT); props.put(mail.smtp.auth, true); props.put(mail.smtp.ssl.enable, true); props.put(mail.smtp.socketFactory.class, javax.net.ssl.SSLSocketFactory); Session session Session.getInstance(props, new Authenticator() { Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(FROM_EMAIL, AUTH_CODE); } }); MimeMessage message new MimeMessage(session); message.setFrom(new InternetAddress(FROM_EMAIL)); message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail)); message.setSubject(subject, UTF-8); // 创建混合类型的Multipart MimeMultipart multipart new MimeMultipart(); // 添加正文 MimeBodyPart textPart new MimeBodyPart(); textPart.setText(content, UTF-8); // 添加附件 MimeBodyPart attachmentPart new MimeBodyPart(); attachmentPart.attachFile(attachment); // 设置附件的Content-ID部分邮件客户端内联图片需要用到 // attachmentPart.setHeader(Content-ID, image1); multipart.addBodyPart(textPart); multipart.addBodyPart(attachmentPart); // 将Multipart设置为邮件内容 message.setContent(multipart); Transport.send(message); }这段代码用到的MimeBodyPart相当于邮件这个“集装箱”里一个个独立的包装箱每个包装箱可以只装正文也可以只装附件。attachFile(File)方法会自动识别文件类型并设置对应的Content-Type和文件名中文文件名也能正确处理。如果你的需求是发送多个附件循环调用addBodyPart逐个添加即可。唯一的注意点是不要在一个MimeBodyPart里既放正文又放附件这样会导致客户端解析异常。3.5 实际操作记录我用一个注册欢迎邮件的例子验证我拿一个典型的“用户注册成功发送欢迎邮件”场景做了完整验证。场景需求是用户注册后系统给用户发送一封HTML格式的欢迎邮件邮件里包含用户昵称和一段激活链接。实现的调用代码String html htmlbody h3欢迎 nickname 注册我们的系统/h3 p请点击以下链接激活账号/p a href activateUrl 激活账号/a /body/html; MailUtil.sendHtmlEmail(userEmail, 注册确认邮件, html);在实际验证时我发现HTML邮件有一个容易被忽视的细节很多邮件客户端尤其是QQ邮箱、网易邮箱出于安全考虑会默认屏蔽邮件中的外部图片和脚本只显示纯文本。styledisplay:block这种写法的图片在邮件里经常不生效。所以在设计HTML邮件时尽量不要依赖外部图片重要内容要用文字表达需要放图片就作为附件嵌入不要用外链。此外我还测试了收件人格式的兼容性。如果收件人填写的是多个邮箱地址用逗号分隔InternetAddress.parse方法能一次性解析出多个地址。但如果你用了message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(aqq.com,b163.com))你会发现QQ邮箱和163邮箱都能正常收到。这里不需要额外处理。4. 常见报错与问题排查实录这一节是文章的精华。我干这行这些年见过太多的JSP邮件问题下面这几种是出现频率最高的我把典型的报错信息、排查思路和解决方案列成一份速查表。4.1 各类报错原因与解决办法速查表报错现象可能原因解决方法AuthenticationFailedException用户名密码错误填了邮箱登录密码而不是授权码账号未开启SMTP服务确认使用授权码登录邮箱网页版开启SMTP服务Could not connect to SMTP host端口被防火墙屏蔽邮箱服务商拒绝连接服务器禁止外发换465或587端口本地测试排除网络原因检查云服务器安全组出方向规则SSLHandshakeExceptionSSL相关配置错误JDK版本与SSL协议不兼容检查mail.smtp.ssl.enable配置更新JDK小版本javax.mail.SendFailedException: Invalid Addresses收件人邮箱格式错误收件人地址不存在检查收件人地址确认InternetAddress.parse解析结果中文乱码编码未统一在setSubject、setText方法中显式指定UTF-8确保JSP页面编码一致ClassNotFoundException: javax.activation.DataHandlerJDK 9缺少JAF依赖引入javax.activation的Maven坐标550 Mailbox not found收件人邮箱不存在收件人地址域名被拒收确认收件人邮箱拼写正确部分邮件服务商对陌生域名的邮件有严格反垃圾策略发送成功却收不到邮件进入了垃圾箱被邮件服务商延迟服务器IP信誉度低检查垃圾箱设置SPF/DKIM记录联系收件方服务商确认这里重点说一下热词里频繁出现的两个问题。第一个是mailbox name not allowed. the server response was: auth。这个报错本质上就是SMTP服务器返回的认证失败信息通常发生在客户端发AUTH命令之后。出现这个报错时优先去确认密码是否正确以及account是否有SMTP发送权限。还有一个小概率的坑某些服务器要求使用mail.smtp.auth.login.disabletrue或特定的auth机制但正常情况下把账号密码授权码改对这个报错就能解决。第二个是authentication failed; nested exception is javax.mail.AuthenticationFailedException。这个其实就是我在4.1表里列的第一条重点说两遍都不嫌多一定要确认用的是授权码不是登录密码。尤其是QQ邮箱和163邮箱你在代码里填了登录密码100%会报这个错。创建授权码时注意QQ邮箱会限制授权码的使用范围如果你的授权码之前已经绑定过其他客户端直接复用在JavaMail上通常会失败需要重新生成一个新的。4.2 一个真实问题的排查过程有次帮一个读者排查问题他的Tomcat部署在Linux服务器上邮箱用的是公司自建的企业邮箱。现象是在本地Windows环境跑完全正常部署到Linux服务器后超时失败。我让他把mail.debug打开看控制台日志。日志里发现连接SMTP服务器时卡住了一直没收到服务器的任何响应。这就能推断出是网络层面的问题而不是代码问题。排查步骤是在Linux服务器上用telnet smtp服务器地址 465测试端口连通性结果直接拒绝连接。检查防火墙状态和端口放行策略发现服务器出方向规则没有放行465端口。联系运维在安全组中放行465端口重启Tomcat后问题解决。这个案例说明一个很重要的排查思维当代码在本地能跑、在服务器上不能跑时优先检查环境差异而不是反复检查代码。网络策略、DNS解析、防火墙、证书信任库这些才是最大的变量。4.3 异步化与阻塞问题JSP发送邮件还有一个经常被忽略的性能问题邮件发送是IO操作如果直接同步执行一次发送可能耗时1~3秒在并发高的时候这一两秒就是灾难。典型场景是用户在注册页面点击提交JSP页面同步等待邮件发送完成才返回响应。遇到SMTP服务器响应慢或者网络抖动整个流程卡住用户会以为页面挂了。我的建议是把邮件发送从业务主流程中拆出去用一个独立的线程池处理。在Servlet 3.0环境中可以用WebServlet(asyncSupported true)配合AsyncContext也可以简单地用ExecutorServiceprivate static final ExecutorService EMAIL_POOL Executors.newFixedThreadPool(5); public static void sendEmailAsync(final String toEmail, final String subject, final String content) { EMAIL_POOL.submit(new Runnable() { Override public void run() { try { sendTextEmail(toEmail, subject, content); } catch (MessagingException e) { e.printStackTrace(); // 这里建议记录日志不要吞掉异常 } } }); }这样用户在页面上提交之后请求立即返回“邮件发送中请稍后查收”真正发送的工作在后台线程完成。如果担心线程池的方式在应用重启时丢失邮件任务你也可以引入消息队列来做异步化那属于大型系统的范畴了这里点到为止。4.4 部署环境中的三个隐藏问题最后再补充三个部署环境里很容易被忽视的隐藏问题。第一个是DNS解析。有些服务器上代码里的SMTP主机名解析不到IPv6地址或者DNS服务器本身有问题导致连接超时。遇到这种情况可以尝试直接把mail.smtp.host配置成SMTP服务器的IP地址绕过DNS解析但这种方式不推荐因为IP可能变化。第二个是SSL证书校验。Java运行时会校验SMTP服务器的SSL证书如果你的SMTP服务器证书链不完整或者用了自签名证书会直接报PKIX path building failed。解决办法是要么让IT把完整的证书链配置好要么在Java代码中信任该证书但后者存在安全风险生产环境不建议。第三个是服务器时间。这个是我最近才碰到的新坑JavaMail的SSL握手协议会校验证书有效期如果服务器系统时间不正确比如快了几个小时会导致本应有效的证书被判定为“尚未生效”或“已过期”从而握手失败。排查了一下午最后发现服务器时间被重置到了一个月前。记得用date命令确认服务器时间准确性。5. JSP项目中邮件功能的几个扩展方向邮件功能做出来后往往不会止步于“发送一封文本邮件”。结合项目的实际需求有几个扩展方向很常见这里简单说说我的处理思路。5.1 邮件模板化实际业务中邮件的正文很少是一成不变的。比如“密码重置邮件”、“订单通知邮件”、“活动促销邮件”内容各不相同但整体排版风格一致。如果每次都在Java代码里拼接HTML字符串维护起来非常痛苦。我的做法是用模板文件配合占位符替换。最简单的方案是在项目里放几个.html模板文件用String.replace把${name}、${link}这样的占位符替换成实际值。复杂一点可以用FreeMarker或者Thymeleaf这类模板引擎但对JSP老项目来说引入模板引擎的成本可能比收益还大用占位符替换方案就够了。5.2 邮件发送日志记录与失败重试邮件发送只是业务系统里的一个动作真正重要的是“知道哪些邮件发出去了哪些失败了”。建议在邮件发送工具类里统一记录日志日志至少包含以下字段发送时间、发件人、收件人、主题、发送状态、失败原因、耗时。如果邮件的时效性要求高可以支持失败重试重试时退避一下比如第一次失败后等30秒再试避免频繁重试给SMTP服务器造成压力。5.3 定时邮件报表有不少管理系统的需求是每天定时把统计数据发到指定邮箱这也是一个典型的JSP/JavaWeb场景。实现思路不难写一个Servlet或者普通Java类负责查询数据、生成HTML表格、调用邮件工具类发送再用java.util.Timer或者Quartz定时触发。导出Excel再作为附件发送也是同样的套路先通过POI生成Excel文件到临时目录发送完邮件后再删除临时文件。这个方向有个细节值得提一下生成Excel文件到服务器本地时注意临时文件的清理。如果定时任务每天跑一次一年就在服务器上积累了三百多个临时文件磁盘总会满的。要么在发送后立即删除要么定义一个固定目录定期清理。6. 一些经验之谈代码写到这里邮件发送的基础功能已经齐了。最后聊几点我在实际项目中总结的经验不一定写在哪本教科书里但确实能帮你少走弯路。邮件发送这件事代码本身不难难的是把它放到项目中能稳定运行。稳定性靠什么靠日志、超时、重试、监控这四个要素。日志的重要性前面说过了开发时打开mail.debug生产环境记得关掉把关键信息用log4j或slf4j记录到独立的邮件日志文件里。超时配置也务必设置否则SMTP服务器没有响应时你的线程会一直挂在那里拖垮整个应用的线程池。还有一点是不要把邮箱账号密码硬编码在代码里。至少放到.properties配置文件中从外部加载。真要上了生产环境用环境变量或者配置中心管理凭证是更稳妥的做法。不要觉得这是小事我见过不止一个项目因为代码仓库泄露导致内部邮箱被垃圾邮件刷爆。关于文件上传临时路径如果你在Web应用里做“用户选择Excel文件上传然后邮件发送给指定收件人”这种功能要注意用户上传的文件默认保存在Tomcat的work目录或者你指定的临时目录中不要在邮件发送后保留这些文件。这是个经常被忽略的细节。如果你是用JSP做毕业设计或课设建议在论文的思路设计部分明确写清楚JSP负责表现层Servlet/JavaBean负责业务逻辑JavaMail API负责邮件传输层。这样的架构描述比“用JSP发邮件”听上去专业得多也更容易让答辩老师理解你的设计思路。最后还有一个心态层面的经验遇到邮件相关的问题不要一上来就怀疑代码。先看网络通不通再确认账号密码是否正确最后才检查代码逻辑。按照这个顺序排查大部分问题十分钟内就能定位。我见过太多人为了一个AuthenticationFailedException改了两个小时的代码实际上只是授权码复制时多了一个空格。这种小问题任何时候都有可能遇到细心一点比什么都强。