# 快速搭建专属域名邮箱服务，简单整合邮箱消息推送功能至 Spring Boot 应用程序

原文：https://blog.grtsinry43.com/posts/spring-boot-mail-push/

作者：grtsinry43

发布时间：2025-01-02T13:27:42Z

内容更新时间：2025-01-02T13:27:42Z

作为即时通信的重要方式，邮件在互联网互动中起到举足轻重的作用，而搭建邮件推送服务不仅可以做到实时的消息交流，更能拉近网站与用户的距离，增加用户粘性，本文将介绍获取域名邮箱，启用 smtp，集成后端，实现推送的全过程，带你让网站“活起来”。

## 搭建域名邮箱服务

> 感谢 [@miaoer](https://www.miaoer.net) 和 [@Innei](https://innei.in) 大佬提供的快捷解决方案，节省折腾 Postfix 和 Dovecot 的精力，不过后续也会更新服务器手动搭建邮件首发服务的详细步骤（等期末之后有精力写详细文档，步骤还是挺复杂的）

这里我们选择 Lark（飞书国际版，国内版的操作类似，只是多了实名步骤）提供的邮箱服务

### 获取邮箱

我们可以使用邮箱或者 Google 帐号注册并创建企业（个人就是 1-50 人企业选项），注册完成后，我们需要为当前登录帐号分配邮箱：

进入 [企业管理页面](https://larksuite.com/admin)，点击上方产品设置，找到邮箱，在域名管理中即可为自己的邮箱指定域。

![邮箱管理](https://blog.grtsinry43.com/uploads/2025/01/02/_20250101_224456.png_0fefa2d9-6c97-4e87-a249-b443305f3333.png)

![添加自定义地址](https://blog.grtsinry43.com/uploads/2025/01/02/_20250101_222416.png_c3197ed8-a910-414c-88e3-2b40e3a73b1b.png)

按照要求填写并前往对应的 DNS 提供商管理面板添加解析记录，我们便成功获取到了域名邮箱。

![设置DNS记录](https://blog.grtsinry43.com/uploads/2025/01/02/_20250101_224706.png_5c6b9ef1-0c9b-4e09-b892-e656d548fe5c.png)

随后我们返回企业管理——组织架构——成员与部门，为自己的登录账户添加企业邮箱，由此便可以通过网页直接收发邮件

### 设置 smtp

依然是管理面板——产品设置——邮箱，这次我们找到左面的地址管理——公共邮箱，我们便可以创建支持 smtp 的公共推送地址

![获取SMTP](https://blog.grtsinry43.com/uploads/2025/01/02/_20250101_225338.png_ae41bc21-a757-4dc6-8ce3-ad00f4c753f5.png)

## 整合至后端程序

条件具备，回到主线。

### 整合依赖

我们这里使用的是 Spring 提供的 [spring-boot-starter-mail](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-mail/3.4.1)，在 `pom.xml` 中添加以下依赖：

```xml
<!--邮箱服务-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
            <version>3.4.1</version>
        </dependency>
```

随后在 `application.xml` 中添加好对应的配置：

```xml
spring:
  mail:
    host: smtp.larksuite.com
    port: 587
    username: name@domain.com
    password: your_password
    properties:
      mail.smtp.auth: true
      mail.smtp.starttls.enable: true
      mail.smtp.starttls.required: true
    default-encoding: UTF-8
```

### 创建服务

我们先以最小可行性思想来完成这个实验，先创建基础的 `EmailService.java`

```java
package com.grtsinry43.grtblog.service;

import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;

/**
 * @author grtsinry43
 * @date 2025/1/1 23:04
 * @description 热爱可抵岁月漫长
 */
@Service
public class EmailService {
    private final JavaMailSender mailSender;

    public EmailService(JavaMailSender mailSender) {
        this.mailSender = mailSender;
    }

    public void sendEmail(String to, String subject, String content) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom("name@domain.com");
        message.setTo(to);
        message.setSubject(subject);
        message.setText(content);
        mailSender.send(message);
    }
}
```

额，就这么简单，~~是不是很 ruozhi~~

### 邮件模板

当然，我们给用户展示的不可能是简单的一点点文字而已吧，所以我们需要自定义邮件的样式，由于其支持使用 html，那我们便可以利用模板引擎 `thymeleaf` 来快速创建模板，添加如下依赖：

```xml
<!--模板支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
```

我们在 `src/main/resources/templates` 中新建 `emailTemplate.html`，这样我们便可以使用模板语法将内容填充进我们的邮件啦，利用 `org.thymeleaf.context.Context;`，也就是创建上下文，由模板引擎提供，在 html 生成时消费，语法大概是 `th:text="${type}` 这种，你也可以参考下我的：

```html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Comment Reply Notification</title>
        <style>
            /*太多了，这里省略了*/
        </style>
    </head>
    <body>
        <div class="bg-container">
            <div class="main-container">
                <div class="header">
                    <img src="https://dogeoss.grtsinry43.com/img/author.jpeg" alt="Logo">
                    <h1>Grtsinry43's Blog | 邮件通知 </h1>
                </div>
                <div class="content">
                    <h2 style="font-size: medium"> 亲爱的朋友 <span th:text="${name}"></span>, 您在
                        <span th:text="${type}"></span>「
                        <span th:text="${title}"></span>」的评论收到了新的回复哇 </h2>
                    <div class="user-info">
                        <img class="user-avatar" th:src="${avatarUrl}" alt="Logo">
                        <div style="margin-left: 10px"><span th:text="${replyName}"></span> 在 <span
                                th:text="${replyTime}"></span> 回复了您的评论
                        </div>
                    </div>
                    <div style="margin-top: 10px">
                        <span th:utext="${replyContent}"></span>
                    </div>
                    <div>
                        <span> 您的原评论内容：</span>
                        <div style="margin-top: 10px">
                            <span th:utext="${commentContent}"></span>
                        </div>
                    </div>
                    <div class="action-container">
                        <a class="more-button" th:href="${url}">
                            点击查看详情
                        </a>
                    </div>
                </div>
                <div class="footer">
                    <p>Powered by Grtblog. 此邮件由系统自动发出，请勿回复。</p>
                    <p>Copyright &copy; 2022-2025 Grtsinry43's Blog. All rights reserved.</p>
                    <p><a href="https://blog.grtsinry43.com"> 前往主站 </a></p>
                </div>
            </div>
        </div>
    </body>
</html>
```

这里我选择在发送邮件的方法中添加一个HashMap作为参数，传递所需的上下文，也就是这样：

```java
package com.grtsinry43.grtblog.service;

import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import java.util.HashMap;

/**
 * @author grtsinry43
 * @date 2025/1/1 23:04
 * @description 热爱可抵岁月漫长
 */
@Service
public class EmailService {
    private final JavaMailSender mailSender;
    private final TemplateEngine templateEngine;

    public EmailService(JavaMailSender mailSender, TemplateEngine templateEngine) {
        this.mailSender = mailSender;
        this.templateEngine = templateEngine;
    }

    @Async
    public void sendEmail(String to, String subject, HashMap<String,String> info) throws MessagingException {
        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");

        Context context = new Context();
        context.setVariable("name", info.getOrDefault("name", " "));
        context.setVariable("type", info.getOrDefault("type", " "));
        // ... 这里都是设置上下文咯
        String html = templateEngine.process("emailTemplate", context);

        helper.setFrom("notification@grtsinry43.com");
        helper.setTo(to);
        helper.setSubject(subject);
        helper.setText(html, true);

        mailSender.send(message);

        System.out.println("邮件发送成功" + to);
    }
}

```

Tip！这里还有一个小问题，邮件发送是花费时间很长的时间，如果同步进行会严重阻塞业务，这里采用异步模式，你也可以配合日志记录来追溯所有的邮件发送记录

## 结果

最后的效果大约是这样的，你可以在任意需要的场景触发，或是自定义邮件的内容，任你喜欢！

![最后效果](https://blog.grtsinry43.com/uploads/2025/01/02/_20250102_211916.png_f34bc0d3-6471-4ae0-bb3d-d06d81cd0b76.png)
