当前位置:

首页 > 编程开发 > SpringBoot集成邮件服务的完整实现方案

SpringBoot集成邮件服务的完整实现方案

本文目录

    SpringBoot集成邮件服务需引入starter-mail依赖并配置SMTP服务器及授权码。将发送功能封装为工具类,配合条件注解自动装配。结合RabbitMQ实现异步解耦,注册成功后推送消息至队列,消费者异步发送邮件,提高接口响应速度。

    在SpringBoot项目中集成邮件服务,其实套路很固定,但真要把“发送邮件”这个功能玩得顺手,尤其是跟微服务、消息队列这些场景结合起来,还是有不少细节值得捋一捋的。下面这份方案,咱们从底层依赖一步步搭到上层业务,并附带了异步解耦的实战案例。

    1. 引入邮箱依赖

    要在SpringBoot里发邮件,首先得把starter-mail请进来,就这么简单。

    
        org.springframework.boot
        spring-boot-starter-mail
    

    2. 配置邮箱

    有了依赖,接下来就是配置邮件服务器。这里以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

    3. 测试发送一封简单的邮件

    配置搞定后,最直接的方式就是写个单元测试,看看能不能跑通。这段代码展示了最基础的发件流程:创建消息、设置发件人、收件人、标题和正文,然后发送。注意,这里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);
    }

    实际代码-前置条件:

    SpringBoot集成邮件服务的完整实现方案

    不过,在实际项目中,我们肯定不会在每个业务代码里都重复写这一套底层逻辑。所以,接下来就是封装和抽象。

    1. 将邮件发送功能封装在一个公共类里,方便其他地方调用

    说到封装,最直接的做法就是把它抽成一个公共工具类,放到common模块里。这样做有几个好处:

    • 依赖:工具类在common里,所有子服务都不用重复导mail依赖了。
    • yml配置:需要提醒的是,无论工具类放哪,每个要发邮件的微服务都必须自己配mail配置,因为每个服务的邮箱账号可能不同。
    • 调用:其他服务只需要注入这个MailUtil,直接调用send方法就行,不用再关心创建MimeMessage那些底层代码。
    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。
    • Spring会从容器中找到MailConfig注册好的Mail Bean,注入进来。
    • 调用mail.send(邮箱,标题,正文),本质就是执行工具类里封装好的邮件发送代码。

    这样一来,整个邮件发送的链路就清晰了:业务层只管发消息,消息队列负责解耦,邮件工具类负责发送,各司其职,非常干净。

    本文内容来源于网友投稿,如有侵权请联系删除。
    作者最新文章
    编程开发
    相关文章 更多
    PHP递归性能优化技巧与迭代替代方案
    PHP递归性能优化技巧与迭代替代方案

    解析PHP递归函数在树形数据处理中的性能瓶颈,提供预加载数据消除I/O、使用显式栈替代深层递归的实战方案,帮助开发者在代码可读性与执行效率间做出合理取舍。

    Java测试中怎么使用Mockito模拟依赖对象
    Java测试中怎么使用Mockito模拟依赖对象

    详细讲解在Java单元测试中如何使用Mockito模拟依赖对象,包括引入依赖、创建Mock、打桩返回值、行为验证以及Mock与Spy的核心差异和常见陷阱排查。

    链表删除节点的时间复杂度是多少及其详细分析
    链表删除节点的时间复杂度是多少及其详细分析

    详细分析链表删除节点的时间复杂度,深入探讨单链表与双向链表在不同已知前提下的查找与删除开销,并结合完整代码与清晰图解进行对比总结。

    codex如何配置模型参数及文件设置教程
    codex如何配置模型参数及文件设置教程

    想知道如何让AI写出的代码更贴合你的习惯?本文手把手教你在VS Code中调整Codex相关模型参数,通过修改配置文件优化温度值和令牌限制,解决代码建议不准确或响应慢的问题。

    Claude Code AI编程工具实力揭秘与编程助手实测
    Claude Code AI编程工具实力揭秘与编程助手实测

    通过实测展示Claude Code在终端中如何理解自然语言指令、自动修改代码文件并处理复杂编程任务,帮助开发者评估其实际辅助能力。

    winforms教程自学入门与基础开发步骤详解
    winforms教程自学入门与基础开发步骤详解

    本教程详细讲解如何使用Visual Studio创建WinForms项目,通过添加按钮和标签控件并编写点击事件代码,实现一个基础的计数器功能,适合C#初学者快速上手Windows窗体应用开发。

    Cursor自动补全设置教程教你快速开启代码补全功能
    Cursor自动补全设置教程教你快速开启代码补全功能

    详解Cursor编辑器中自动补全功能的开启与优化设置,涵盖Tab触发机制、上下文窗口调整及模型切换,帮助开发者解决补全延迟、干扰大等问题,提升编码流畅度。

    pandas的数据格式怎么转换和设置方法教程
    pandas的数据格式怎么转换和设置方法教程

    详解Pandas中数据格式转换的核心方法,包括astype强制转换、to_numeric容错处理及日期解析技巧,解决常见类型错误并提升数据处理效率。

    VS Code中文设置方法 简体语言包安装与切换教程
    VS Code中文设置方法 简体语言包安装与切换教程

    详细介绍在Visual Studio Code中安装Chinese (Simplified)语言包的方法,包括通过扩展市场搜索、安装及自动重启切换至简体中文界面的完整步骤,帮助开发者快速将编辑器本地化。

    cursor安装过程无法更改安装位置的解决方法
    cursor安装过程无法更改安装位置的解决方法

    针对Cursor安装包默认锁定C盘且无路径选择界面的问题,提供通过手动移动文件并创建目录联结(Symbolic Link)的解决方案,实现将软件安装在其他磁盘分区。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    macOS软件
    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    Mac软件 更多
    photoshop
    photoshop
    Windows、macOS 、 iPad

    Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。

    灵活计算器
    灵活计算器
    macOS/iOS/Android

    灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

    WINDOWS 更多
    3dmax(3ds max)
    3dmax(3ds max)
    Windows

    Autodesk 3ds Max 是一款专业的三维建模、动画与渲染软件,广泛应用于建筑可视化、游戏开发、影视动画、广告设计和产品展示等领域。

    photoshop
    photoshop
    Windows、macOS 、 iPad

    Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。