1.概述
日常开发中,消息通知是比较常见的一种功能。比如在你购买完火车票之后,会给你发送一条短信;再比如某些网站你登陆后,会给你发一封确认邮件,等等等等。还有一些特殊场景,需要告警信息推送功能,来及时发现并解决一些问题。本文将基于springboot环境,实现邮件发送功能,并基于此功能,实现一个有意思的案例,加强应用性。
2.发送邮件功能实现
javaEE中提供了专门的包用于发送邮件(JavaMail),spring对JavaMail进行了封装,并提供了专用的接口org.springframework.mail.javamail.JavaMailSender来实现邮件发送功能,springboot也为此提供了自动化配置,同时提供了对应接口,方便开箱即用。实现发送邮件的关键步骤如下:
2.1 添加依赖
引入以下pom文件:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2.2 新增配置文件
#邮箱服务器地址,这个根据自己使用什么邮箱有区别,这里选用的是网易邮箱,平时使用较多的可能有qq、126、火狐等
spring.mail.host=smtp.163.com
spring.mail.port=25
#编码方式
spring.mail.default-encoding=UTF-8
#邮箱登陆用户名
spring.mail.username=test0220@163.com
#第三方登陆授权码
spring.mail.password=123456
#https需要开启
spring.mail.properties.mail.stmp.ssl.enable=true
spring.mail.properties.mail.stmp.ssl.required=true
#邮件接收时间的限制,单位毫秒
spring.mail.properties.mail.stmp.timeout=10000
#连接时间的限制,单位毫秒
spring.mail.properties.mail.stmp.connectiontimeout=10000
#邮件发送时间的限制,单位毫秒
spring.mail.properties.mail.stmp.writetimeout=10000
#发送者账号
mail.sender=marin@163.com
上述第三方登陆授权码与平时的邮箱登陆密码不是一样的,授权码是第三方客户端登陆邮箱的密码,授权码的获取需要去对应的邮箱官网进行开启POP3/SMTP以及IMAP/SMTP服务,由于邮箱种类较多,这里仅以网易邮箱为例,具体操作如下图所示:
2.3 编写工具类
2.3.1 编写接口MailService
public interface MailService {
//简单文本邮件
String sendSimpleMailMessage(String destination, String subject, String content);
//html邮件
String sendMimeMessage(String destination, String subject, String content);
//带附件的邮件
String sendMineMessageWithFile(String destination, String subject, String content, String filePath);
//html邮件加附件
String sendMimeMessageWithAttachment(String destination, String subject, String content, Map<String, String> maps);
}
2.3.2 实现接口MailServiceImpl
package com.eckey.lab.service.impl;
import com.eckey.lab.service.MailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.util.Map;
@Slf4j
@Service
public class MailServiceImpl implements MailService {
@Autowired
private JavaMailSender mailSender;
@Value("${mail.sender}")
private String sender;
@Override
public String sendSimpleMailMessage(String destination, String subject, String content) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(sender);
message.setTo(destination);
message.setSubject(subject);
message.setText(content);
try {
mailSender.send(message);
} catch (Exception e) {
log.error("发送简单邮件时发生异常!", e);
}
return "success";
}
@Override
public String sendMimeMessage(String destination, String subject, String content) {
MimeMessage message = mailSender.createMimeMessage();
try {
//true表示需要创建一个multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(sender);
helper.setTo(destination);
helper.setSubject(subject);
helper.setText(content, true);
mailSender.send(message);
} catch (MessagingException e) {
log.error("发送MimeMessge时发生异常!", e);
}
return "success";
}
@Override
public String sendMineMessageWithFile(String destination, String subject, String content, String filePath) {
MimeMessage message = mailSender.createMimeMessage();
try {
//true表示需要创建一个multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(sender);
helper.setTo(destination);
helper.setSubject(subject);
helper.setText(content, true);
FileSystemResource file = new FileSystemResource(new File(filePath));
String fileName = file.getFilename();
helper.addAttachment(fileName, file);
mailSender.send(message);
} catch (MessagingException e) {
log.error("发送带附件的MimeMessge时发生异常!", e);
}
return "success";
}
@Override
public String sendMimeMessageWithAttachment(String destination, String subject, String content, Map<String, String> maps) {
MimeMessage message = mailSender.createMimeMessage();
try {
//true表示需要创建一个multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(sender);
helper.setTo(destination);
helper.setSubject(subject);
helper.setText(content, true);
for (Map.Entry<String, String> entry : maps.entrySet()) {
FileSystemResource file = new FileSystemResource(new File(entry.getValue()));
helper.addInline(entry.getKey(), file);
}
mailSender.send(message);
} catch (MessagingException e) {
log.error("发送带静态文件的MimeMessge时发生异常!", e);
}
return "success";
}
}
2.3.3 编写控制器测试
package com.eckey.lab.controller;
import com.eckey.lab.service.MailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/mail")
public class MailController {
@Autowired
private MailService mailService;
//1812989950@qq.com
private static final String DESTINATION = "123456@qq.com";
private static final String SUBJECT = "这是一封爱的邮件!";
private static final String CONTENT = "横眉冷对千夫指,俯首甘为孺子牛!";
@PostMapping("/simple")
public String sendSimpleMail() {
log.info("发送简单邮件");
return mailService.sendSimpleMailMessage(DESTINATION, SUBJECT, CONTENT);
}
@PostMapping("/mine")
public String sendMineMail() {
log.info("发送HTML邮件!");
return mailService.sendMimeMessage(DESTINATION, SUBJECT, "<h1>" + CONTENT + "</h1>");
}
@PostMapping("/file")
public String sendMailWithFile() throws FileNotFoundException {
log.info("发送text邮件!");
String path = "test.txt";
File file = ResourceUtils.getFile("classpath:test.txt");
String filePath = file.getAbsolutePath();
return mailService.sendMineMessageWithFile(DESTINATION, SUBJECT, CONTENT, filePath);
}
@PostMapping("/all")
public String sendMimeMessageWithAttachment() throws FileNotFoundException {
log.info("发送带附件的邮件!");
String htmlStr = "<html><body>测试:图片1 <br> <img src=\'cid:pic1\'/> <br>图片2 <br> <img src=\'cid:pic2\'/></body></html>";
Map<String, String> rscIdMap = new HashMap<>(2);
rscIdMap.put("pic1", ResourceUtils.getFile("classpath:img/redis.jpg").getAbsolutePath());
rscIdMap.put("pic2", ResourceUtils.getFile("classpath:img/hbase.jpg").getAbsolutePath());
return mailService.sendMimeMessageWithAttachment(DESTINATION, SUBJECT, htmlStr, rscIdMap);
}
}
2.3.4 测试结果如下
1.简单邮件
2.HTML邮件
3.带附件的邮件
4.带图片的邮件
3.一个让女朋友爱到不能自拔的案例
为了充分发挥学以致用的精神,我们结合上述邮件案例来实现一个小需求。需求如下:每天定时发送一封邮件给女朋友,邮件的内容需要包括时间、当前城市、天气情况、以及一首爱的诗歌。实现这个需求包含以下条件:
1.需要一个可以实时获取天气的网站
http://wthrcdn.etouch.cn/weather_mini?city=
2.需要编写一个邮件模板
3.需要定时任务定时执行
4.需要每天写不同诗歌(可以建立一个诗歌库,每天定时获取一条记录)
3.1 编写天气信息获取接口
public interface WeatherService {
Weather getWeatherInfo(String city);
}
3.2 实现天气信息获取接口
package com.eckey.lab.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.eckey.lab.entity.TomorrowWeather;
import com.eckey.lab.entity.Weather;
import com.eckey.lab.entity.YesterdayWeather;
import com.eckey.lab.service.WeatherService;
import com.eckey.lab.util.RestTemplateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.List;
@Slf4j
@Service
public class WeatherServiceImpl implements WeatherService {
@Value("${weather.url}")
private String weatherUrl;
@Autowired
private RestTemplateUtil restTemplateUtil;
@Override
public Weather getWeatherInfo(String city) {
String url = weatherUrl + city;
String result = restTemplateUtil.sendGet(url);
log.info("weather info:{}", result);
JSONObject jsonObject = JSON.parseObject(result);
Weather weather = new Weather();
if (jsonObject.getIntValue("status") == 1000 && jsonObject.getString("desc").equals("OK")) {
String data = jsonObject.getString("data");
JSONObject weatherInfo = JSON.parseObject(data);
weather.setCity(city);
if (StringUtils.isNoneBlank(data)) {
YesterdayWeather yesterdayWeather = JSON.parseObject(weatherInfo.getString("yesterday"), YesterdayWeather.class);
weather.setYesterday(yesterdayWeather);
}
String ganmao = weatherInfo.getString("ganmao");
weather.setGanmao(ganmao);
weather.setWendu(weatherInfo.getString("wendu"));
String forecast = weatherInfo.getString("forecast");
List<TomorrowWeather> tomorrowWeathers = JSON.parseArray(forecast, TomorrowWeather.class);
TomorrowWeather tomorrowWeather = tomorrowWeathers.get(0);
weather.setCurrentWeather(tomorrowWeather);
weather.setTomorrowWeathers(tomorrowWeathers);
log.info("weather info :{}", JSON.toJSONString(weather));
}
return weather;
}
}
获取今日天气实体:
package com.eckey.lab.entity;
import lombok.Data;
import java.io.Serializable;
@Data
public class TomorrowWeather implements Serializable {
/**
* 日期
*/
private String date;
/**
* 最高温度
*/
private String high;
/**
* 风力
*/
private String fengli;
/**
* 最低温度
*/
private String low;
/**
* 风向
*/
private String fengxiang;
/**
* 天气情况(晴天、多云、小雨)
*/
private String type;
}
昨日天气实体:
package com.eckey.lab.entity;
import lombok.Data;
import lombok.ToString;
import java.io.Serializable;
@Data
@ToString
public class YesterdayWeather implements Serializable {
/**
* 日期
*/
private String date;
/**
* 最高温度
*/
private String high;
/**
* 风向
*/
private String fx;
/**
* 最低温度
*/
private String low;
/**
* 风力
*/
private String fl;
/**
* 天气情况
*/
private String type;
}
所有天气信息实体(包括昨日、今日,未来7日天气):
package com.eckey.lab.entity;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class Weather implements Serializable {
/**
* 城市
*/
private String city;
/**
* 昨天天气
*/
private YesterdayWeather yesterday;
/**
* 根据天气温馨提示语
*/
private String ganmao;
/**
* 当前温度
*/
private String wendu;
/**
* 未来天气
*/
private List<TomorrowWeather> tomorrowWeathers;
/**
* 今日天气
*/
private TomorrowWeather currentWeather;
}
3.3 编写邮件工具类
package com.eckey.lab.util;
import com.eckey.lab.entity.TomorrowWeather;
import com.eckey.lab.entity.Weather;
import com.eckey.lab.enums.MonthEnum;
import com.eckey.lab.enums.WeatherEnum;
import com.eckey.lab.service.MailService;
import com.eckey.lab.service.WeatherService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
@Slf4j
@Component
public class MailUtil {
@Autowired
private TemplateEngine templateEngine;
@Autowired
private MailService mailService;
@Autowired
private WeatherService weatherService;
private static final String DESTINATION = "18@qq.com";
private static final String SUBJECT = "这是一封爱的邮件!";
public void sendSimpleMail(String city) {
Weather weatherInfo = weatherService.getWeatherInfo(city);
Context context = new Context();
HashMap<String, Object> maps = new HashMap<>();
Date date = new Date();
maps.put("week", getWeek(date));
maps.put("city", city);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String format = simpleDateFormat.format(date);
String[] months = format.split("-");
String internal = MonthEnum.getInternal(Integer.valueOf(months[1]));
StringBuffer stringBuffer = new StringBuffer(months[2]).append(" ").append(internal).append(" ").append(months[0]);
maps.put("date", stringBuffer);
if (weatherInfo != null) {
TomorrowWeather currentWeather = weatherInfo.getCurrentWeather();
if (StringUtils.isNotBlank(currentWeather.getHigh())) {
maps.put("high", getTemperature(currentWeather.getHigh()));
}
if (StringUtils.isNotBlank(currentWeather.getLow())) {
maps.put("low", getTemperature(currentWeather.getLow()));
}
if (StringUtils.isNotBlank(weatherInfo.getGanmao())) {
maps.put("message", weatherInfo.getGanmao());
}
if (StringUtils.isNotBlank(currentWeather.getFengxiang())) {
maps.put("fengxiang", currentWeather.getFengxiang());
}
if (StringUtils.isNotBlank(currentWeather.getType())) {
String type = currentWeather.getType();
maps.put("type", type);
if (type.contains("晴")) {
maps.put("icon", WeatherEnum.SUNNY);
} else if (type.contains("雨")) {
maps.put("icon", WeatherEnum.RAINY);
} else if (type.contains("雪")) {
maps.put("icon", WeatherEnum.SNOW);
} else {
maps.put("icon", WeatherEnum.CLOUD);
}
}
if (StringUtils.isNotBlank(currentWeather.getFengli())) {
String[] result = currentWeather.getFengli().split("\\[");
String[] split = result[2].split("\\]");
maps.put("fengli", split[0]);
}
}
context.setVariables(maps);
String content = this.templateEngine.process("mail", context);
mailService.sendMimeMessage(DESTINATION, SUBJECT, content);
mailService.sendMimeMessage("2@qq.com",SUBJECT,content);
}
public String getTemperature(String str) {
String trim = str.trim();
String[] strings = trim.split(" ");
if (strings.length == 2) {
return strings[1];
} else
return "10";
}
//根据日期取得星期几
public static String getWeek(Date date) {
String[] weeks = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int week_index = cal.get(Calendar.DAY_OF_WEEK) - 1;
if (week_index < 0) {
week_index = 0;
}
return weeks[week_index];
}
}
3.4 编写定时任务
package com.eckey.lab.scheduler;
import com.eckey.lab.service.MailService;
import com.eckey.lab.service.WeatherService;
import com.eckey.lab.util.MailUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Slf4j
@Component
public class MyScheduler {
@Autowired
private MailUtil mailUtil;
public static final String city = "杭州";
//每天上午9点执行
@Scheduled(cron = "0 0 9 * * ?")
public void sendMessage() {
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = simpleDateFormat.format(date);
mailUtil.sendSimpleMail(city);
}
}
3.5 邮件模板
邮件模板选择使用thymeleaf,将获取到的天气信息和诗歌信息写入模板中,前端模板如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<script src="https://unpkg.com/feather-icons"></script>
<style>
@import url('https://fonts.googleapis.com/css?family=Montserrat:400,700,900&display=swap');
:root {
--gradient: linear-gradient( 135deg, #72EDF2 10%, #5151E5 100%);
}
* {
-webkit-box-sizing: border-box;
box-sizing: border-box;
line-height: 1.25em;
}
.clear {
clear: both;
}
body {
margin: 0;
width: 100%;
height: 100vh;
font-family: 'Montserrat', sans-serif;
background-color: #ffffff;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
.container {
border-radius: 25px;
-webkit-box-shadow: 0 0 70px -10px rgba(0, 0, 0, 0.2);
box-shadow: 0 0 70px -10px rgba(0, 0, 0, 0.2);
background-color: #ffffff;
color: #000000;
height: 400px;
}
.weather-side {
position: relative;
height: 100%;
border-radius: 25px;
background-image: url("//repo.bfw.wiki/bfwrepo/image/5f26af591b919.png");
width: 300px;
-webkit-box-shadow: 0 0 20px -10px rgba(0, 0, 0, 0.2);
box-shadow: 0 0 20px -10px rgba(0, 0, 0, 0.2);
-webkit-transition: -webkit-transform 300ms ease;
transition: -webkit-transform 300ms ease;
-o-transition: transform 300ms ease;
transition: transform 300ms ease;
transition: transform 300ms ease, -webkit-transform 300ms ease;
-webkit-transform: translateZ(0) scale(1.02) perspective(1000px);
transform: translateZ(0) scale(1.02) perspective(1000px);
float: left;
}
.weather-side:hover {
-webkit-transform: scale(1.1) perspective(1500px) rotateY(10deg);
transform: scale(1.1) perspective(1500px) rotateY(10deg);
}
.weather-gradient {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-image: var(--gradient);
border-radius: 25px;
opacity: 0.8;
}
.date-container {
position: absolute;
top: 25px;
left: 25px;
}
.date-dayname {
margin: 0;
}
.date-day {
display: block;
}
.location {
display: inline-block;
margin-top: 10px;
}
.location-icon {
display: inline-block;
height: 0.8em;
width: auto;
margin-right: 5px;
}
.weather-container {
position: absolute;
bottom: 25px;
left: 25px;
}
.weather-icon.feather {
height: 60px;
width: auto;
}
.weather-temp {
margin: 0;
font-weight: 700;
font-size: 4em;
}
.weather-desc {
margin: 0;
}
.info-side {
position: relative;
float: left;
height: 100%;
padding-top: 25px;
}
.today-info {
padding: 15px;
margin: 0 25px 25px 25px;
/* box-shadow: 0 0 50px -5px rgba(0, 0, 0, 0.25); */
border-radius: 10px;
}
.today-info>div:not(:last-child) {
margin: 0 0 10px 0;
}
.today-info>div .title {
float: left;
font-weight: 700;
}
.today-info>div .value {
float: right;
}
.week-list {
list-style-type: none;
padding: 0;
margin: 10px 35px;
-webkit-box-shadow: 0 0 50px -5px rgba(0, 0, 0, 0.25);
box-shadow: 0 0 50px -5px rgba(0, 0, 0, 0.25);
border-radius: 10px;
background: #000000;
}
.week-list>li {
float: left;
padding: 15px;
cursor: pointer;
-webkit-transition: 200ms ease;
-o-transition: 200ms ease;
transition: 200ms ease;
border-radius: 10px;
}
.week-list>li:hover {
-webkit-transform: scale(1.1);
-ms-transform: scale(1.1);
transform: scale(1.1);
background: #fff;
color: #222831;
-webkit-box-shadow: 0 0 40px -5px rgba(0, 0, 0, 0.2);
box-shadow: 0 0 40px -5px rgba(0, 0, 0, 0.2)
}
.week-list>li.active {
background: #fff;
color: #222831;
border-radius: 10px;
}
.week-list>li .day-name {
display: block;
margin: 10px 0 0 0;
text-align: center;
}
.week-list>li .day-icon {
display: block;
height: 30px;
width: auto;
margin: 0 auto;
}
.week-list>li .day-temp {
display: block;
text-align: center;
margin: 10px 0 0 0;
font-weight: 700;
}
.location-container {
padding: 25px 35px;
}
.location-button {
outline: none;
width: 100%;
-webkit-box-sizing: border-box;
box-sizing: border-box;
border: none;
border-radius: 15px;
padding: 10px;
font-family: 'Montserrat', sans-serif;
background-image: var(--gradient);
color: #ffffff;
font-weight: 700;
-webkit-box-shadow: 0 0 30px -5px rgba(0, 0, 0, 0.25);
box-shadow: 0 0 30px -5px rgba(0, 0, 0, 0.25);
cursor: pointer;
-webkit-transition: -webkit-transform 200ms ease;
transition: -webkit-transform 200ms ease;
-o-transition: transform 200ms ease;
transition: transform 200ms ease;
transition: transform 200ms ease, -webkit-transform 200ms ease;
}
.location-button:hover {
-webkit-transform: scale(0.95);
-ms-transform: scale(0.95);
transform: scale(0.95);
}
.location-button .feather {
height: 1em;
width: auto;
margin-right: 5px;
}
</style>
</head>
<body translate="no">
<div class="container">
<div class="weather-side">
<div class="weather-gradient"></div>
<div class="date-container">
<h2 class="date-dayname" th:text="${week}"></h2><span class="date-day" th:text="${date}"></span><i class="location-icon" data-feather="map-pin"></i><span class="location" th:text="${city}">杭州</span>
</div>
<div class="weather-container">
<i class="weather-icon" th:attr="data-feather=${icon}"></i>
<h1 class="weather-temp" th:text="${high}"></h1>
<h3 class="weather-desc" th:text="${type}"></h3>
</div>
</div>
<div class="info-side">
<div class="today-info-container">
<div class="today-info">
<div class="precipitation"> <span class="title">风 向:</span><span class="value" th:text="${fengxiang}"></span>
<div class="clear"></div>
</div>
<div class="humidity"> <span class="title">风 力:</span><span class="value" th:text="${fengli}"></span>
<div class="clear"></div>
</div>
<div class="wind"> <span class="title">最低温度:</span><span class="value" th:text="${low}"></span>
<div class="clear"></div>
</div>
</div>
</div>
<div class="week-container" style="padding: 0px 40px">
<p>热情的太阳是我迷恋你的天气,</p>
<p>阴沉的乌云是我挂念你的心情,</p>
<p>绚丽的彩虹是我爱你的痕迹。</p>
</div>
<div class="location-container">
<button class="location-button"><span th:text="${message}"></span></button>
</div>
</div>
</div>
<script id="rendered-js">
feather.replace();
</script>
</body>
</html>
3.6 邮件测试案例
4.小结
1.发送邮件一封邮件需要几个关键步骤:引入依赖、开启第三方授权码、引入配置等;
2.邮件的主要类型包括简单文本文件、html邮件、附件邮件、HTML附件邮件;
3.上述案例中,诗歌模块可以进一步优化:通过将每一首三行情书进行入库操作,每天定时获取一条记录,并将该条记录id存入缓存,第二天先获取缓存中的id,并增加指定步长,再利用新id查询数据库记录,保证每一天不一样(这里的前提是:数据库中有足够的诗歌数据,该数据的主键按指定规则递增);
4.邮件模板选择使用thymeleaf,其实就是定好HTML模板,每次将获取到的指定数据写入即可;
5.上述案例主要展示关键代码,完整版代码可参考下面的git地址。
5.参考文献
1.https://juejin.cn/post/6844903827477364744
2.https://juejin.cn/post/6844903825053057037
3.https://juejin.cn/post/6844903813820710920
6.项目地址
https://gitee.com/Marinc/springboot-demos/tree/master/springboot-email