一起来学SpringBoot(十二)发送邮件

发送邮件应该是网站的必备功能之一,什么注册验证,忘记密码或者是给用户发送营销信息。最早期的时候我们会使用JavaMail相关api来写发送邮件的相关代码,后来spring退出了JavaMailSender更加简化了邮件发送的过程,在之后springboot对此进行了封装就有了现在的spring-boot-starter-mail,本章文章的介绍主要来自于此包。
首先呢肯定是要加入依赖

<!-- springboot集成的email -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>

然后呢配置下yml

spring:
mail:
host: smtp.163.com
username: 13103779727@163.com
password: nibuzhidaomima
properties:
mail:
smtp:
auth: true
timeout: 25000

写个最简单的服务

package com.mail;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;

@Component
public class MailService {

@Autowired
JavaMailSender jms;

public void send() {
//建立邮件消息
SimpleMailMessage mainMessage = new SimpleMailMessage();
//发送者
mainMessage.setFrom("13103779727@163.com");
//接收者
mainMessage.setTo("????@qq.com");
//发送的标题
mainMessage.setSubject("嗨喽");
//发送的内容
mainMessage.setText("hello world");
jms.send(mainMessage);
}
}

这样就实现了发送邮件,其实发模版html的也能实现,但是总感觉不那么大气代码量也死多死多,干脆封装下吧。


首先呢,创建一个实体

package com.maoxs.email.entity;

import lombok.Data;

import java.io.Serializable;
import java.util.Map;

/**
* @Description: 邮件实体
*
* @author fulin
* @Project
*/
@Data
public class Mail implements Serializable {

private static final long serialVersionUID = 1L;

/** 邮件标题 */
private String title;
/** 邮件内容 */
private String content;
/** 内容格式(默认html) */
private String contentType;
/** 接收邮件地址 */
private String to;

/*************模板发送****************/
/** 模板名称 */
private String templateName;
/** 模板变量替换 */
private Map<String, Object> maps;
}

创建类型枚举类

package com.maoxs.email.enums;

/**
* @Description: 邮件内容的类型
*
* @author fulin
* @Project
*/
public enum MailContentTypeEnum {

//text格式
TEXT("text"),
//html格式
HTML("html"),
//模板
TEMPLATE("template")
;

private String value;

MailContentTypeEnum(String value) {
this.value = value;
}

public String value() {
return value;
}
}

创建一个基于spring的工厂类

package com.maoxs.email.factory;

import com.maoxs.email.enums.MailContentTypeEnum;

import java.util.HashMap;
import java.util.Map;

/**
* @Description: 邮件发送bean工厂类
*
* @author fulin
* @Project
*/
public class MailFactory {

private MailFactory() {}

private static final class InnerMailFactory {
private static final MailFactory MAIL_FACTORY = new MailFactory();
}

private static Map<String, String> maps = new HashMap<>();

static {
maps.put(MailContentTypeEnum.TEXT.value(), getStrategyClassName("simple"));
maps.put(MailContentTypeEnum.HTML.value(), getStrategyClassName("html"));
maps.put(MailContentTypeEnum.TEMPLATE.value(), getStrategyClassName("template"));
}

public static final MailFactory getInstance() {
return InnerMailFactory.MAIL_FACTORY;
}

public String get(String type) {
return maps.get(type);
}

/**
* 获取策略类名
*
* @param classNamePrefix:类名前缀
* @return
*/
private static String getStrategyClassName(String classNamePrefix) {
return classNamePrefix + "MailStrategy";
}

}

创建 邮件发送策略类的接口

package com.maoxs.email.strategy;

import com.maoxs.email.entity.Mail;

/**
* @Description: 发送邮件策略接口
*
* @author fulin
* @Project
*/
public interface MailStrategy {

/**
* 发送邮件
*
* @param from:谁发送
* @param mail:邮件信息
*/
void sendMail(String from, Mail mail);

}

然后是三个基础实现类

package com.maoxs.email.strategy.impl;

import com.maoxs.email.entity.Mail;
import com.maoxs.email.strategy.MailStrategy;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

/**
* @Description: HTML格式发送邮件
*
* @author fulin
* @Project
*/
@Component
public class HtmlMailStrategy implements MailStrategy {
private static final Logger LOG = LogManager.getLogger("bizLog");

@Autowired
private JavaMailSender javaMailSender;

@Override
public void sendMail(String from, Mail mail) {
MimeMessage message = javaMailSender.createMimeMessage();
try {
//true表示需要创建一个multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(mail.getTo());
helper.setSubject(mail.getTitle());
helper.setText(mail.getContent(), true);
javaMailSender.send(message);
LOG.info("html邮件已经发送{}。", mail.getTo());
} catch (MessagingException e) {
LOG.error("发送html邮件时发生异常!", e);
}
}
}
package com.maoxs.email.strategy.impl;

import com.maoxs.email.entity.Mail;
import com.maoxs.email.strategy.MailStrategy;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;

/**
* @Description: 简单文本发送邮件
*
* @author fulin
* @Project
*/
@Component
public class SimpleMailStrategy implements MailStrategy {
private static final Logger LOG = LogManager.getLogger("bizLog");

@Autowired
private JavaMailSender javaMailSender;

@Override
public void sendMail(String from, Mail mail) {
SimpleMailMessage message = new SimpleMailMessage();
try {
message.setFrom(from);
message.setTo(mail.getTo());
message.setSubject(mail.getTitle());
message.setText(mail.getContent());
javaMailSender.send(message);
LOG.info("纯文本的邮件已经发送给【{}】。", mail.getTo());
} catch (Exception e) {
LOG.error("纯文本邮件发送时发生异常!", e);
}
}

}
package com.maoxs.email.strategy.impl;

import com.maoxs.email.entity.Mail;
import com.maoxs.email.strategy.MailStrategy;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.Locale;
import java.util.Map;

/**
* @Description: 自定义模板格式发送邮件
*
* @author fulin
* @Project
*/
@Component
public class TemplateMailStrategy implements MailStrategy {
private static final Logger LOG = LogManager.getLogger("bizLog");

@Autowired
private JavaMailSender javaMailSender;
@Autowired
private TemplateEngine templateEngine;

@Override
public void sendMail(String from, Mail mail) {
final Context ctx = new Context(new Locale(""));
if (null != mail.getMaps() && mail.getMaps().size() != 0) {
for (Map.Entry<String, Object> entry : mail.getMaps().entrySet()) {
ctx.setVariable(entry.getKey(), entry.getValue());
}
}
final String htmlContent = templateEngine.process(mail.getTemplateName(), ctx);
final MimeMessage mimeMessage = javaMailSender.createMimeMessage();
final MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8");
try {
message.setFrom(from);
message.setTo(mail.getTo());
message.setSubject(mail.getTitle());
message.setText(htmlContent, true);
javaMailSender.send(mimeMessage);
LOG.info("模板邮件已经发送{}。", mail.getTo());
} catch (MessagingException e) {
e.printStackTrace();
LOG.error("发送模板邮件【{}】时发生异常!", mail.getTemplateName());
}
}
}

然后就是邮件的发送类

package com.maoxs.email.biz;

import com.maoxs.email.entity.Mail;
import com.maoxs.email.enums.MailContentTypeEnum;
import com.maoxs.email.factory.MailFactory;
import com.maoxs.email.strategy.MailStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
* @Description: 邮件发送类
*
* @author fulin
* @Project
**/
@Component
public class MailBiz {
@Autowired
private Map<String, MailStrategy> mailStrategy;

@Value("${spring.mail.username}")
private String from;

/** 邮件实体 */
private Mail mail = new Mail();

/**
* 设置邮件标题
*
* @param title:邮件标题
* @return
*/
public MailBiz title(String title) {
mail.setTitle(title);
return this;
}

/**
* 设置邮件内容
*
* @param content:内容
* @return
*/
public MailBiz content(String content) {
mail.setContent(content);
return this;
}

/**
* 设置邮件格式
*
* @param typeEnum:邮件格式
* @return
*/
public MailBiz contentType(MailContentTypeEnum typeEnum) {
mail.setContentType(typeEnum.value());
return this;
}

/**
* 设置请求目标邮件地址
*
* @param to:请求目标邮件地址
* @return
*/
public MailBiz to(String to) {
mail.setTo(to);
return this;
}

/**
* 设置模板名称
*
* @param templateName
* @return
*/
public MailBiz templateName(String templateName) {
mail.setTemplateName(templateName);
return this;
}

/**
* 模板发送的变量
*
* @param maps:maps
* @return
*/
public MailBiz maps(Map<String, Object> maps) {
mail.setMaps(maps);
return this;
}

/**
* 执行发送邮件
*/
public void send() {
String key = MailFactory.getInstance().get(mail.getContentType());
this.mailStrategy.get(key).sendMail(from, mail);
}
}

然后呢就封装完毕了 在发送类里使用了对象级联的方式,使其调用更方便如果使用的是freemarker

这边我也提供了发送方式,建议大家自己扩展。

package com.maoxs.email.strategy.impl;

import com.maoxs.email.entity.Mail;
import com.maoxs.email.strategy.MailStrategy;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.IOException;

@Component
public class FreemarkerMailStrategy implements MailStrategy {
private static final Logger LOG = LogManager.getLogger("bizLog");

@Autowired
private Configuration configuration;
@Autowired
private JavaMailSender javaMailSender;

@Override
public void sendMail(String from, Mail mail) {
try {
final Template t = configuration.getTemplate(mail.getTemplateName());
final String htmlContent = FreeMarkerTemplateUtils.processTemplateIntoString(t, mail.getMaps());
final MimeMessage mimeMessage = javaMailSender.createMimeMessage();
final MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8");
message.setFrom(from);
message.setTo(mail.getTo());
message.setSubject(mail.getTitle());
message.setText(htmlContent, true);
javaMailSender.send(mimeMessage);
LOG.info("FreeMarker模板邮件已经发送{}。", mail.getTo());
} catch (IOException e) {
e.printStackTrace();
} catch (TemplateException e) {
e.printStackTrace();
} catch (MessagingException e) {
e.printStackTrace();
LOG.error("FreeMarker发送模板邮件【{}】时发生异常!", mail.getTemplateName());
}
}
}

