毋庸置疑,在一个企业级的管理后台中,企业发送邮件是必要的的。在没有spring Mail之前,javaMail发送邮件的步骤是繁琐的。后来spring整合了相关的Mail之后,其API接口方式就变得很简单了。其实springboot2.x版本整合spring Mail还是很简单的。这里主要还是说明一下一些具体逻辑的处理:
关于下面的三个箭头,我想就不用说明了吧,其实很核心的东西就在这个异步执行中。在企业邮件中我们会因为各种原因导致邮件发送不出去,如网络问题等,如果邮件没有发送出去的话,我我们如何进行二次甚至多次发送呢?
在多数的企业中采用了MQ的消费模式来进行处理。这里对于应用级别未达到使用tasker+redis实现邮件重发。具体实现:如果邮件发送出现异常,就将该条记录存储到Redis中,定时任务查询,如果有就发送,发送正常就删除该条K。
一些实现的逻辑说明了,我们直接上代码吧
org.springframework.boot
spring-boot-starter-mail
spring.mail.host=smtp.163.com //qq则为smtp.qq.com,其中qq需要开启smtp
spring.mail.password=mxz123456ccc
[email protected]
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
package com.config.mail.service.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.config.mail.model.MyMailProperties;
import com.config.mail.service.MailConService;
import javax.mail.internet.MimeMessage;
/**
* 实现类
* @author Owner
*
*/
@Service
public class MailConServiceImpl implements MailConService {
private final static Logger logger = LoggerFactory.getLogger(MailConServiceImpl.class);
@Autowired
private MailProperties mailProperties;
@Autowired
private JavaMailSender javaMailSender;
/**
* 发送简单文本的邮件
* @param to
* @param subject
* @param content
* @return
*/
@Override
public boolean send(String to, String subject, String content) {
logger.info("## Ready to send mail ...");
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
// 邮件发送来源
simpleMailMessage.setFrom(mailProperties.getUsername());
// 邮件发送目标
simpleMailMessage.setTo(to);
// 设置标题
simpleMailMessage.setSubject(subject);
// 设置内容
simpleMailMessage.setText(content);
try {
// 发送
javaMailSender.send(simpleMailMessage);
logger.info("=====================>Send the mail success ...");
} catch (Exception e) {
logger.error("Send mail error: ", e);
return false;
}
return true;
}
@Override
@Async("taskExecutor")
public boolean sendToSome(String[] tos, String subject, String content) {
for(String snedTo:tos) {
this.send(snedTo, subject, content);
}
return false;
}
/**
* 发送 html 的邮件
* @param to
* @param subject
* @param html
* @return
*/
@Override
public boolean sendWithHtml(String to, String subject, String html) {
logger.info("## Ready to send mail ...");
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = null;
try {
mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
// 邮件发送来源
mimeMessageHelper.setFrom(mailProperties.getUsername());
// 邮件发送目标
mimeMessageHelper.setTo(to);
// 设置标题
mimeMessageHelper.setSubject(subject);
// 设置内容,并设置内容 html 格式为 true
mimeMessageHelper.setText(html, true);
javaMailSender.send(mimeMessage);
logger.info("## Send the mail with html success ...");
} catch (Exception e) {
e.printStackTrace();
logger.error("Send html mail error: ", e);
return false;
}
return true;
}
/**
* 发送带有图片的 html 的邮件
* @param to
* @param subject
* @param html
* @param cids
* @param filePaths
* @return
*/
@Override
public boolean sendWithImageHtml(String to, String subject, String html, String[] cids, String[] filePaths) {
logger.info("## Ready to send mail ...");
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = null;
try {
mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
// 邮件发送来源
mimeMessageHelper.setFrom(mailProperties.getUsername());
// 邮件发送目标
mimeMessageHelper.setTo(to);
// 设置标题
mimeMessageHelper.setSubject(subject);
// 设置内容,并设置内容 html 格式为 true
mimeMessageHelper.setText(html, true);
// 设置 html 中内联的图片
for (int i = 0; i < cids.length; i++) {
FileSystemResource file = new FileSystemResource(filePaths[i]);
mimeMessageHelper.addInline(cids[i], file);
}
javaMailSender.send(mimeMessage);
logger.info("## Send the mail with image success ...");
} catch (Exception e) {
e.printStackTrace();
logger.error("Send html mail error: ", e);
return false;
}
return true;
}
/**
* 发送带有附件的邮件
* @param to
* @param subject
* @param content
* @param filePaths
* @return
*/
@Override
public boolean sendWithWithEnclosure(String to, String subject, String content, String[] filePaths) {
logger.info("## Ready to send mail ...");
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = null;
try {
mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
// 邮件发送来源
mimeMessageHelper.setFrom(mailProperties.getUsername());
// 邮件发送目标
mimeMessageHelper.setTo(to);
// 设置标题
mimeMessageHelper.setSubject(subject);
// 设置内容
mimeMessageHelper.setText(content);
// 添加附件
for (int i = 0; i < filePaths.length; i++) {
FileSystemResource file = new FileSystemResource(filePaths[i]);
String attachementFileName = "附件" + (i + 1);
mimeMessageHelper.addAttachment(attachementFileName, file);
}
javaMailSender.send(mimeMessage);
logger.info("## Send the mail with enclosure success ...");
} catch (Exception e) {
logger.error("Send html mail error: ", e);
return false;
}
return true;
}
}
package system;
import org.junit.Assert;
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.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import com.Application;
import com.config.mail.service.MailConService;
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = Application.class)
public class MailTests {
@Autowired
private MailConService mailService;
/**
* 测试简单文本邮件发送
*/
@Test
public void send() {
String to = "[email protected]";
String title = "标题: 简单文本邮件发送测试";
String content = "简单文本 ...";
Assert.assertTrue(mailService.send(to, title, content));
}
// /**
// * 测试html邮件发送
// */
// @Test
// public void sendHtml() {
// String to = "[email protected]";
// String title = "标题: html邮件发送测试";
//
// String htmlContent = "欢迎关注微信公众号: 小哈学Java
";
//
// Assert.assertTrue(mailService.sendWithHtml(to, title, htmlContent));
// }
/**
* 测试html邮件发送
*/
@Test
public void sendImageHtml() {
String to = "[email protected]";
String title = "标题: 带图片的html邮件发送测试";
String htmlContent = "" +
"欢迎关注微信公众号: 小哈学Java
" +
"图片1
" +
"图片2
" +
"";
// cid 要与上面 html 中的 cid 对应,否则设置图片不成功
String[] cids = new String[]{
"test",
};
// 文件路径
String[] filePaths = new String[]{
"D:\\1.jpg",
};
Assert.assertTrue(mailService.sendWithImageHtml(to, title, htmlContent, cids, filePaths));
}
// /**
// * 测试添加附件的邮件发送
// */
// @Test
// public void sendWithWithEnclosure() {
// String to = "[email protected]";
// String title = "标题: 带有附件的邮件发送测试";
// String content = "带有附件的邮件发送测试";
//
// // 添加两个附件
// String[] filePaths = new String[]{
// "D:\\1.jpg"
//
// };
//
// Assert.assertTrue(mailService.sendWithWithEnclosure(to, title, content, filePaths));
// }
}
网上,很多博客说需要整合Thymeleaf或者freemark实现邮件模板。实则个人认为没有必要,因为一旦模板设置死了,想改就很难,这里我建议使用一个富文本来保存一些常用的模板。
一般企业级邮箱,都是用的企业邮箱来发送的,那么配置文件中写死了也没有关系,但是如果我们需要后面在数据库中动态配置发送人邮箱,怎么办?我们来看一下源码,这方法视线中有这样一个Bean:
这个Bean的源码,我们来看一下:
好吧大概是明白了,是从Yml配置文件中读取spting mail先关的配置信息,那么我们重写一个子类重写其方法:
package com.config.mail.model;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
/**
* 自定义加载邮箱配置,重写加载配置文件,一般都从数据库读取
* @author Owner
*
*/
@Service
@Primary
public class MyMailProperties extends MailProperties {
public void setUsername(String username) {
super.setUsername("[email protected]");
}
}
我这里是写死了的,大家可以从数据库或者缓存中记载过来。当然了发方法接口也要稍微修改一下:
// @Autowired
// private MailProperties mailProperties;
@Autowired
private MyMailProperties mailProperties;
定时任务的逻辑已经分析过了,这里就不做实现可。