最近寫一個監(jiān)控爬蟲采集量,定時發(fā)送郵件的功能,基于springboot實現(xiàn),定時任務(wù)用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>
郵件授權(quán)碼申請
郵件協(xié)議有兩個
smtp:郵件發(fā)送協(xié)議
pop3:郵件接收協(xié)議
我們現(xiàn)在要實現(xiàn)的郵件發(fā)送,需要用到smtp協(xié)議
在這里我們借助第三的郵件系統(tǒng)的smtp服務(wù)器來。比如QQ 163等
-
1、QQ郵箱設(shè)置方式:
打開郵箱設(shè)置-》賬戶
image.png
生成授權(quán)碼
注:(PS:QQ修改密碼后,授權(quán)碼需要重新生成)
2、騰訊企業(yè)郵箱的設(shè)置方式:
需要先綁定微信生成“客戶端專用密碼”
具體操作方式詳見:https://work.weixin.qq.com/help?person_id=0&doc_id=301&helpType=exmail
與QQ郵箱的host:smtp.qq.com 不同,企業(yè)郵箱對應(yīng)的host為host: smtp.qq.com
配置文件
#email
spring.mail.host=smtp.qq.com
spring.mail.username=your qq郵箱
spring.mail.password=生成授權(quán)碼
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;
// 自定義參數(shù)
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;
}
}
郵件發(fā)送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 = "采集監(jiān)控管理員";
/**
* 默認的收件人郵箱
*/
@Value("${mail.receiveEmail")
private String receiveEmail;
/**
* 根據(jù)模板名 獲取郵件內(nèi)容
*
* @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傳遞動態(tài)數(shù)據(jù)
//注意動態(tài)數(shù)據(jù)的key和模板標簽中指定的屬性相匹配
//解析模板并替換動態(tài)數(shù)據(jù),最終code將替換模板文件中的${code}標簽。
mailText = FreeMarkerTemplateUtils.processTemplateIntoString(template, params);
return mailText;
}
public boolean sendWithHTMLTemplate(Mail mail) {
try {
//發(fā)件人的昵稱
String nick = MimeUtility.encodeText(USERNAME);
//發(fā)件人是誰
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郵件內(nèi)容
String result = getMailTextByTemplateName(mail.getTemplate(), mail.getParams());
mimeMessageHelper.setText(result, true);
javaMailSender.send(mimeMessage);
return true;
} catch (Exception e) {
logger.error("發(fā)送郵件失敗" + 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>大數(shù)據(jù)采集系統(tǒng)采集監(jiān)控</title>
<meta http-equiv="keywords" content="keyPairs">
<meta http-equiv="description" content="大數(shù)據(jù)采集系統(tǒng)采集監(jiān)控">
<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}采集任務(wù)入庫量統(tǒng)計</h2>
<table id="table-1">
<thead>
<td width="150">采集任務(wù)</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
如果有其他郵件,編寫不同的模板即可
因為用到了模板名稱渲染,所以我們根據(jù)規(guī)范,編寫一個模板枚舉
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);
// 參數(shù)
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會給配置文件里默認郵箱發(fā)郵件
mail.setEmail(null);
mail.setParams(params);
mail.setTemplate(MailTemplateNameEnum.DataMonitorReport.getCode());
mail.setSubject("采集監(jiān)控報告");
mailSendService.sendWithHTMLTemplate(mail);
}
}
添加定時任務(wù)
-主類添加注解@EnableScheduling
- 創(chuàng)建定時任務(wù)類
@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());
}
}
注意:可以看到三個定時任務(wù)都已經(jīng)執(zhí)行,并且使同一個線程中串行執(zhí)行,如果只有一個定時任務(wù),這樣做肯定沒問題,當定時任務(wù)增多,如果一個任務(wù)卡死,會導(dǎo)致其他任務(wù)也無法執(zhí)行,多線程任務(wù)執(zhí)行可以參考https://blog.csdn.net/wqh8522/article/details/79224290
郵件效果如下:

