SpringBoot实现邮件推送

SpringBoot实现邮件推送

在项目中经常会遇到SpringBoot推送消息的业务,除了站内推送通知,邮件推送也是一种常见的方式,本次小鹿就带大家实现邮件推送。

引入依赖

xml 复制代码
<!-- spring mail -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- thymeleaf -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
  1. spring-boot-starter-mail:

    • 作用:提供了使用Spring Framework的邮件发送功能的起步依赖。
    • 功能:使得在Spring Boot应用中可以轻松地配置和使用邮件发送功能,包括发送简单文本邮件、HTML邮件、附件邮件等。
    • 使用:结合Spring的邮件模块,你可以方便地在你的应用程序中集成邮件发送功能,无论是用于发送通知、验证邮件还是其他目的。
  2. spring-boot-starter-thymeleaf:

    • 作用:提供了使用Thymeleaf模板引擎的起步依赖。
    • 功能:Thymeleaf是一种流行的Java模板引擎,用于在Web应用中创建动态HTML模板。
    • 使用:结合Spring Boot和Thymeleaf,你可以使用Thymeleaf的模板语言来生成动态内容,比如渲染服务器端数据到HTML页面上,以便在Web应用中更灵活地展示数据。

在项目中,使用第二个依赖再结合静态的html文件就可以实现,将邮件以预定义模板的形式发送出去。

在实际中只需要将对应的值以Map的方式填入到对应字段中即可,别急~接下来小鹿会带大家实现的。

配置

在项目配置中加入如下配置

yml格式如下:

yaml 复制代码
spring:
  # 邮箱配置
  mail:
    host: smtp.qq.com
    # 用户名 替换成你实际的用户名
    username: test@foxmail.com
    # 授权码 可以去官方申请 不是个人邮箱密码
    password: xxxxxxxxxx

application.properties文件格式如下:

ini 复制代码
# 需要开启 smtp qq邮箱为例
spring.mail.host=smtp.qq.com
spring.mail.port=465
# 发件人的邮箱
spring.mail.username=xxxxx
# qq 邮箱的第三方授权码 并非个人密码
spring.mail.password=xxxx
#开启ssl 否则 503 错误 但是实际测试发现其实不加也不会报错 但是最好还是加上
spring.mail.properties.mail.smtp.ssl.enable=true

代码具体实现

创建邮件实体封装类

kotlin 复制代码
package com.ican.model.dto;
​
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
​
import java.util.Map;
​
/**
 * 邮箱DTO
 * @author xiayexiaolu
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "邮箱DTO")
public class MailDTO {
​
    /**
     * 接收者邮箱号
     */
    @ApiModelProperty(value = "接收者邮箱号")
    private String toEmail;
​
    /**
     * 主题
     */
    @ApiModelProperty(value = "主题")
    private String subject;
​
    /**
     * 内容
     */
    @ApiModelProperty(value = "内容")
    private String content;
​
    /**
     * 内容信息
     */
    @ApiModelProperty(value = "内容信息")
    private Map<String, Object> contentMap;
​
    /**
     * 邮件模板
     */
    @ApiModelProperty(value = "邮件模板")
    private String template;
}
​

创建邮件服务接口并实现

java 复制代码
package com.ican.service;
​
import com.ican.model.dto.MailDTO;
​
/**
 * 邮件服务接口
 *
 * @author xiaoyexiaolu
 **/
public interface EmailService {
​
    /**
     * 发送简单邮件
     *
     * @param mailDTO 邮件信息
     */
    void sendSimpleMail(MailDTO mailDTO);
​
    /**
     * 发送HTML邮件
     *
     * @param mailDTO 邮件信息
     */
    void sendHtmlMail(MailDTO mailDTO);
}
​

对接口进行实现,下面为模板代码,可直接使用:

ini 复制代码
package com.ican.service.impl;
​
import com.ican.model.dto.MailDTO;
import com.ican.service.EmailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
​
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
​
/**
 * 邮件服务接口实现类
 *
 * @author xiayexiaolu
 **/
@Service
public class EmailServiceImpl implements EmailService {
​
    /**
     * 邮箱号
     */
    @Value("${spring.mail.username}")
    private String email;
​
    @Autowired
    private JavaMailSender javaMailSender;
​
    @Autowired
    private TemplateEngine templateEngine;
​
    @Override
    public void sendSimpleMail(MailDTO mailDTO) {
        SimpleMailMessage simpleMail = new SimpleMailMessage();
        simpleMail.setFrom(email);
        simpleMail.setTo(mailDTO.getToEmail());
        simpleMail.setSubject(mailDTO.getSubject());
        simpleMail.setText(mailDTO.getContent());
        javaMailSender.send(simpleMail);
    }
​
    @Override
    public void sendHtmlMail(MailDTO mailDTO) {
        try {
            MimeMessage mimeMessage = javaMailSender.createMimeMessage();
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage);
            Context context = new Context();
            context.setVariables(mailDTO.getContentMap());
            String process = templateEngine.process(mailDTO.getTemplate(), context);
            mimeMessageHelper.setFrom(email);
            mimeMessageHelper.setTo(mailDTO.getToEmail());
            mimeMessageHelper.setSubject(mailDTO.getSubject());
            mimeMessageHelper.setText(process, true);
            javaMailSender.send(mimeMessage);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }
}

其实到这里发送邮件就已经能用了,但是如果使用原生发送邮件的方式可能会有少许缺点。使用消息队列有以下好处

  1. 异步处理:通过消息队列,你可以将邮件发送操作异步化,即使发送邮件的过程比较耗时,也不会阻塞主线程,提高了应用程序的响应速度和吞吐量。
  2. 解耦:使用消息队列可以将邮件发送操作解耦合,发送邮件的业务逻辑与其他业务逻辑相互独立。这意味着你可以轻松地更改或扩展邮件发送的实现方式,而不影响其他业务逻辑。
  3. 负载均衡:通过消息队列,你可以将邮件发送请求分发到多个邮件发送服务节点上,从而实现负载均衡,提高了系统的稳定性和可用性。
  4. 削峰填谷:在高并发情况下,邮件发送请求可能会突然增加,而消息队列可以帮助平滑处理这种突发情况,避免邮件服务器过载。
  5. 失败处理:使用消息队列可以更好地处理邮件发送失败的情况。如果邮件发送失败,你可以通过消息队列实现重试机制或者将失败的消息重新放回队列,以便稍后再次尝试发送。

使用消息队列实现邮件发送

额外引入依赖

xml 复制代码
<!-- spring amqp -->
<dependency>
      <groupId>cn.dev33</groupId>
      <artifactId>sa-token-spring-boot-starter</artifactId>
      <version>${saToken.version}</version>
</dependency>

这个依赖会自动地添加所需的 RabbitMQ 客户端库,并配置好Spring Boot的AMQP支持。

在配置中配置连接

yaml 复制代码
spring:
  # rabbitmq配置
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    listener:
      simple:
        retry:
          enabled: true
          # 重试间隔时间
          initial-interval: 3000
          # 最大重试次数
          max-attempts: 3

创建邮件消费者

less 复制代码
package com.ican.consumer;

import com.ican.model.dto.MailDTO;
import com.ican.service.EmailService;
import org.springframework.amqp.core.ExchangeTypes;
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.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;

import static com.ican.constant.MqConstant.*;

/**
 * 邮件消费者
 * @author xiayexiaolu
 */
@Component
public class EmailConsumer {

    @Autowired
    private EmailService emailService;

    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue(value = EMAIL_SIMPLE_QUEUE, durable = "true", autoDelete = "false"),
                    exchange = @Exchange(value = EMAIL_EXCHANGE, type = ExchangeTypes.TOPIC),
                    key = EMAIL_SIMPLE_KEY
            )})
    public void listenSendSimpleEmail(@Payload MailDTO mail) {
        emailService.sendSimpleMail(mail);
    }

    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue(value = EMAIL_HTML_QUEUE, durable = "true", autoDelete = "false"),
                    exchange = @Exchange(value = EMAIL_EXCHANGE, type = ExchangeTypes.TOPIC),
                    key = EMAIL_HTML_KEY
            )})
    public void listenSendHtmlEmail(@Payload MailDTO mail) {
        emailService.sendHtmlMail(mail);
    }
}
  1. @Component 注解表示这是一个 Spring 组件,会被 Spring 扫描并纳入到应用程序的上下文中。
  2. @Autowired 注解注入了一个 EmailService 对象,用于实际发送邮件的逻辑处理。
  3. 使用了 @RabbitListener 注解标注了两个方法,分别用于监听两个队列中的消息。这两个方法分别处理发送简单邮件和发送 HTML 邮件的逻辑。
  4. @RabbitListener 注解中,通过 @QueueBinding 注解配置了队列与交换机的绑定关系,指定了队列的属性和交换机的名称、类型、路由键等信息。
  5. 在方法参数中使用 @Payload 注解,指定了消息体的类型为 MailDTO,这样方法参数就会自动解析为消息体的内容。

