SpringBoot按照模板定时发送邮件,JavaMail发送,支持FreeMark模板渲染

最近写一个监控爬虫采集量,定时发送邮件的功能,基于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邮箱设置方式:
    打开邮箱设置-》账户


    image.png

    生成授权码

  • 注:(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>

如果有其他邮件,编写不同的模板即可
因为用到了模板名称渲染,所以我们根据规范,编写一个模板枚举

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

邮件效果如下:

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容