最近写一个监控爬虫采集量,定时发送邮件的功能,基于springboot实现,定时任务用quartz,邮件用JaveMail,邮件模板用FreeMark
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
邮件授权码申请
邮件协议有两个
smtp:邮件发送协议
pop3:邮件接收协议
我们现在要实现的邮件发送,需要用到smtp协议
在这里我们借助第三的邮件系统的smtp服务器来。比如QQ 163等
-
1、QQ邮箱设置方式:
打开邮箱设置-》账户
生成授权码
注:(PS:QQ修改密码后,授权码需要重新生成)
2、腾讯企业邮箱的设置方式:
需要先绑定微信生成“客户端专用密码”
具体操作方式详见:https://work.weixin.qq.com/help?person_id=0&doc_id=301&helpType=exmail
与QQ邮箱的host:smtp.qq.com 不同,企业邮箱对应的host为host: smtp.qq.com
配置文件
#email
spring.mail.host=smtp.qq.com
spring.mail.username=your qq邮箱
spring.mail.password=生成授权码
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
#默认收件人
mail.receiveEmail=默认收件人邮箱
#编码格式
spring.freemarker.charset=UTF-8
#req访问request
spring.freemarker.allow-request-override=false
#缓存配置
spring.freemarker.cache=false
spring.freemarker.expose-request-attributes=false
spring.freemarker.expose-session-attributes=false
spring.freemarker.content-type=text/html
spring.freemarker.template-loader-path=classpath:/static/template/
spring.freemarker.expose-spring-macro-helpers=false
spring.freemarker.check-template-location=true
spring.freemarker.enabled=true
邮件类 Mail.java
package com.nbspider.domain;
import java.io.Serializable;
import java.util.Map;
/**
* @author: shaol
* @date: 2018年12月6日22:16:38
*/
public class Mail implements Serializable {
private static final long serialVersionUID = 1L;
//接收方邮件
private String email;
//主题
private String subject;
//模板
private String template;
// 自定义参数
private Map<String, Object> params;
public static long getSerialVersionUID() {
return serialVersionUID;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getTemplate() {
return template;
}
public void setTemplate(String template) {
this.template = template;
}
public Map<String, Object> getParams() {
return params;
}
public void setParams(Map<String, Object> params) {
this.params = params;
}
}
邮件发送service
package com.nbspider.service;
import com.nbspider.domain.Mail;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
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.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author: shaol
* @date: 2018年12月6日22:17:16
*/
@Service
public class MailSendService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private JavaMailSender javaMailSender;
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;
@Value("${spring.mail.username}")
private String USERNAME = "采集监控管理员";
/**
* 默认的收件人邮箱
*/
@Value("${mail.receiveEmail")
private String receiveEmail;
/**
* 根据模板名 获取邮件内容
*
* @param templateName
* @param params
* @return
*/
private String getMailTextByTemplateName(String templateName, Map<String, Object> params) throws IOException, TemplateException {
String mailText = "";
//通过指定模板名获取FreeMarker模板实例
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(templateName);
//FreeMarker通过Map传递动态数据
//注意动态数据的key和模板标签中指定的属性相匹配
//解析模板并替换动态数据,最终code将替换模板文件中的${code}标签。
mailText = FreeMarkerTemplateUtils.processTemplateIntoString(template, params);
return mailText;
}
public boolean sendWithHTMLTemplate(Mail mail) {
try {
//发件人的昵称
String nick = MimeUtility.encodeText(USERNAME);
//发件人是谁
InternetAddress from = new InternetAddress(nick + "<" + USERNAME + ">");
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
//添加默认收件人
String email = mail.getEmail();
if (StringUtils.isBlank(email)) {
email = this.receiveEmail;
}
mimeMessageHelper.setTo(email);
mimeMessageHelper.setFrom(from);
mimeMessageHelper.setSubject(mail.getSubject());
HashMap<String, Object> params = new HashMap<>();
// 使用模板生成html邮件内容
String result = getMailTextByTemplateName(mail.getTemplate(), mail.getParams());
mimeMessageHelper.setText(result, true);
javaMailSender.send(mimeMessage);
return true;
} catch (Exception e) {
logger.error("发送邮件失败" + e.getMessage());
return false;
}
}
}
编辑采集日志模板 data_monitor_report.ftl
注意配置文件在 resources\static\template下
- data_monitor_report.ftl
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>大数据采集系统采集监控</title>
<meta http-equiv="keywords" content="keyPairs">
<meta http-equiv="description" content="大数据采集系统采集监控">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<style type="text/css">
/* Border styles */
#table-1 thead, #table-1 tr {
border-top-width: 1px;
border-top-style: solid;
border-top-color: rgb(230, 189, 189);
}
#table-1 {
border-bottom-width: 1px;
border-bottom-style: solid;
border-bottom-color: rgb(230, 189, 189);
}
/* Padding and font style */
#table-1 td, #table-1 th {
padding: 5px 10px;
font-size: 12px;
font-family: Verdana;
color: rgb(177, 106, 104);
}
/* Alternating background colors */
#table-1 tr:nth-child(even) {
background: rgb(238, 211, 210)
}
#table-1 tr:nth-child(odd) {
background: #FFF
}
.content {
width: 500px;
margin: 50 auto;
}
</style>
</head>
<body>
<div class="content">
<h2> ${date}采集任务入库量统计</h2>
<table id="table-1">
<thead>
<td width="150">采集任务</td>
<td width="150">采集量</td>
</thead>
<#list keyPairs as keyPair>
<tr>
<td>${keyPair.name}</td>
<td>${keyPair.value}</td>
</tr>
</#list>
</table>
</div>
</body>
</html>
- freemark list遍历参考https://blog.csdn.net/pauony/article/details/80164688
如果有其他邮件,编写不同的模板即可
因为用到了模板名称渲染,所以我们根据规范,编写一个模板枚举
package com.nbspider.domain;
/**
* @author: shaol
* @date: 2018年12月6日22:18:52
*/
public enum MailTemplateNameEnum{
DataMonitorReport("data_monitor_report.ftl", "采集日志报告");
String code;
String desc;
private MailTemplateNameEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
编写测试类
package com.nbspider.spider;
import com.nbspider.domain.Mail;
import com.nbspider.domain.MailTemplateNameEnum;
import com.nbspider.service.MailSendService;
import com.nbspider.util.ConfigProperty;
import com.nbspider.site.domain.KeyPair;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.*;
/**
* @author: shaol
* @date:2018年12月6日22:19:52
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class MailSendServiceTest {
@Autowired
private MailSendService mailSendService;
@Test
public void sendWithHTMLTemplate() {
List<KeyPair> allCountLsit = new ArrayList<>();
KeyPair keyPair1 = new KeyPair("test", 1);
KeyPair keyPair2 = new KeyPair("test", 1);
KeyPair keyPair3 = new KeyPair("test", 1);
allCountLsit.add(keyPair1);
allCountLsit.add(keyPair1);
allCountLsit.add(keyPair2);
allCountLsit.add(keyPair3);
// 参数
Map<String, Object> params = new HashMap<>();
params.put("keyPairs", allCountLsit);
params.put("date", "2018年12月6日 12:00 ~ 2018年12月6日 19:00 ");
Mail mail = new Mail();
//传null会给配置文件里默认邮箱发邮件
mail.setEmail(null);
mail.setParams(params);
mail.setTemplate(MailTemplateNameEnum.DataMonitorReport.getCode());
mail.setSubject("采集监控报告");
mailSendService.sendWithHTMLTemplate(mail);
}
}
添加定时任务
-主类添加注解@EnableScheduling
- 创建定时任务类
@Slf4j
@Component
public class ScheduledService {
@Scheduled(cron = "0/5 * * * * *")
public void scheduled(){
log.info("=====>>>>>使用cron {}",System.currentTimeMillis());
}
@Scheduled(fixedRate = 5000)
public void scheduled1() {
log.info("=====>>>>>使用fixedRate{}", System.currentTimeMillis());
}
@Scheduled(fixedDelay = 5000)
public void scheduled2() {
log.info("=====>>>>>fixedDelay{}",System.currentTimeMillis());
}
}
注意
:可以看到三个定时任务都已经执行,并且使同一个线程中串行执行,如果只有一个定时任务,这样做肯定没问题,当定时任务增多,如果一个任务卡死,会导致其他任务也无法执行,多线程任务执行可以参考https://blog.csdn.net/wqh8522/article/details/79224290