SpringBoot整合FreeMarker+email+Quartz定时发送一封模板邮件
一、FreeMarker
1.概述![](https://img-blog.csdnimg.cn/77ef7a6f36374b79839f37803e1e0b84.png)
2.简介
FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 是一个Java类库。
FreeMarker 被设计用来生成 HTML Web 页面,特别是基于 MVC 模式的应用程序,将视图从业务逻辑中抽离处理,业务中不再包括视图的展示,而是将视图交给 FreeMarker 来输出。虽然 FreeMarker 具有一些编程的能力,但通常由 Java 程序准备要显示的数据,由 FreeMarker 生成页面,通过模板显示准备的数据。
3.FreeMarker环境搭建
3.1 在maven项目里添加依赖(已经引入了springboot依赖)
<!-- FreeMarker依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
(ps:我springboot的版本是2.7.9如下图:)
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.9</version>
</parent>
3.2 配置properties里面FreeMarker的相关配置
#freemarker??
#允许mvc使用freemarker
spring.freemarker.enabled=true
#读取的文件路径
spring.freemarker.template-loader-path=classpath:/templates/
#freemarker默认后缀名.ftl
spring.freemarker.suffix=.ftl
#编码格式
spring.freemarker.charset=UTF-8
#响应返回类型
spring.freemarker.content-type=application/pdf;charset=UTF-8
3.3 在resources目录下面创建templates目录,在templates目录下面创建index.ftl文件
注意index的文件名后缀一定是.ftl(index可以任意取),因为我们的配置文件里配置了FreeMarker的默认后缀名是.ftl
3.4 编辑index.ftl模板文件
3.5 介绍FreeMarker的一些数据类型输出模板
<#--
数据类型:日期类型
在freemarker中日期类型不能直接输出;如果输出要先转成日期型或字符串
1. 年月日 ?date
2. 时分秒 ?time
3. 年月日时分秒 ?datetime
4. 指定格式 ?string("自定义格式")
y:年 M:月 d:日
H:时 m:分 s:秒
-->
<#-- 输出日期格式 -->
${createDate?date} <br>
<#-- 输出时间格式 -->
${createDate?time} <br>
<#-- 输出日期时间格式 -->
${createDate?datetime} <br>
<#-- 输出格式化日期格式 -->
${createDate?string("yyyy年MM月dd日 HH:mm:ss")} <br>
<#--
数据类型:布尔类型
在freemarker中布尔类型不能直接输出;如果输出要先转成字符串
方式一:?c
方式二:?string 或 ?string("true时的文本","false时的文本")
-->
${flag?c}<br>
${flag?string}<br>
${flag?string("yes","no")}<br>
<#--
数据类型:数值类型
在freemarker中数值类型可以直接输出;
1. 转字符串
普通字符串 ?c
货币型字符串 ?string.currency
百分比型字符串 ?string.percent
2. 保留浮点型数值指定小数位(#表示一个小数位)
?string["0.##"]
-->
<#-- 直接输出数值型 -->
${age} <br>
${salary} <br>
<#-- 将数值转换成字符串输出 -->
${salary?c} <br>
<#-- 将数值转换成货币类型的字符串输出 -->
${salary?string.currency} <br>
<#-- 将数值转换成百分比类型的字符串输出 -->
${avg?string.percent} <br>
<#-- 将浮点型数值保留指定小数位输出 (##表示保留两位小数) -->
${avg?string["0.##"]} <br>
<#--
数据类型:字符串类型
在freemarker中字符串类型可以直接输出;
1. 截取字符串(左闭右开) ?substring(start,end)
2. 首字母小写输出 ?uncap_first
3. 首字母大写输出 ?cap_first
4. 字母转小写输出 ?lower_case
5. 字母转大写输出 ?upper_case
6. 获取字符串长度 ?length
7. 是否以指定字符开头(boolean类型) ?starts_with("xx")?string
8. 是否以指定字符结尾(boolean类型) ?ends_with("xx")?string
9. 获取指定字符的索引 ?index_of("xx")
10. 去除字符串前后空格 ?trim
11. 替换指定字符串 ?replace("xx","xx")
-->
<#-- 直接输出 -->
${msg1} - ${msg2} <br>
${msg1?string} - ${msg2?string} <br>
<#-- 1. 截取字符串(左闭右开) ?substring(start,end) -->
${msg2?substring(1,4)} <br>
<#-- 2. 首字母小写输出 ?uncap_first -->
${msg1?uncap_first} <br>
<#-- 3. 首字母大写输出 ?cap_first -->
${msg2?cap_first} <br>
<#-- 4. 字母转小写输出 ?lower_case -->
${msg1?lower_case} <br>
<#-- 5. 字母转大写输出 ?upper_case -->
${msg1?upper_case} <br>
<#-- 6. 获取字符串长度 ?length -->
${msg1?length} <br>
<#-- 7. 是否以指定字符开头(boolean类型) ?starts_with("xx")?string -->
${msg1?starts_with("H")?string} <br>
<#-- 8. 是否以指定字符结尾(boolean类型) ?ends_with("xx")?string -->
${msg1?ends_with("h")?string} <br>
<#-- 9. 获取指定字符的索引 ?index_of("xxx") -->
${msg1?index_of("e")} <br>
<#-- 10. 去除字符串前后空格 ?trim -->
${msg1?trim?length} <br>
<#-- 11. 替换指定字符串 ?replace("xx","xxx") -->
${msg1?replace("o","a")}<br>
二、email(以QQ邮箱为例)
1.springboot整合email的准备工作
我们要使用QQ邮箱的话,首先要获取到QQ邮箱的客户端授权码。
首先到QQ邮箱的设置里面去
再点击账号
找到以下图片里的服务
开启服务要发送短信,按照指示来就好
发送成功后获得授权码,建议将授权码复制下来,后续的配置文件需要用到授权码
2.email环境搭建
2.1 在maven项目里添加依赖(已经引入了springboot依赖)
<!-- 邮箱依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2..2 配置properties里面FreeMarker的相关配置
三、Quartz
1.简介
Quartz 是 OpenSymphony 开源组织在 Job Scheduling 领域又一个开源项目,是完全由 Java 开发的一个开源任务日程管理系统,“任务进度管理器”就是一个在预先确定(被纳入日程)的时间到达时,负责执行(或者通知)其他软件组件的系统。 Quartz 是一个开源的作业调度框架,它完全由 Java 写成,并设计用于 J2SE 和 J2EE 应用中,它提供了巨大的灵活性而不牺牲简单性
官方网站:http://quartz-scheduler.org/
2.核心概念
Scheduler:Quartz 中的任务调度器,通过 Trigger 和 JobDetail 可以用来调度、暂停和删除任务。调度器就相当于一个容器,装载着任务和触发器,该类是一个接口,代表一个 Quartz 的独立运行容器,Trigger 和 JobDetail 可以注册到 Scheduler 中,两者在 Scheduler 中拥有各自的组及名称,组及名称是 Scheduler 查找定位容器中某一对象的依据,Trigger 的组及名称必须唯一,JobDetail 的组和名称也必须唯一(但可以和 Trigger 的组和名称相同,因为它们是不同类型的)。
Trigger:Quartz 中的触发器,是一个类,描述触发 Job 执行的时间触发规则,主要有 SimpleTrigger 和 CronTrigger 这两个子类。当且仅当需调度一次或者以固定时间间隔周期执行调度,SimpleTrigger 是最适合的选择;而 CronTrigger 则可以通过 Cron 表达式定义出各种复杂时间规则的调度方案:如工作日周一到周五的 15:00 ~ 16:00 执行调度等。
JobDetail:Quartz 中需要执行的任务详情,包括了任务的唯一标识和具体要执行的任务,可以通过 JobDataMap 往任务中传递数据。
Job:Quartz 中具体的任务,包含了执行任务的具体方法。是一个接口,只定义一个方法 execute() 方法,在实现接口的 execute() 方法中编写所需要定时执行的 Job。
3.Cron 表达式
Cron 表达式是一个字符串,包括 6~7 个时间元素,在 Quartz 中可以用于指定任务的执行时间
3.1 Cron 语法
Seconds Minutes Hours DayofMonth Month DayofWeek
秒 分钟 小时 日期 天/日 月份 星期
3.2. Cron 语法中每个时间元素的说明
3.3. Cron 语法中特殊字符说明
3.4. 在线 Cron 表达式生成器
其实 Cron 表达式无需多记,需要使用的时候直接使用在线生成器就可以了,地址:https://cron.qqe2.com/
4.Quartz环境搭建
4.1 在maven项目里添加依赖(已经引入了springboot依赖)
<!-- springboot整合定时器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
四、项目
前期准备工作都完成了,正式开始编写项目
1.项目结构如下:
2.创建Email对象类
package com.yc.email;
import com.alibaba.fastjson.JSON;
import lombok.Data;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Data
public class Email {
//邮件发送方
private String sender;
//邮件接收方
private List<String> receiverList = new ArrayList<>();
//邮件抄送方
private List<String> ccList = new ArrayList<>();
//邮件主题
private String subject;
//邮件内容
private String content;
//目前不使用
//嵌入式资源
private Map<String,String> inlineMap = new HashMap<>();
/**
* 附件列表(value:filePath)
*/
private List<String> attachmentList = new ArrayList<>();
public String[] getReceiverArray() {
return this.receiverList.toArray(new String[0]);
}
public String[] getCcArray() {
return this.ccList.toArray(new String[0]);
}
public String toJsonString() {
return JSON.toJSONString(this);
}
/**
* 邮件对象主要包含:
* 邮件发送方(sender)
* 邮件接收方(receiverList),支持发送多人
* 邮件抄送方(ccList),支持抄送多人
* 邮件主题(subject)
* 邮件内容(content),可以使用freeMark模板
* 嵌入式资源(inlineMap),支持嵌入多个资源
* 附件列表(attachmentList),支持多个附件发送
*/
}
3.编写EmailService类
package com.yc.email;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@Service
@Slf4j
public class EmailService {
@Autowired
private JavaMailSenderImpl mailSender;
@Autowired
private FreeMarkerConfigurer configurer;
/**
* 发送复杂的邮件 用来发送html邮件、带附件的邮件、有静态资源(图片)的邮件
* @param email
* @return
*/
public boolean sendSimpleEmail(Email email,String templateName, Map<String,Object> data){
log.info("发送复杂邮件信息:{}",email.toJsonString());
//实例化MimeMessage
MimeMessage mimeMessage = mailSender.createMimeMessage();
try{
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage);
//设置邮件发件人
mimeMessageHelper.setFrom(email.getSender());
//设置邮件接收人(可以多人)
mimeMessageHelper.setTo(email.getReceiverArray());
//设置邮件抄送
String[] ccArray = email.getCcArray();
if(ccArray.length > 0){
mimeMessageHelper.setCc(ccArray);//普通抄送,邮件接收人可以看见发件人
}
//设置邮件主题
mimeMessageHelper.setSubject(email.getSubject());
//通过模板获取文件
String content = getContent(templateName,data);
//设置邮件内容
mimeMessageHelper.setText(content,true);
//批量处理嵌入式资源
Map<String,String> inlineMap = email.getInlineMap();
for(Map.Entry<String,String> entry : inlineMap.entrySet()){
String contentId = entry.getKey();
String filePath = entry.getValue();
//设置嵌入式资源
mimeMessageHelper.addInline(contentId,new File(filePath));
}
//批量处理附件
List<String> attachmentList = email.getAttachmentList();
for(int i = 0;i < attachmentList.size(); i++){
String filePath = attachmentList.get(i);
String fileName = i + "-" + filePath.substring(filePath.lastIndexOf(File.separator));
//设置附件
mimeMessageHelper.addAttachment(fileName,new File(filePath));
}
//发送复杂邮件
mailSender.send(mimeMessage);
return true;
}catch (Exception e){
log.error(e.getMessage());
}
return false;
}
public String getContent(String templateName,Map<String,Object> data) throws IOException, TemplateException {
//获取模板
Template template = configurer.getConfiguration().getTemplate(templateName,"UTF-8");
//获取数据并把模板转为字符串
return FreeMarkerTemplateUtils.processTemplateIntoString(template,data);
}
}
4.编写任务类-MyJob
5.编写启动类
package com.yc;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableScheduling;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@SpringBootApplication
@Slf4j
@EnableSwagger2
@EnableScheduling //开启定时器
@EnableAspectJAutoProxy //表示启用 AspectJ支持
public class AppMain {
public static void main(String[] args) {
SpringApplication.run(AppMain.class);
}
}
启动程序即可
五、测试效果