SpringBoot | 第二十六章:邮件发送
前言
讲解了日志相关的知识点后。今天来点相对简单的,一般上,我们在开发一些注册功能、发送验证码或者订单服务时,都会通过短信或者邮件的方式通知消费者,注册或者订单的相关信息。而且基本上邮件的内容都是模版,一些差异化的可根据实际情况来进行替换。比如12306购票成功后,会发送一封购票信息邮件;在京东下单成功后,也会收到一封包含订单的邮件。所以,今天就来讲讲如何利用
Spring
提供的JavaMailSender
接口,实现邮件发送功能。
一点知识
讲解前,我们先来简单了解下相关邮件知识。
JavaMail介绍
JavaMail
是由Sun
定义的一套收发电子邮件的API
,不同的厂商可以提供自己的实现类。但它并没有包含在JDK中,而是作为JavaEE
的一部分。而JavaMailSender
底层也是基于JavaMail
jar包的,官网介绍时就说了依赖关系:
邮件通信协议
- SMTP:简单邮件传输协议,用于发送电子邮件的传输协议;
- POP3:用于接收电子邮件的标准协议;
- IMAP:互联网消息协议,是POP3的替代协议。
这三种协议都有对应SSL加密传输的协议,分别是SMTPS
,POP3S
和IMAPS
。除JavaMail
服务提供程序之外,JavaMail
还需要JAF(JavaBeans Activation Framework)
来处理不是纯文本的邮件内容,这包括MIME(多用途互联网邮件扩展)、URL页面和文件附件等内容。
JavaMail关键对象
- Properties:属性对象。针对不同的的邮件协议,JavaMail规定了服务提供者必须支持一系列属性。
- Session会话对象
这个不要混淆了,和web
中的session
不一样的,简单来说,它就是配置的集合。
Session的主要作用包括两个方面:
- 接收各种配置属性信息:通过Properties对象设置的属性信息;
- 初始化JavaMail环境:根据JavaMail的配置文件,初始化JavaMail环境,以便通过Session对象创建其他重要类的实例。
Transport和Store:传输和存储
邮件操作只有发送或接收两种处理方式,JavaMail
将这两种不同操作描述为传输(javax.mail.Transport)
和存储(javax.mail.Store)
,传输对应邮件的发送,而存储对应邮件的接收。Message:消息对象
一旦获得Session
对象,就可以继续创建要发送的消息。Message
是个抽象类,常用的实现类为:javax.mail.internet.MimeMessage
(下文中使用它来实现附件发送)。Address:地址
创建了Session
和Message
,并将内容填入消息后,就可以用Address
确定信件地址了。Address
也是个抽象类。对应常用实现类:javax.mail.internet.InternetAddress
。
其他相关信息,可点击查看:https://blog.csdn.net/a2241076850/article/details/52856863讲解的比较清楚。这里就不阐述了(本人也是不甚了解,只是写这篇文章时,有搜索了些相关资料)
Spring
封装后,使用起来基本上都不需要去关心这些对象值了,简单了解下即可。当然了,有兴趣,可以搜索下相关资料。也可以去官网查看:https://java.net/projects/javamail/pages/Home
SpringBoot集成
SpringBoot
中,集成邮件发送功能,是很简单的。只需引入spring-boot-starter-mail
即可,以下就简单的介绍下如何发送不同格式的邮件,已满足不同的业务需求。
前提条件
0.加入pom
依赖
<!-- 加入mail pom -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
1.配置文件加入相关配置信息,如发送方邮箱信息等。
# 邮件相关
# SMTP服务器地址
spring.mail.host=smtp.qq.com
# SMTP服务器端口号
# spring.mail.port=25
# 发送方帐号
spring.mail.username=邮箱
# 发送方密码(授权码)
spring.mail.password=邮箱密码
#javaMailProperties 配置
# 开启用户身份验证
spring.mail.properties.mail.smtp.auth=true
# STARTTLS:一种通信协议,具体可以搜索下
#spring.mail.properties.mail.smtp.starttls.enable=true
#spring.mail.properties.mail.smtp.starttls.required=true
这里需要注意:使用QQ邮箱
发送时,需要开通POP3/SMTP
服务,邮箱密码是填写授权码
的,而不是邮箱密码,这个需要注意下。具体网站说明:在设置
-->账户
-->POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务
有说明,大家可自行获取授权码。
关于JavaMailSender
使用文档,可以查看官网指南:https://docs.spring.io/spring/docs/4.3.18.RELEASE/spring-framework-reference/htmlsingle/#mail。
3.引入JavaMailSender
接口对象,已经自动注入了,只需引入即可。
@Autowired
private JavaMailSender mailSender;
具体可查看spring-boot-autoconfigure
jar包下的org.springframework.boot.autoconfigure.mail
类,里面已经引入了JavaMailSenderImpl
实现类了。
纯文本格式
纯文本格式,比较简单,使用
SimpleMailMessage
即可。
/**
* 纯文本格式
* @return
*/
@GetMapping("/simple")
public String simpleSend() {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("499452441@qq.com");
message.setTo("499452441@qq.com");
message.setSubject("主题:来自oKong邮件");
message.setText("公众号:一枚趔趄的猿(lqdevOps),作者:oKong");
mailSender.send(message);
return "发送成功!";
}
启动后,发送,一切正常情况下,可以看见邮件发送成功了。
附件格式
上文有提到,创建附件时,可使用
MimeMessage
消息对象。使用也很简单,如下:
@GetMapping("/attach")
public String attachSend() throws MessagingException {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setFrom("499452441@qq.com");
helper.setTo("499452441@qq.com");
helper.setSubject("主题:来自oKong邮件(带附件)");
helper.setText("(含附件)公众号:一枚趔趄的猿(lqdevOps),作者:oKong");
//添加附件
File qrCode = new File("wxgzh8cm.jpg");
//建议文件带上后缀,可支持在线预览
helper.addAttachment("公众号二维码.jpg", qrCode);
mailSender.send(mimeMessage);
return "附件邮件发送成功!";
}
说明:主要就是利用addAttachment
方法进行附件添加,可添加多个。附件名称建议带上后缀。
运行后,即可看见邮件已发送成功:
HTML内容格式
以上的格式,基本都是纯文本格式。我们知道,在发送邮件时,一般上可以使用
html
格式进行发送,可嵌入静态资源,如图片等等。而JavaMailSender
中使用也很简单,主要就是利用MimeMessageHelper
类的setText(String text, boolean html)
方法。
/**
* html格式
* @return
* @throws MessagingException
*/
@GetMapping("/html")
public String htmlSend() throws MessagingException {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setFrom("499452441@qq.com");
helper.setTo("499452441@qq.com");
helper.setSubject("主题:来自oKong邮件(带附件)");
helper.setText("<html><body><div>(含附件)公众号:一枚趔趄的猿(lqdevOps),作者:oKong</div><div><img src='cid:winxinQr'></div></body></html>",true);
//抄送人
// helper.setCc("");
//密送人
// helper.setBcc("");
//添加附件
File qrCode = new File("wxgzh8cm.jpg");
//建议文件带上后缀,可支持在线预览
helper.addAttachment("公众号二维码.jpg", qrCode);
helper.addInline("winxinQr", qrCode);
mailSender.send(mimeMessage);
return "附件邮件发送成功!";
}
注意:这里的cid(Content-ID)
是固定写法,冒号后面的值即为需要替换资源的contentId
值,就是对应addInline
的资源id。
而且注意官网的一句话:Be sure to first add the text and after that the resources. If you are doing it the other way around, it won’t work! 顺序不能颠倒了,需要先设置text
,之后添加资源信息!
启动后,一切正常即可看见邮件内容如下:
模版邮件
业务开发时,一般上发送的邮件格式都是固定的,而是替换部分参数即可,如用户信息、订单信息等。所以,我们可以利用之前讲解过的
模版引擎
进行管理各类模版邮件,同时在发送时替换对应的参数值。以下以freemarker
模版引擎为例子。
关于模版引擎的使用,可点击查看文章:第十六章:web应用开发
0.引入pom依赖
<!-- 加入模版引擎 freemarker -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
1.加入配置信息,指定模版目录、后缀名等:
# 缓存配置 开发阶段应该配置为false 因为经常会改
spring.freemarker.cache=false
# 模版后缀名 默认为ftl
spring.freemarker.suffix=.ftl
# 文件编码
spring.freemarker.charset=UTF-8
# 模版加载的目录
spring.freemarker.template-loader-path=classpath:/templates/
2.编写模版文件(放在resources\templates
目录下),主要就是替换userName
变量:
mail.ftl
<html>
<body>
<div>
尊敬的${userName}:
</div>
<div >
您好!
</div>
<div >
多谢关注公众号:一枚趔趄的猿(ledevOps),分享程序猿日常,不定期发布关于SpringBoot、SpringCloud、Java及其他相关教程,记录工作中碰到的问题。欢迎关注~
</div>
<div >
一起学习,共同进步!
</div>
<div >
<img src="cid:winxinQr">
</div>
</body>
</html>
3.编写具体实现类:
//自动注入
@Autowired
freemarker.template.Configuration freemarkerConfig;
@GetMapping("/template")
public String template(String userName) throws Exception {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setFrom("499452441@qq.com");
helper.setTo("499452441@qq.com");
helper.setSubject("主题:" + userName + ",你有一封来自oKong邮件(From模版)");
//设置替换的参数对象
Map<String, Object> model = new HashMap<String, Object>();
model.put("userName", StringUtils.isEmpty(userName) ? "oKong" : userName);
String templateString = FreeMarkerTemplateUtils.processTemplateIntoString(freemarkerConfig.getTemplate("mail.ftl"), model);
helper.setText(templateString,true);
//抄送人
// helper.setCc("");
//密送人
// helper.setBcc("");
//添加附件
File qrCode = new File("wxgzh8cm.jpg");
//建议文件带上后缀,可支持在线预览
helper.addAttachment("公众号二维码.jpg", qrCode);
helper.addInline("winxinQr", qrCode);
mailSender.send(mimeMessage);
return "模版文件发送成功!";
}
这里说明下:
使用FreeMarker
模版时,可直接使用Spring
提供的工具类FreeMarkerTemplateUtils
的processTemplateIntoString
方法进行模版文件的替换。而freemarker.template.Template
类,熟悉FreeMarker
的同学应该知道,可以直接从配置类freemarker.template.Configuration
中获取,此类直接注入即可,SpringBoot
在启动时,已经注入这个bean
了。具体的自动配置可查看:org.springframework.boot.autoconfigure.freemarker
类。
关于一些Freemarker
的语法这里就不说明了,大家可到官网查看下:https://freemarker.apache.org/docs/index.html或者,中文参考(可能版本不是最新):http://freemarker.foofun.cn/toc.html
启动后,一切正常情况下可以看见如下:邮件内容已被替换了:
参考资料
- https://docs.spring.io/spring/docs/4.3.18.RELEASE/spring-framework-reference/htmlsingle/#mail
- https://docs.spring.io/spring-boot/docs/1.5.15.RELEASE/reference/htmlsingle/#boot-features-email
- https://blog.csdn.net/a2241076850/article/details/52856863
总结
本注解主要简单的讲解了如何利用
JavaMailSender
发送各类格式的邮件。这里只是演示了以JavaMailSender
发送邮件时,使用是很简单的,只需要调用相应的api方法即可。想了解底层如何实现的同学,建议搜索下相关JavaMail
的知识,加深印象。
最后
目前互联网上很多大佬都有
SpringBoot
系列教程,如有雷同,请多多包涵了。原创不易,码字不易,还希望大家多多支持。若文中有所错误之处,还望提出,谢谢。
老生常谈
- 个人QQ:
499452441
- 微信公众号:
lqdevOps
个人博客:http://blog.lqdev.cn
完整示例:https://github.com/xie19900123/spring-boot-learning/tree/master/chapter-26
原文地址:http://blog.lqdev.cn/2018/08/29/springboot/chapter-twenty-six
SpringBoot | 第二十六章:邮件发送的更多相关文章
- Gradle 1.12用户指南翻译——第二十六章. War 插件
其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...
- “全栈2019”Java多线程第二十六章:同步方法生产者与消费者线程
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java第二十六章:流程控制语句中循环语句do-while
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- UNP学习笔记(第二十六章 线程)
线程有时称为轻权进程(lightweight process) 同一进程内的所有线程共享相同的全局内存.这使得线程之间易于共享信息,然后这样也会带来同步的问题 同一进程内的所有线程处理共享全局变量外还 ...
- SpringBoot | 第二十五章:日志管理之自定义Appender
前言 前面两章节我们介绍了一些日志框架的常见配置及使用实践.一般上,在开发过程中,像log4j2.logback日志框架都提供了很多Appender,基本上可以满足大部分的业务需求了.但在一些特殊需求 ...
- 第二十六章 hystrix-dashboard + turbine
一.使用turbine的意义 引入多个hystrix stream: 1.使用hystrix-dashboard的可以添加多个stream的功能 图中添加的两个stream会在真正monitor的时候 ...
- 【第二十六章】 hystrix-dashboard + turbine
一.使用turbine的意义 引入多个hystrix stream: 1.使用hystrix-dashboard的可以添加多个stream的功能 图中添加的两个stream会在真正monitor的时候 ...
- SpringBoot | 第二十九章:Dubbo的集成和使用
前言 今年年初时,阿里巴巴开源的高性能服务框架dubbo又开始了新一轮的更新,还加入了Apache孵化器.原先项目使用了spring cloud之后,已经比较少用dubbo.目前又抽调回原来的行业应用 ...
- SpringBoot | 第二十二章:定时任务的使用
前言 上两章节,我们简单的讲解了关于异步调用和异步请求相关知识点.这一章节,我们来讲讲开发过程也是经常会碰见的定时任务.比如每天定时清理无效数据.定时发送短信.定时发送邮件.支付系统中的定时对账等等, ...
随机推荐
- Linux编程里getopt_long_only函数用法详解
在程序中难免需要使用命令行选项,可以选择自己解析命令行选项,但是有现成的,何必再造轮子.下面介绍使用getopt_long_only和getopt_long(两者用法差不多)解析命令行选项. 程序中主 ...
- ng2 中使用echart
1.首先创建echarts指令 //echart.directive.ts important { Directive,ElementRef,Input,Ouput,Onchanges,OnInit, ...
- 前端之css样式(选择器)
一.css概述 CSS是Cascading Style Sheets的简称,中文称为层叠样式表,对html标签的渲染和布局 CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明. 例如 二.c ...
- j++与++j
(j++)+(++j)+(++j) 核心部分汇编代码 执行顺序j原始值为5,存放在栈的ebp-4中;q无初始值,存放在栈的ebp-8中先执行一次j自增赋值++j ;j=6执行第一个加法运算;值存放在C ...
- ParentViewController中添加SubViewController(IOS学习)
我是用的是Container.addSubView的方法. 1. ParentViewController.m的@interface()中添加2个子vc的实例变量,代码如下 @property (no ...
- ResultSetMetaData和ResultSet
我现在有一张表t_product;我们查询所有的商品:SELECT * FROM t_product; 上述所有的数据都可以封装成一个对象,我们称这个查询出来的对象为结果集对象:ResultSet. ...
- R语言中的字符处理
R语言中的字符处理 (2011-07-10 22:29:48) 转载▼ 标签: r语言 字符处理 字符串 连接 分割 分类: R R的字符串处理能力还是很强大的,具体有base包的几个函数和strin ...
- 12、IGV-Integrative Genomics Viewer
1.IGV的网址:http://software.broadinstitute.org/software/igv/(java环境) 常见的几种输入格式bam/sam(比对文件) TDF(bam的精简 ...
- sklearn正规化(Normalization或者scale)
from sklearn import preprocessing import numpy as np a = np.array([[10,2.7,3.6],[-100,5,-2],[120,20, ...
- JavaScript中的真和假,==和===, 不等
咋JS中,下面这些值表示 “假”: "" (empty string) 0,-0,NaN (invalid number) null, undefined false 除了上面这些 ...