具体怎么使用呢 来看下测试类

package com;

import com.maoxs.email.biz.MailBiz;
import com.maoxs.email.enums.MailContentTypeEnum;
import com.mail.MailService;
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.SpringRunner;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestApplicationTests {
@Autowired
private MailBiz mailBiz;

@Test
public void sendMail() {
mailBiz
.contentType(MailContentTypeEnum.HTML)
.title("测试")
.to("??????@qq.com")
.content("<h1>测试content</h1>")
.send();
}


@Test
public void sendMail2() {
Map<String, Object> params = new HashMap<>();
params.put("username", "昴先生");
params.put("methodName", "???");
params.put("errorMessage", "出错了唉");
params.put("occurredTime", new Date());
mailBiz
.contentType(MailContentTypeEnum.TEMPLATE)
.title("测试")
.to("??????@qq.com")
.content("测试content")
.templateName("Test")
.maps(params)
.send();
}
}

然后呢这是邮件 thy的 由于上面提供了freemarker的发送方式,建议大家自己扩展

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<title>Hello World</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css"
th:href="@{https://cdn.bootcss.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css}"/>
</head>
<style type="text/css">
.full-screen {
height: fit-content;
width: fit-content;
}

.fixed-page {
overflow-x: scroll;
overflow-y: scroll;
}

.container {
width: 100%;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto
}

.jumbotron {
padding: 2rem 1rem;
margin-bottom: 2rem;
background-color: #e9ecef;
border-radius: .3rem
}

.jumbotron-fluid {
padding-right: 0;
padding-left: 0;
border-radius: 0
}

</style>
<body class="fixed-page">
<table width="100%" border="0" align="center" cellpadding="0" cellspacing="0" bgcolor="#ffffff"
style="font-family:'Microsoft YaHei';">
<div class="jumbotron jumbotron-fluid full-screen">
<div class="container full-screen">
<img class="c-img c-img6" src="https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2222976400,3502441702&fm=58&bpow=440&bpoh=440">
<img class="currentImg" id="currentImg" onload="alog && alog('speed.set', 'c_firstPageComplete', +new Date); alog.fire && alog.fire('mark');" src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1532597177454&di=aec784f189c38b207ee88ea296f2457f&imgtype=0&src=http%3A%2F%2Fimg5.duitang.com%2Fuploads%2Fblog%2F201308%2F13%2F20130813185613_T4nxf.thumb.700_0.jpeg" data-ispreload="0" width="485.91370558376" height="547" style="top: 0px; left: 382px; width: 318.02px; height: 358px; cursor: pointer;" log-rightclick="p=5.102" title="点击查看源网页">
<img class="currentImg" id="currentImg" onload="alog && alog('speed.set', 'c_firstPageComplete', +new Date); alog.fire && alog.fire('mark');" src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1532597216973&di=5a1b48da42304a2dd63ec9697084bb11&imgtype=0&src=http%3A%2F%2Fi2.hdslb.com%2Fbfs%2Farchive%2Fb55830fe8fb1d3221b3494442b0b5c1902c6ef3a.jpg" width="288.71287128713" height="162" style="top: 48px; left: 397px; width: 288.713px; height: 162px; cursor: pointer;" log-rightclick="p=5.102" title="点击查看源网页">
<img class="currentImg" id="currentImg" onload="alog && alog('speed.set', 'c_firstPageComplete', +new Date); alog.fire && alog.fire('mark');" src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1532597216973&di=5d72892a3193c4462b45d87f6a5370b2&imgtype=0&src=http%3A%2F%2Fi2.hdslb.com%2Fbfs%2Fface%2Faff582421bd0e8331255cb92622328d19616e2c0.jpg" width="288.71287128713" height="162" style="top: 29px; left: 392px; width: 300px; height: 300px; cursor: pointer; display: block;" log-rightclick="p=5.102" title="点击查看源网页">
<h3>Hi,
<span th:text="${username}">Developer</span>
</h3>
<p>There is an exception occurred in method
<code style="color: #d57e13;"><span th:text="${methodName}">methodName</span></code>,
the error message is :
</p>
<div>

<pre>
<code style="font-family: 'Source Code Pro';">
<span th:text="${errorMessage}">Error Message</span>
</code>
</pre>
</div>
<span th:text="${occurredTime}">occurredTime</span>
</div>

</div>
</table>
</body>
</html>

然后呢我们跑去邮箱里去看看

okokok,完事,这边还有一个多邮件轮训发送没有扩展,有时间在扩展

本博文是基于springboot2.x 如果有什么不对的请在下方留言。