|
| 1 | +--- |
| 2 | +title: Springboot 系列(十三)使用邮件服务 |
| 3 | +toc_number: true |
| 4 | +date: 2019-03-12 00:20:22 |
| 5 | +url: springboot/springboot-13-email |
| 6 | +tags: |
| 7 | + - Springboot |
| 8 | + - 邮件 |
| 9 | + - E-mail |
| 10 | +categories: |
| 11 | + - Springboot |
| 12 | +--- |
| 13 | + |
| 14 | +我们这个时代,邮件服务不管是对于工作上的交流,还是平时的各种邮件通知,都是一个十分重要的存在。Java 从很早时候就可以通过 Java mail 支持邮件服务。Spring 更是对 Java mail 进行了进一步的封装,抽象出了 `JavaMailSender`. 后来随着 Springboot 的出现,理所当然的出现了 `spring-boot-starter-mail`. 不管怎么说,每次的封装都让使用变得越来越简单。 |
| 15 | +<!-- more --> |
| 16 | +## Springboot mail 依赖 |
| 17 | + |
| 18 | +创建 Springboot 项目不提,先看一下总体目录结构。 |
| 19 | + |
| 20 | + |
| 21 | +直接引入 Springboot 邮件服务所需的依赖。 |
| 22 | +```xml |
| 23 | + <dependencies> |
| 24 | + <dependency> |
| 25 | + <groupId>org.springframework.boot</groupId> |
| 26 | + <artifactId>spring-boot-starter-web</artifactId> |
| 27 | + </dependency> |
| 28 | + <!-- 邮件服务 --> |
| 29 | + <dependency> |
| 30 | + <groupId>org.springframework.boot</groupId> |
| 31 | + <artifactId>spring-boot-starter-mail</artifactId> |
| 32 | + </dependency> |
| 33 | + <!-- Thymeleaf 模版,用于发送模版邮件 --> |
| 34 | + <dependency> |
| 35 | + <groupId>org.springframework.boot</groupId> |
| 36 | + <artifactId>spring-boot-starter-thymeleaf</artifactId> |
| 37 | + </dependency> |
| 38 | + |
| 39 | + <dependency> |
| 40 | + <groupId>org.projectlombok</groupId> |
| 41 | + <artifactId>lombok</artifactId> |
| 42 | + <optional>true</optional> |
| 43 | + </dependency> |
| 44 | + |
| 45 | + <dependency> |
| 46 | + <groupId>org.springframework.boot</groupId> |
| 47 | + <artifactId>spring-boot-starter-test</artifactId> |
| 48 | + <scope>test</scope> |
| 49 | + </dependency> |
| 50 | + </dependencies> |
| 51 | +``` |
| 52 | + |
| 53 | +## Springboot mail 配置 |
| 54 | +使用邮件服务需要配置自己可以使用的邮箱信息,一般需要配置发送协议 SMTP、邮箱帐号(本次以126邮箱为例)、邮箱密码以及编码格式。 |
| 55 | +```properties |
| 56 | +spring.mail.host=smtp.126.com |
| 57 | +spring.mail.port=25 |
| 58 | +# 你的邮箱地址 |
| 59 | +spring.mail.username[email protected] |
| 60 | +# 你的授权码(126 和 163 以及 qq 邮箱 都需要授权码登录,没有授权码的直接登录网页版邮箱设置里设置) |
| 61 | +spring.mail.password=password |
| 62 | +spring.mail.default-encoding=UTF-8 |
| 63 | +``` |
| 64 | +## Springboot mail 文本邮件 |
| 65 | +文本邮件是最简单也是最基础的一种邮件,使用 Spring 封装的 `JavaMailSender` 直接发送就可以了。 |
| 66 | + |
| 67 | +创建 `MailService` 类,注入 `JavaMailSender` 用于发送邮件,使用 `@Value("${spring.mail.username}")` 绑定配置文件中的参数用于设置邮件发送的来邮箱。使用 `@Service` 注解把 `MailService` 注入到 Spring 容器,使用 `Lombok` 的 `@Slf4j` 引入日志。 |
| 68 | +```java |
| 69 | +/** |
| 70 | + * <p> |
| 71 | + * 邮件服务 |
| 72 | + * |
| 73 | + * @Author niujinpeng |
| 74 | + * @Date 2019/3/10 21:45 |
| 75 | + */ |
| 76 | +@Service |
| 77 | +@Slf4j |
| 78 | +public class MailService { |
| 79 | + |
| 80 | + @Value("${spring.mail.username}") |
| 81 | + private String from; |
| 82 | + |
| 83 | + @Autowired |
| 84 | + private JavaMailSender mailSender; |
| 85 | + |
| 86 | + /** |
| 87 | + * 发送简单文本邮件 |
| 88 | + * |
| 89 | + * @param to |
| 90 | + * @param subject |
| 91 | + * @param content |
| 92 | + */ |
| 93 | + public void sendSimpleTextMail(String to, String subject, String content) { |
| 94 | + SimpleMailMessage message = new SimpleMailMessage(); |
| 95 | + message.setTo(to); |
| 96 | + message.setSubject(subject); |
| 97 | + message.setText(content); |
| 98 | + message.setFrom(from); |
| 99 | + mailSender.send(message); |
| 100 | + log.info("【文本邮件】成功发送!to={}", to); |
| 101 | + } |
| 102 | +} |
| 103 | +``` |
| 104 | +创建 Springboot 的单元测试类测试文本邮件,实验中的收信人为了方便,都设置成了自己的邮箱。 |
| 105 | +```java |
| 106 | +@RunWith(SpringRunner.class) |
| 107 | +@SpringBootTest |
| 108 | +public class MailServiceTest { |
| 109 | + |
| 110 | + @Autowired |
| 111 | + private MailService mailService; |
| 112 | + @Autowired |
| 113 | + private TemplateEngine templateEngine; |
| 114 | + |
| 115 | + @Test |
| 116 | + public void sendSimpleTextMailTest() { |
| 117 | + |
| 118 | + String subject = "Springboot 发送简单文本邮件"; |
| 119 | + String content = "<p>第一封 Springboot 简单文本邮件</p>"; |
| 120 | + mailService.sendSimpleTextMail(to, subject, content); |
| 121 | + } |
| 122 | +} |
| 123 | +``` |
| 124 | +运行单元测试,测试文本邮件的发送。 |
| 125 | + |
| 126 | +PS:如果运行报出异常 `AuthenticationFailedException: 535 Error`. 一般都是用户名和密码有误。 |
| 127 | +```log |
| 128 | +Caused by: javax.mail.AuthenticationFailedException: 535 Error: authentication failed |
| 129 | +
|
| 130 | + at com.sun.mail.smtp.SMTPTransport$Authenticator.authenticate(SMTPTransport.java:965) |
| 131 | + at com.sun.mail.smtp.SMTPTransport.authenticate(SMTPTransport.java:876) |
| 132 | + at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:780) |
| 133 | + at javax.mail.Service.connect(Service.java:366) |
| 134 | + at org.springframework.mail.javamail.JavaMailSenderImpl.connectTransport(JavaMailSenderImpl.java:517) |
| 135 | + at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:436) |
| 136 | + ... 34 more |
| 137 | +``` |
| 138 | +正常运行输出成功发送的日志。 |
| 139 | +```log |
| 140 | +2019-03-11 23:35:14.743 INFO 13608 --- [ main] n.codingme.boot.service.MailServiceTest : Started MailServiceTest in 3.964 seconds (JVM running for 5.749) |
| 141 | +2019-03-11 23:35:24.718 INFO 13608 --- [ main] net.codingme.boot.service.MailService : 【文本邮件】成功发送![email protected] |
| 142 | +``` |
| 143 | +查看邮箱中的收信。 |
| 144 | + |
| 145 | + |
| 146 | + |
| 147 | +文本邮件正常收到,同时可见文本邮件中的 HTML 标签也不会被解析。 |
| 148 | + |
| 149 | +## Springboot mail HTML 邮件 |
| 150 | +在上面的 `MailService` 类里新加一个方法 `sendHtmlMail`,用于测试 HTML 邮件。 |
| 151 | +```java |
| 152 | + /** |
| 153 | + * 发送 HTML 邮件 |
| 154 | + * |
| 155 | + * @param to |
| 156 | + * @param subject |
| 157 | + * @param content |
| 158 | + * @throws MessagingException |
| 159 | + */ |
| 160 | + public void sendHtmlMail(String to, String subject, String content) throws MessagingException { |
| 161 | + MimeMessage message = mailSender.createMimeMessage(); |
| 162 | + MimeMessageHelper messageHelper = new MimeMessageHelper(message, true); |
| 163 | + messageHelper.setFrom(from); |
| 164 | + messageHelper.setTo(to); |
| 165 | + messageHelper.setSubject(subject); |
| 166 | + // true 为 HTML 邮件 |
| 167 | + messageHelper.setText(content, true); |
| 168 | + mailSender.send(message); |
| 169 | + log.info("【HTML 邮件】成功发送!to={}", to); |
| 170 | + } |
| 171 | +``` |
| 172 | +在测试方法中增加 HTML 邮件测试方法。 |
| 173 | +```java |
| 174 | + @Test |
| 175 | + public void sendHtmlMailTest() throws MessagingException { |
| 176 | + |
| 177 | + String subject = "Springboot 发送 HTML 邮件"; |
| 178 | + String content = "<h2>Hi~</h2><p>第一封 Springboot HTML 邮件</p>"; |
| 179 | + mailService.sendHtmlMail(to, subject, content); |
| 180 | + } |
| 181 | +``` |
| 182 | +运行单元测试,查看收信情况。 |
| 183 | + |
| 184 | + |
| 185 | + |
| 186 | +HTML 邮件正常收到,HTML 标签也被解析成对应的样式。 |
| 187 | + |
| 188 | +## Springboot mail 附件邮件 |
| 189 | +在上面的 `MailService` 类里新加一个方法 `sendAttachmentMail`,用于测试 附件邮件。 |
| 190 | +```java |
| 191 | + /** |
| 192 | + * 发送带附件的邮件 |
| 193 | + * |
| 194 | + * @param to |
| 195 | + * @param subject |
| 196 | + * @param content |
| 197 | + * @param fileArr |
| 198 | + */ |
| 199 | + public void sendAttachmentMail(String to, String subject, String content, String... fileArr) |
| 200 | + throws MessagingException { |
| 201 | + MimeMessage mimeMessage = mailSender.createMimeMessage(); |
| 202 | + MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true); |
| 203 | + messageHelper.setFrom(from); |
| 204 | + messageHelper.setTo(to); |
| 205 | + messageHelper.setSubject(subject); |
| 206 | + messageHelper.setText(content, true); |
| 207 | + |
| 208 | + // 添加附件 |
| 209 | + for (String filePath : fileArr) { |
| 210 | + FileSystemResource fileResource = new FileSystemResource(new File(filePath)); |
| 211 | + if (fileResource.exists()) { |
| 212 | + String filename = fileResource.getFilename(); |
| 213 | + messageHelper.addAttachment(filename, fileResource); |
| 214 | + } |
| 215 | + } |
| 216 | + mailSender.send(mimeMessage); |
| 217 | + log.info("【附件邮件】成功发送!to={}", to); |
| 218 | + } |
| 219 | +``` |
| 220 | +在测试方法中增加附件邮件测试方法。 |
| 221 | +```java |
| 222 | + @Test |
| 223 | + public void sendAttachmentTest() throws MessagingException { |
| 224 | + |
| 225 | + String subject = "Springboot 发送 HTML 附件邮件"; |
| 226 | + String content = "<h2>Hi~</h2><p>第一封 Springboot HTML 附件邮件</p>"; |
| 227 | + String filePath = "pom.xml"; |
| 228 | + mailService.sendAttachmentMail(to, subject, content, filePath, filePath); |
| 229 | + } |
| 230 | +``` |
| 231 | +运行单元测试,查看收信情况。 |
| 232 | + |
| 233 | + |
| 234 | + |
| 235 | +带附件的邮件正常收到,多个附件的实现方式同理。 |
| 236 | + |
| 237 | +## Springboot mail 图片邮件 |
| 238 | +图片邮件和其他的邮件方式略有不同,图片邮件需要先在内容中定义好图片的位置并出给一个记录 ID ,然后在把图片加到邮件中的对于的 ID 位置。 |
| 239 | + |
| 240 | +在上面的 `MailService` 类里新加一个方法 `sendImgMail`,用于测试 附件邮件。 |
| 241 | +```java |
| 242 | + /** |
| 243 | + * 发送带图片的邮件 |
| 244 | + * |
| 245 | + * @param to |
| 246 | + * @param subject |
| 247 | + * @param content |
| 248 | + * @param imgMap |
| 249 | + */ |
| 250 | + public void sendImgMail(String to, String subject, String content, Map<String, String> imgMap) |
| 251 | + throws MessagingException { |
| 252 | + MimeMessage mimeMessage = mailSender.createMimeMessage(); |
| 253 | + MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true); |
| 254 | + messageHelper.setFrom(from); |
| 255 | + messageHelper.setTo(to); |
| 256 | + messageHelper.setSubject(subject); |
| 257 | + messageHelper.setText(content, true); |
| 258 | + // 添加图片 |
| 259 | + for (Map.Entry<String, String> entry : imgMap.entrySet()) { |
| 260 | + FileSystemResource fileResource = new FileSystemResource(new File(entry.getValue())); |
| 261 | + if (fileResource.exists()) { |
| 262 | + String filename = fileResource.getFilename(); |
| 263 | + messageHelper.addInline(entry.getKey(), fileResource); |
| 264 | + } |
| 265 | + } |
| 266 | + mailSender.send(mimeMessage); |
| 267 | + log.info("【图片邮件】成功发送!to={}", to); |
| 268 | + } |
| 269 | +``` |
| 270 | +在测试方法中增加图片邮件测试方法,测试方法中使用的 apple.png 是项目里的一个图片。可以看上面的项目结构。 |
| 271 | +```java |
| 272 | + @Test |
| 273 | + public void sendImgTest() throws MessagingException { |
| 274 | + |
| 275 | + String subject = "Springboot 发送 HTML 图片邮件"; |
| 276 | + String content = |
| 277 | + "<h2>Hi~</h2><p>第一封 Springboot HTML 图片邮件</p><br/><img src=\"cid:img01\" /><img src=\"cid:img02\" />"; |
| 278 | + String imgPath = "apple.png"; |
| 279 | + Map<String, String> imgMap = new HashMap<>(); |
| 280 | + imgMap.put("img01", imgPath); |
| 281 | + imgMap.put("img02", imgPath); |
| 282 | + mailService.sendImgMail(to, subject, content, imgMap); |
| 283 | + } |
| 284 | +``` |
| 285 | +运行单元测试,查看收信情况。 |
| 286 | + |
| 287 | + |
| 288 | + |
| 289 | +两个图片正常显示在邮件里。 |
| 290 | + |
| 291 | +## Springboot mail 模版邮件 |
| 292 | +模版邮件的用处很广泛,像经常收到的注册成功邮件或者是操作通知邮件等都是模版邮件,模版邮件往往只需要更改其中的几个变量。Springboot 中的模版邮件首选需要选择一款模版引擎,在引入依赖的时候已经增加了模版引擎 `Thymeleaf`. |
| 293 | + |
| 294 | +模版邮件首先需要一个邮件模版,我们在 `Templates` 下新建一个 `HTML` 文件 `RegisterSuccess.html`. 其中的 username 是给我们自定义的。 |
| 295 | +```html |
| 296 | +<!DOCTYPE html> |
| 297 | +<html lang="en" xmlns:th="http://www.thymeleaf.org"> |
| 298 | +<head> |
| 299 | + <meta charset="UTF-8"> |
| 300 | + <title>注册成功通知</title> |
| 301 | +</head> |
| 302 | +<body> |
| 303 | +<p>[[${username}]],您好! |
| 304 | +</p> |
| 305 | +<p> |
| 306 | + 新的公钥已添加到你的账户:<br/> |
| 307 | + 标题: HP-WIN10 <br/> |
| 308 | + 如果公钥无法使用,你可以在这里重新添加: SSH Keys |
| 309 | +</p> |
| 310 | +</body> |
| 311 | +</html> |
| 312 | +``` |
| 313 | +在邮件服务 `MailService` 中注入模版引擎,然后编写邮件模版发送代码。 |
| 314 | +```java |
| 315 | + @Autowired |
| 316 | + private TemplateEngine templateEngine; |
| 317 | + |
| 318 | + /** |
| 319 | + * 发送模版邮件 |
| 320 | + * |
| 321 | + * @param to |
| 322 | + * @param subject |
| 323 | + * @param paramMap |
| 324 | + * @param template |
| 325 | + * @throws MessagingException |
| 326 | + */ |
| 327 | + public void sendTemplateMail(String to, String subject, Map<String, Object> paramMap, String template) |
| 328 | + throws MessagingException { |
| 329 | + Context context = new Context(); |
| 330 | + // 设置变量的值 |
| 331 | + context.setVariables(paramMap); |
| 332 | + String emailContent = templateEngine.process(template, context); |
| 333 | + sendHtmlMail(to, subject, emailContent); |
| 334 | + log.info("【模版邮件】成功发送!paramsMap={},template={}", paramMap, template); |
| 335 | + } |
| 336 | +``` |
| 337 | +在单元单元测试中增加模版邮件测试方法,然后发送邮件测试。 |
| 338 | +```java |
| 339 | + @Test |
| 340 | + public void sendTemplateMailTest() throws MessagingException { |
| 341 | + |
| 342 | + String subject = "Springboot 发送 模版邮件"; |
| 343 | + Map<String, Object> paramMap = new HashMap(); |
| 344 | + paramMap.put("username", "Darcy"); |
| 345 | + mailService.sendTemplateMail(to, subject, paramMap, "RegisterSuccess"); |
| 346 | + } |
| 347 | +``` |
| 348 | +查看收信情况。 |
| 349 | + |
| 350 | + |
| 351 | + |
| 352 | +可以发现模版邮件已经正常发送了。 |
| 353 | + |
| 354 | +## Springboot mail 补充 |
| 355 | +上面的例子中,是 Springboot 邮件服务的基本用法,代码也有很多重复,和实际的使用情况相比还有很多不足,比如缺少`异常处理机制`,在发送失败时的`重试机制`也没有,实际情况中邮件服务往往对实时性不高,多说情况下会用于`异步请求`。 |
| 356 | + |
| 357 | +文章相关代码已经上传 Github [Spring Boot 相关整合 - 邮件服务](https://github.com/niumoo/springboot/tree/master/springboot-mail)。 |
| 358 | + |
| 359 | +<完> |
| 360 | +本文原发于个人博客:[https://www.wdbyte.com](https://www.wdbyte.com) 转载请注明出处。 |
0 commit comments