发布于2026-07-23 阅读(0)
扫一扫,手机访问
在SpringBoot项目中集成邮件服务,其实套路很固定,但真要把“发送邮件”这个功能玩得顺手,尤其是跟微服务、消息队列这些场景结合起来,还是有不少细节值得捋一捋的。下面这份方案,咱们从底层依赖一步步搭到上层业务,并附带了异步解耦的实战案例。
要在SpringBoot里发邮件,首先得把starter-mail请进来,就这么简单。
org.springframework.boot spring-boot-starter-mail
有了依赖,接下来就是配置邮件服务器。这里以QQ邮箱为例,需要配置SMTP服务器地址、端口、以及最重要的——授权码。注意,这个password不是你的邮箱登录密码,而是开启SMTP服务后生成的授权码,这个坑不少人踩过。
spring:
mail:
host: smtp.qq.com
username: 你的QQ邮箱@qq.com
password: 你的授权码 #需要自己去邮箱内开启SMTP服务
default-encoding: UTF-8
port: 465
protocol: smtp
properties:
mail:
smtp:
auth: true
ssl:
enable: true
socketFactory:
class: ja vax.net.ssl.SSLSocketFactory
配置搞定后,最直接的方式就是写个单元测试,看看能不能跑通。这段代码展示了最基础的发件流程:创建消息、设置发件人、收件人、标题和正文,然后发送。注意,这里setText的第二个参数为true,表示正文是HTML格式。
@Autowired
Ja vaMailSender ja vaMailSender;
@Test
public void test() throws Exception {
// 创建一个邮件消息
MimeMessage message = ja vaMailSender.createMimeMessage();
// 创建 MimeMessageHelper
MimeMessageHelper helper = new MimeMessageHelper(message, false);
// 发件人邮箱和名称
helper.setFrom("747692844@qq.com", "springdoc");
// 收件人邮箱
helper.setTo("admin@springboot.io");
// 邮件标题
helper.setSubject("Hello");
// 邮件正文,第二个参数表示是否是HTML正文
helper.setText("Hello World! ", true);
// 发送
ja vaMailSender.send(message);
}
实际代码-前置条件:

不过,在实际项目中,我们肯定不会在每个业务代码里都重复写这一套底层逻辑。所以,接下来就是封装和抽象。
1. 将邮件发送功能封装在一个公共类里,方便其他地方调用
说到封装,最直接的做法就是把它抽成一个公共工具类,放到common模块里。这样做有几个好处:
package com.bite.common.utils;
import jakarta.mail.internet.MimeMessage;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.mail.ja vamail.Ja vaMailSender;
import org.springframework.mail.ja vamail.MimeMessageHelper;
import ja va.util.Optional;
public class Mail {
private Ja vaMailSender ja vaMailSender;
private MailProperties mailProperties;
public Mail(Ja vaMailSender ja vaMailSender, MailProperties mailProperties) {
this.ja vaMailSender = ja vaMailSender;
this.mailProperties = mailProperties;
}
public void send(String to, String subject, String content) throws Exception {
MimeMessage mimeMessage = ja vaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, false);
//发件人名称
String personal = Optional.ofNullable(mailProperties.getProperties().get("personal"))
.orElse(mailProperties.getUsername());
helper.setFrom(mailProperties.getUsername(), personal);
helper.setTo(to); //收件人
helper.setSubject(subject); //邮件主题
helper.setText(content, true);
ja vaMailSender.send(mimeMessage);
}
}
2. 写Spring邮件配置类,哪个服务需要就写在哪个服务里,也可以提出来写在服务的公共类里
工具类写好了,怎么让它被Spring管理起来?这就需要写一个配置类,把它注册成一个Bean。这里有个小技巧:用@ConditionalOnProperty注解。它的意思是,只有当你配置了spring.mail.username这个属性,这个Bean才会被创建。这样,那些不需要发邮件的服务(比如博客展示服务)就能避免因为找不到邮箱配置而报错,非常优雅。
package com.bite.common.config;
import com.bite.common.utils.Mail;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.ja vamail.Ja vaMailSender;
@Configuration
public class MailConfig {
@Bean
@ConditionalOnProperty(prefix="spring.mail",name="username")
public Mail mail(Ja vaMailSender ja vaMailSender, MailProperties mailProperties) {
return new Mail(ja vaMailSender,mailProperties);
}
}
光说不练假把式,下面看一个实际业务场景。假设我们有一个用户注册服务,在注册成功后,需要给用户发一封欢迎邮件。但发邮件这个操作可能会比较耗时,如果同步处理,会拖慢用户注册的响应速度。所以,这里引入RabbitMQ来做异步解耦。
某服务的注册业务(Service)代码:
@Override
public Integer register(UserInfoRegisterRequest registerRequest) {
//校验参数:
checkUserInfo(registerRequest);
//校验通过,才是用户注册,插入数据库
UserInfo userInfo = BeanConvert.convertUserInfoByEncrypt(registerRequest);
try{
int result = userInfoMapper.insert(userInfo);
if(result==1){
//存储数据到redis中
redis.set(buildKey(userInfo.getUserName()), JsonUtil.toJson(userInfo),EXPIRE_TIME);
//⭐注册成功主动推送用户数据到 RabbitMQ,交给消费者异步处理发邮件,不阻塞注册接口响应速度
userInfo.setPassword("");
rabbitTemplate.convertAndSend(Constants.USER_EXCHANGE_NAME,"",JsonUtil.toJson(userInfo));
return userInfo.getId();
}else{
throw new BlogException("用户注册失败");
}
}catch (Exception e){
log.error("用户注册失败,e:",e);
throw new BlogException("用户注册失败");
}
}
看到没有,关键代码就是这一行:rabbitTemplate.convertAndSend(...)。注册成功后,立刻把用户信息丢到消息队列里,然后直接返回。这样,用户注册接口的响应速度非常快,而发邮件的任务则交给消费者去异步处理。
生产者:注册成功后,主动推送用户数据到RabbitMQ,交给消费者异步处理发邮件,不阻塞注册接口响应速度。
消费者:消费收到的用户数据,并做出下一步操作,比如发送邮件。
package com.bite.user.listener;
import com.bite.common.constant.Constants;
import com.bite.common.utils.JsonUtil;
import com.bite.common.utils.Mail;
import com.bite.user.dataobject.UserInfo;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import ja va.io.IOException;
@Slf4j
@Component
public class UserQueueListener {
//⭐
@Autowired
private Mail mail;
@RabbitListener(bindings=@QueueBinding(
value=@Queue(value=Constants.USER_QUEUE_NAME,durable="true"),
exchange=@Exchange(value=Constants.USER_EXCHANGE_NAME,type= ExchangeTypes.FANOUT)
))
public void handler(Message message, Channel channel) throws IOException {
long deliveryTag=message.getMessageProperties().getDeliveryTag();
try{
String body = new String(message.getBody());
log.info("收到用户信息,body:{}",body);
//TODO 发送注册成功邮件
UserInfo userInfo= JsonUtil.parseJson(body, UserInfo.class);
//⭐发送邮箱
mail.send(userInfo.getEmail(),"恭喜加入XM博客社区",buildContent(userInfo.getUserName()));
//确认
channel.basicAck(deliveryTag,true);
}catch(Exception e){
//否定确认
channel.basicNack(deliveryTag,true,true);
log.error("邮件发送失败,e:",e);
}
}
//发送的邮箱内容
private String buildContent(String userName){
StringBuilder builder=new StringBuilder();
builder.append("尊敬的").append(userName).append(",您好!").append("
");
builder.append("感谢您注册成为我们博客社区的一员!我们很高兴您加入我们的大家庭!
");
builder.append("您的注册信息如下:用户名: ").append(userName).append("
");
builder.append("为了您的账号安全,请妥善保管您的登录信息.如果使用过程中,遇到任何问题,欢迎联系我们的支持团队. XXXX@bXM.com
");
builder.append("再次感谢您的加入,我们期待看到您的精彩内容!
")
.append("最好的祝愿
")
.append("XM博客团队");
return builder.toString();
}
}
发送邮件前置条件:公共类里的
① 把Mail工具类创建成Spring Bean,放进容器
这一步就是前面提到的MailConfig配置类的作用。它把Mail工具类注册成Bean,并自动注入Ja vaMailSender和MailProperties。通过@ConditionalOnProperty,只有配置了邮箱的服务才会创建这个Bean,灵活又安全。
② 邮箱发送操作实现逻辑
这就是Mail类本身做的事情:封装底层发邮件逻辑,创建邮件、设置发件人/收件人/HTML正文、调用发送API。对外暴露一个简洁的send(String to, String subject, String content)方法。
三者关系
@Autowired注入Mail。MailConfig注册好的Mail Bean,注入进来。mail.send(邮箱,标题,正文),本质就是执行工具类里封装好的邮件发送代码。这样一来,整个邮件发送的链路就清晰了:业务层只管发消息,消息队列负责解耦,邮件工具类负责发送,各司其职,非常干净。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8