Java整合Spring发送邮件

又懒了,要多看书和总结,然后早点写博客😝

有时候代码执行完之后,需要邮件进行通知,所以通过工作中的项目和网上的资料,特地去学习了如何使用Java发送邮件。Demo使用的是QQ邮箱进行邮件发送,可以先了解一下基础文档协议RFC882、MIME、SMTP等


文档协议说明

看了孤傲苍狼的文章,关于邮件协议,他解释的很好,这边进行记录了一下

RFC882文档(发送简单文字邮件)

RFC882文档规定了如何编写一封简单的邮件(纯文本邮件),一封简单的邮件包含邮件头和邮件体两个部分,邮件头和邮件体之间使用空行分隔。

邮件头包含的内容有:

  • from字段   –用于指明发件人
  • to字段    –用于指明收件人
  • subject字段 –用于说明邮件主题
  • cc字段    – 抄送,将邮件发送给收件人的同时抄送给另一个收件人,收件人可以看到邮件抄送给了谁
  • bcc字段    – 密送,将邮件发送给收件人的同时将邮件秘密发送给另一个收件人,收件人无法看到邮件密送给了谁

MIME文档(可以发送HTML邮件)

在我们的实际开发当中,一封邮件既可能包含图片,又可能包含有附件,在这样的情况下,RFC882文档规定的邮件格式就无法满足要求了。

MIME协议是对RFC822文档的升级和补充,它描述了如何生产一封复杂的邮件。通常我们把MIME协议描述的邮件称之为MIME邮件。MIME协议描述的数据称之为MIME消息。
  
对于一封复杂邮件,如果包含了多个不同的数据,MIME协议规定了要使用分隔线对多段数据进行分隔,并使用Content-Type头字段对数据的类型、以及多个数据之间的关系进行描述。   

SMTP协议

要使用QQ邮箱发送邮件,就要遵守SMTP协议。SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。

需要在QQ邮箱中打开SMTP服务和生成授权码

QQ邮箱


Demo的时序图


代码实现

demo使用的是Velocity模板引擎进行html渲染,其它渲染方式类似,最后都是生成context内容。

基础依赖

java mail/spring-context-support/velocity

1
2
3
compile group: 'com.sun.mail', name: 'javax.mail', version: '1.6.1'
compile group: 'org.springframework', name: 'spring-context-support', version: '5.0.8.RELEASE'
compile group: 'org.apache.velocity', name: 'velocity', version: '1.7'

邮件基础Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MailBean {
/** 邮件发送人地址,非空 */
private String from;
/** 邮件发送人姓名,非空 */
private String fromName;
/** 邮件接收人地址列表,非空 */
private String[] toList;
/** 邮件抄送人地址列表 */
private String[] ccList;
/** 邮件主题,非空 */
private String subject;
/** 邮件内容velocity上下文参数 */
private Map<String, Object> mailContext;
/** 邮件内容velocity模板,非空 */
private String mailVmName;
···
}

模板引擎渲染工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class VelocityUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(VelocityUtils.class);
private static final VelocityEngine VE = new VelocityEngine();
static {
initVelocity();
}
private static void initVelocity() {
try {
VE.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
VE.setProperty(
"classpath.resource.loader.class",
ClasspathResourceLoader.class.getName());
VE.setProperty(RuntimeConstants.RUNTIME_LOG, "logs/velocity.log");
VE.setProperty(
RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
"org.apache.velocity.runtime.log.NullLogChute");
VE.init();
} catch (Exception e) {
LOGGER.warn("初始化velocity模板引擎失败", e);
}
}
/** 实际渲染的代码,map里面的字段与编写的模板相对应 **/
public static String render(String vmName, Map<String, Object> context) {
String renderStr = "";
try {
Template template = VE.getTemplate(vmName, "UTF-8");
VelocityContext velocityContext = new VelocityContext();
for (Map.Entry<String, Object> entry : context.entrySet()) {
velocityContext.put(entry.getKey(), entry.getValue());
}
StringWriter sw = new StringWriter();
template.merge(velocityContext, sw);
renderStr = sw.toString();
} catch (Exception e) {
LOGGER.warn("渲染vm模板失败", e);
}
return renderStr;
}
}

发送邮件的服务

我们使用spring进行依赖注入,帮我们托管mailSender

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<bean id="javaMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<!-- <property name="host" value="smtp.qq.com"/> -->
<property name="host" value="smtp.qq.com"/>
<property name="port" value="587"/>
<!-- <property name="username" value="xxxxx@qq.com"/> -->
<property name="username" value="xxxxxx@qq.com"/>
<!-- qq邮箱的授权码,如果是企业邮箱,则使用登录密码 -->
<property name="password" value="xxxxxxxxxxx"/>
<property name="javaMailProperties">
<props >
<prop key="mail.smtp.auth">true</prop>
</props>
</property>
</bean>

面向接口编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public interface MailService {
/**
* 发送邮件
*
* @param mailBean 邮件bean
*/
void sendMail(MailBean mailBean);
}
@Service
public class MailServiceImpl implements MailService {
@Autowired
@Qualifier("javaMailSender")
private JavaMailSender mailSender;
@Override
public void sendMail(MailBean mailBean) {
// 省略了校验
try {
// 工具类进行内容渲染,传入的是模板名称和具体内容MAP
String mailContent = VelocityUtils.render(
mailBean.getMailVmName(),
mailBean.getMailContext());
MimeMessage msg = mailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(msg, true, "UTF-8");
mimeMessageHelper.setFrom(mailBean.getFrom(), mailBean.getFromName());
mimeMessageHelper.setSubject(mailBean.getSubject());
mimeMessageHelper.setTo(mailBean.getToList());
mimeMessageHelper.setText(mailContent, true);
mailSender.send(msg);
} catch (Exception e) {
log.warn("邮件发送失败,mailBean={}", mailBean, e);
}
}
}

demo.vm(模板文件)

1
2
3
4
5
6
7
<html xmlns="http://www.w3.org/1999/xhtml ">
<body class="word-style">
<div class="title" align="center">DEMO</div>
<br/>
<div class="indent">$!{msg}</div>
</body>
</html>

发送邮件

最后通过调用MailService进行邮件发送

1
2
3
4
5
6
7
8
9
10
11
12
13
MailBean mailBean = new MailBean();
mailBean.setFrom("xxxxxx@qq.com");
mailBean.setFromName("DEMO");
mailBean.setSubject("Are you ok");
//模板
mailBean.setMailVmName("demo.vm");
//收件人地址
String[] toList = {"xxxxx@xxxx.com"};
mailBean.setToList(toList);
Map<String, Object> context = new HashMap<>();
context.put("msg", "I'm fine, thank you~");
mailBean.setMailContext(context);
mailService.sendMail(mailBean);

当然,你可以制作更加好看的html邮件~


参考资料

  1. JavaWeb学习总结(五十二)——使用JavaMail创建邮件和发送邮件
  2. Java 发送邮件
  3. 通过spring实现javamail发送邮件功能
  4. 腾讯的IMAP服务说明