![v2-3fa7f80a571a4b922c0f3c6a5f37b8f8_1440w.jpg?source=172ae18b](http://img-03.proxy.5ce.com/view/image?&type=2&guid=3aa6bccb-3a2f-eb11-8da9-e4434bdf6706&url=https://pic2.zhimg.com/v2-3fa7f80a571a4b922c0f3c6a5f37b8f8_1440w.jpg?source=172ae18b)
写在开始
系统开发完成进入线上运维阶段后,系统的运维人员需要及时的了解系统的运行状态,除了主动的去做线上数据监控外,如果可以通过异步的方式通知运维人员系统存在异常并且需要进行排查时,邮件通知不失为一种比较理想的方式。方便、快捷、及时。
在本文章中,将基于Spring Boot为技术基础,构建一个邮件发送的演示项目,让后结合着该项目详细讲解邮件发送的相关知识与实现底层原理。
演示项目功能简介
通过浏览器访问给定的url,调用控制层的sendEmail方法,然后控制层调用服务层的EmailService,进行邮件的发送。功能相对简单能起到演示作用即可。
POM文件配置
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
针对该项目中添加的依赖简单做一个解释,其中spring-boot-starter-web是spring-boot项目中MVC相关的模块依赖,spring-boot-starter-mail为邮件发送相关的依赖,lombok为java语法糖,spring-boot-starter-test主要是用来后续接口开发完毕后进行集成测试使用。
spring-boot-starter-mail
介绍完项目所需的依赖后,我们再回头看看spring-boot-starter-mail为我们具体都引入了那些依赖。还是以spring-boot-starter-mail的pom文件为基础来看看该模块为我们的项目都自动添加了那些jar包。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
</dependency>
</dependencies>
根据spring-boot源码来看,本质上spring-boot-starter-mail本质上用的还是javaee的javax.mail开发包。所以在项目开发中如果不使用spring-boot-starter-mail模块仅使用javax.mail也是可以完成邮件发送功能,不过为了避免遇到spring-boot与版本javax.mail版本不兼容的问题。建议还是直接使用spring-boot为我们提供的mail starter。使用mail starter模块还有一个好处是spring-boot提供的邮件的auto configure功能。
Spring Boot Auto Configure Mail
上一节提到的spring boot提供的auto configure在邮件开发时的好处是用户在使用时只需要进行简单的属性文件配置,便可以进行功能开发了。那么针对邮件模块,Spring Boot都提供了那些配置呢?下面我们来进行重点介绍。
MailProperties
在spring-boot-autoconfiure的mail项目里面,定义了所有的属性文件的配置信息。其中
@ConfigurationProperties(prefix = "spring.mail")
注解中规定了邮件相关属性配置的前缀为spring.mail
除了自动配置所需要的前缀信息外,还定义了一下属性。具体每个属性在实际的使用中都标识什么意思,大家可以直接阅读源代码里面的注释。还是容易理解的。
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
/**
* SMTP server host.
*/
private String host;
/**
* SMTP server port.
*/
private Integer port;
/**
* Login user of the SMTP server.
*/
private String username;
/**
* Login password of the SMTP server.
*/
private String password;
/**
* Protocol used by the SMTP server.
*/
private String protocol = "smtp";
/**
* Default MimeMessage encoding.
*/
private Charset defaultEncoding = DEFAULT_CHARSET;
/**
* Additional JavaMail Session properties.
*/
private Map<String, String> properties = new HashMap<String, String>();
/**
* Session JNDI name. When set, takes precedence to others Session settings.
*/
private String jndiName;
/**
* Test that the mail server is available on startup.
*/
private boolean testConnection;
Application Properties
针对演示项目,具体配置了如下的属性。
spring.mail.protocol=smtp
spring.mail.host=smtp.126.com
spring.mail.port=25
spring.mail.username=your_email_username
spring.mail.password=your_email_pwd
spring.mail.default-encoding=utf-8
通过上面的配置信息,可以看出来,该邮件的发送是通过126的邮箱进行发送的。在实际的项目开发中,可以替换为本公司的邮件服务地址和对应的端口号。
项目的具体代码实现
邮件发送的接口服务定义
package
邮件发送功能具体实现类
package com.mc.mail.boot.mail.service.impl;
import com.mc.mail.boot.mail.service.EmailService;
import lombok.SneakyThrows;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.util.Date;
/**
* @author M.C
* @description EmailServiceImpl
* @date 2019-02-20 9:01
**/
@Service
public class EmailServiceImpl implements EmailService {
@Resource
private JavaMailSender javaMailSender;
@Override
@SneakyThrows
public void sendMail(String receivers, String subject, String htmlMsg){
MimeMessage mail = javaMailSender.createMimeMessage();
mail.addRecipients(Message.RecipientType.TO, InternetAddress.parse(receivers));
//todo replace it with real sender
mail.setFrom(new InternetAddress("******"));
mail.setSentDate(new Date());
mail.setSubject(subject);
MimeBodyPart mimeBodyPart = new MimeBodyPart();
mimeBodyPart.setContent(htmlMsg, "text/html;charset=utf-8");
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(mimeBodyPart);
mail.setContent(multipart);
javaMailSender.send(mail);
}
}
接口的测试
接口开发完成后,我们使用Spring Boot为提供的spring-boot-starter-test模块来进行接口的集成测试。针对spring-boot-starter-test以及spring-test这两个模块的具体使用以及原理介绍,后续的文章会做详细的介绍。
package com.mc.mail.boot.mail.service;
import com.mc.mail.boot.mail.BootMailApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* @author M.C
* @description EmailServiceTest
* @date 2019-02-20 20:29
**/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BootMailApplication.class)
public class EmailServiceTest {
@Resource
EmailService emailService;
@Test
public void testSendEmail(){
String receiver = "yang_ustber@126.com";
String content = "<html><body><h1>Hello World!</h1></body></html>";
try {
emailService.sendMail(receiver,"测试邮件",content);
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试结果如下
![v2-8492c662c2028a84e2bca52ecf570f48_b.jpg](http://img-02.proxy.5ce.com/view/image?&type=2&guid=3aa6bccb-3a2f-eb11-8da9-e4434bdf6706&url=https://pic1.zhimg.com/v2-8492c662c2028a84e2bca52ecf570f48_b.jpg)
![v2-9d316f7a56e7db2572dc96bdcccc524c_b.jpg](http://img-02.proxy.5ce.com/view/image?&type=2&guid=3aa6bccb-3a2f-eb11-8da9-e4434bdf6706&url=https://pic1.zhimg.com/v2-9d316f7a56e7db2572dc96bdcccc524c_b.jpg)
写在最后的前
到此,一个简单的邮件发送接口就算开发与冒烟测试完成了。在测试中我仅设计了一个happy-face的测试用例。读者如果感兴趣的话,可以在进行一些测试场景的设计,来发现接口实现的一些不足。
针对上面的代码,再做一些扩展的介绍。处于好奇心。就在想,除了使用MimeMessage外,是否还有其它格式的邮件消息可以使用呢?还有就是MimeMessage与其他消息格式有什么应用上的区别?在这里也一并做一个说明。也算是自己知识的一点扩展吧。
MimeMessage类继承自Message抽象类。同时扩展了Message抽象类的还有IMAPMessage,IMAPNestedMessage,POP3Message,SMTPMessage,SmartMimeMessage
MimeMessage
MimeMessage代表的是MIME(多用途互联网邮件扩展,Multipurpose Internet Mail Extensions)格式的邮件消息,在本次演示的Demo中使用的就是MIME。
SMTPMessage
SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议。SMTP 认证,简单地说就是要求必须在提供了账户名和密码之后才可以登录 SMTP 服务器,这就使得那些垃圾邮件的散播者无可乘之机。在Spring Boot Starter Mail的自动配置属性文件类中,默认使用的是该协议。
POP3Message
POP3(Post Office Protocol 3)即邮局协议的第3个版本,具体的介绍可以参考维基百科。
IMAPMessage
IMAP(Internet Mail Access Protocol)即交互式邮件存取协议,它是跟POP3类似邮件访问标准协议之一。不同的是,开启了IMAP后,您在电子邮件客户端收取的邮件仍然保留在服务器上,同时在客户端上的操作都会反馈到服务器上。
写在最后的中
如果使用的是Spring Boot Starter Mail的默认SMTP协议,在邮件发送过程中可能会遇到邮件发送失败的问题。其中一个异常为SMTP can only send RFC822 messages。因为但是验证的消息体为非MimeMessage。最终排查到的原因为如下代码抛出的异常。
![v2-d748fec714662ddc5754409536770dce_b.jpg](http://img-01.proxy.5ce.com/view/image?&type=2&guid=3aa6bccb-3a2f-eb11-8da9-e4434bdf6706&url=https://pic3.zhimg.com/v2-d748fec714662ddc5754409536770dce_b.jpg)
写在最后的后
要想发出去的邮件比较吸引人注意的话,除了有丰富的数据支撑外,还要必须熟悉HTML的一些标签。