java发邮件连不上smtp服务器主因是网络或认证配置错误,需检查smtp服务开启、专用密码、端口与tls设置、防火墙及jvm协议支持。

Java发邮件为什么连不上SMTP服务器
绝大多数失败不是代码写错,而是网络或认证配置没对。常见现象是 javax.mail.AuthenticationFailedException 或卡在 connect timed out。
- 确认邮箱是否开启「SMTP服务」且生成了专用密码(比如QQ邮箱叫「授权码」,不是登录密码)
- 检查端口:
587(STARTTLS)和465(SSL)不能混用;用587时必须设mail.smtp.starttls.enable=true - 防火墙/公司代理可能拦截出站
465或587,可临时用本地 telnet 测试:telnet smtp.qq.com 587 - Java 17+ 默认禁用旧版 TLS,若 SMTP 服务商只支持 TLSv1.1,需显式加 JVM 参数:
-Djdk.tls.client.protocols=TLSv1.1,TLSv1.2
用JavaMail API发纯文本邮件的最小可行代码
别一上来就套框架,先跑通最简路径。依赖只需 jakarta.mail(新版)或 javax.mail(旧版),Maven 坐标注意区分。
- 关键类只有三个:
Session、MimeMessage、Transport;其他都是包装 -
Session.getInstance(props, auth)中的props必须包含mail.smtp.host和mail.smtp.port,缺一不可 - 发件人地址必须和认证账号一致,否则 Gmail/QQ 会静默拒收;收件人用
message.addRecipients(Message.RecipientType.TO, ...),别直接拼字符串
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.qq.com");
props.put("mail.smtp.port", "587");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
Session session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("xxx@qq.com", "your-app-password");
}
});
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("xxx@qq.com"));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("to@example.com"));
message.setSubject("测试");
message.setText("Hello from JavaMail");
Transport.send(message);
发送带附件或HTML正文时容易漏掉的关键设置
不是加个 MimeBodyPart 就完事——内容类型、编码、边界符全靠 Multipart 自动管理,手动错一个就会变成乱码或附件打不开。
- HTML 邮件必须设
message.setContent(multipart, "text/html; charset=utf-8"),不能只用setText(..., "UTF-8") - 附件路径用
FileDataSource时,确保文件存在且 Java 进程有读权限;中文路径要避免用new File("中文.txt")直接构造,优先用Paths.get(...).toUri() - 如果 HTML 里引用了本地图片,不要用
<img src="file:///..." alt="在Java里如何开发一个简单的邮件发送系统_Java网络与SMTP协议解析" >,会被客户端屏蔽;应作为内嵌资源用MimeBodyPart.setContentID()绑定 - 大附件(>5MB)建议先压缩再发,部分邮箱(如 Outlook)会直接丢弃超限邮件,不报错
生产环境必须处理的三个非功能点
能发出去 ≠ 能稳定用。真实系统里,连接池、异步、重试这三块不补上,早晚出问题。
立即学习“Java免费学习笔记(深入)”;
- 别每次发邮件都新建
Session——它本身是线程安全的,复用即可;但Transport不是,每次发完必须close() - 阻塞式发送会拖垮 Web 请求,至少用
CompletableFuture.runAsync()包一层;高并发场景建议接入ThreadPoolTaskExecutor(Spring)或Executors.newFixedThreadPool() - 网络抖动导致
SendFailedException很常见,简单重试 2 次 + 指数退避(如 1s、3s)比直接告警更实用;但别重试认证失败类错误
SMTP 协议本身不保证送达,Java 层最多做到「发出成功」;想确认对方是否收到,得靠回执头(Disposition-Notification-To)或对接邮件服务商的投递日志 API——这点很多人一开始根本没想到