具体使用

首先需要创建一个邮件对象

scss 复制代码
MailDTO mailDTO = new MailDTO();
mailDTO.setToEmail("目标邮箱");
mailDTO.setSubject("邮件主题");
mailDTO.setTemplate("邮件使用的模板");  //这里需要在之前加入了thymeleaf依赖支持
mailDTO.setContentMap(contentMap);

// 这里的contentMap是一个HashMap 也是最重要的参数,用于将值映射到模板中 具体示例如下图所示

然后直接使用代码:

scss 复制代码
// 发送HTML邮件
 rabbitTemplate.convertAndSend(EMAIL_EXCHANGE, EMAIL_HTML_KEY, mailDTO);

背后的原理

  1. 邮件发送(生产者)

    • 当你执行 rabbitTemplate.convertAndSend(EMAIL_EXCHANGE, EMAIL_HTML_KEY, mailDTO); 时,实际上是将 mailDTO 对象发送到了 RabbitMQ 中的 EMAIL_EXCHANGE 交换机,并使用 EMAIL_HTML_KEY 路由键。
    • 这个过程是异步的,即发送操作执行后就会立即返回,而不会等待邮件真正发送完成。
  2. 邮件消费者

    • 通过 @RabbitListener 注解,你的邮件消费者监听了名为 EMAIL_HTML_QUEUE 的队列。
    • 当有消息到达队列时,listenSendHtmlEmail 方法会被调用,并传入消息体 mailDTO
    • listenSendHtmlEmail 方法中,调用了 emailService.sendHtmlMail(mail) 方法来实际发送邮件。
  3. 消息传递

    • RabbitMQ 确保了消息传递的可靠性和顺序性。当你发送一条消息到交换机时,RabbitMQ 会根据路由规则将消息路由到对应的队列中去。
    • 邮件消费者监听队列,一旦有消息到达队列,就会立即触发消费者方法的执行,从而实现邮件发送的过程。

总的来说,执行 rabbitTemplate.convertAndSend(EMAIL_EXCHANGE, EMAIL_HTML_KEY, mailDTO); 之后,程序并不会自动调用消费者中的代码,而是将消息发送到 RabbitMQ 中,并触发消费者的监听。一旦有消息到达队列,消费者会立即执行对应的消费方法,完成邮件发送的过程。

小结

本文介绍了如何在Spring Boot项目中实现邮件推送功能。首先,通过引入相关依赖,包括spring-boot-starter-mailspring-boot-starter-thymeleaf,使得项目具备了发送邮件和使用Thymeleaf模板的能力。然后,配置了邮件服务的连接信息,包括邮箱的主机、端口、用户名和授权码等。接着,创建了邮件实体封装类MailDTO,定义了邮件的相关信息。通过邮件服务接口EmailService和其实现类EmailServiceImpl,实现了发送简单邮件和HTML邮件的功能。此外,还介绍了使用消息队列来异步处理邮件发送的好处,以及如何通过RabbitMQ实现邮件发送的异步处理。最后,通过具体的代码示例,展示了如何在项目中使用消息队列发送邮件,并给出了发送邮件的具体步骤。

小鹿的朋友们~大家共同加油呀

相关推荐
bearpping7 分钟前
SpringBoot最佳实践之 - 使用AOP记录操作日志
java·spring boot·后端
一叶飘零_sweeeet9 分钟前
线上故障零扩散:全链路监控、智能告警与应急响应 SOP 完整落地指南
java·后端·spring
开心就好20251 小时前
不同阶段的 iOS 应用混淆工具怎么组合使用,源码混淆、IPA混淆
后端·ios
架构师沉默1 小时前
程序员如何避免猝死?
java·后端·架构
椰奶燕麦2 小时前
Windows PackageManager (winget) 核心故障排错与通用修复指南
后端
zjjsctcdl2 小时前
springBoot发布https服务及调用
spring boot·后端·https
zdl6863 小时前
Spring Boot文件上传
java·spring boot·后端
世界哪有真情3 小时前
哇!绝了!原来这么简单!我的 Java 项目代码终于被 “拯救” 了!
java·后端
RMB Player3 小时前
Spring Boot 集成飞书推送超详细教程:文本消息、签名校验、封装工具类一篇搞定
java·网络·spring boot·后端·spring·飞书
重庆小透明3 小时前
【搞定面试之mysql】第三篇 mysql的锁
java·后端·mysql·面试·职场和发展