From 0ace6bfd9cad3df2b3413009cff9664c1b0e388a Mon Sep 17 00:00:00 2001 From: "Yangkai.Shen" <237497819@qq.com> Date: Tue, 23 Aug 2022 13:32:35 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E5=9F=BA=E7=A1=80=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E4=B9=8B=20=E9=82=AE=E4=BB=B6=20=E6=A1=88=E4=BE=8B?= =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- demo-base/demo-base-email/README.md | 255 ++++++++++ .../demo-base-email}/pom.xml | 35 +- .../com/xkcoding/email/EmailApplication.java | 4 +- .../xkcoding/email/service/MailService.java | 4 +- .../email/service/impl/MailServiceImpl.java | 56 ++- .../src/main/resources/application.yml | 5 +- .../src/main/resources/email/test.html | 0 .../src/main/resources/static/xkcoding.png | Bin 0 -> 94984 bytes .../src/main/resources/templates/welcome.html | 0 .../xkcoding/email/EmailApplicationTests.java | 13 + .../java/com/xkcoding/email/PasswordTest.java | 8 +- .../email/service/MailServiceTest.java | 46 +- demo-base/pom.xml | 1 + demo-email/.gitignore | 25 - demo-email/README.md | 441 ------------------ .../src/main/resources/static/xkcoding.png | Bin 7310 -> 0 bytes .../SpringBootDemoEmailApplicationTests.java | 16 - pom.xml | 1 - 18 files changed, 349 insertions(+), 561 deletions(-) create mode 100644 demo-base/demo-base-email/README.md rename {demo-email => demo-base/demo-base-email}/pom.xml (75%) rename demo-email/src/main/java/com/xkcoding/email/SpringBootDemoEmailApplication.java => demo-base/demo-base-email/src/main/java/com/xkcoding/email/EmailApplication.java (72%) rename {demo-email => demo-base/demo-base-email}/src/main/java/com/xkcoding/email/service/MailService.java (94%) rename {demo-email => demo-base/demo-base-email}/src/main/java/com/xkcoding/email/service/impl/MailServiceImpl.java (75%) rename {demo-email => demo-base/demo-base-email}/src/main/resources/application.yml (76%) rename {demo-email => demo-base/demo-base-email}/src/main/resources/email/test.html (100%) create mode 100644 demo-base/demo-base-email/src/main/resources/static/xkcoding.png rename {demo-email => demo-base/demo-base-email}/src/main/resources/templates/welcome.html (100%) create mode 100644 demo-base/demo-base-email/src/test/java/com/xkcoding/email/EmailApplicationTests.java rename {demo-email => demo-base/demo-base-email}/src/test/java/com/xkcoding/email/PasswordTest.java (83%) rename {demo-email => demo-base/demo-base-email}/src/test/java/com/xkcoding/email/service/MailServiceTest.java (64%) delete mode 100644 demo-email/.gitignore delete mode 100644 demo-email/README.md delete mode 100644 demo-email/src/main/resources/static/xkcoding.png delete mode 100644 demo-email/src/test/java/com/xkcoding/email/SpringBootDemoEmailApplicationTests.java diff --git a/demo-base/demo-base-email/README.md b/demo-base/demo-base-email/README.md new file mode 100644 index 0000000..2b1b8fb --- /dev/null +++ b/demo-base/demo-base-email/README.md @@ -0,0 +1,255 @@ +## spring-boot-demo-email + +> 此 demo 主要演示了 Spring Boot 如何整合邮件功能,包括发送简单文本邮件、HTML邮件(包括模板HTML邮件)、附件邮件、静态资源邮件。 + +### 1.开发步骤 + +#### 1.1.添加依赖 + +```xml + + + + com.xkcoding + common-tools + + + + + org.springframework.boot + spring-boot-starter-mail + + + + + com.github.ulisesbocchio + jasypt-spring-boot-starter + ${jasypt.version} + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + +``` + +#### 1.2.添加邮件相关配置 + +```yaml +spring: + mail: + host: smtp.mxhichina.com + port: 465 + from: "SpringBootDemo测试<${spring.mail.username}>" + username: spring-boot-demo@xkcoding.com + # 使用 jasypt 加密密码,使用com.xkcoding.email.PasswordTest.testGeneratePassword 生成加密密码,替换 ENC(加密密码) + password: ENC(aef0+nM5440HO7YFAo7iUz8ZHpkjZVlR0hNw3OI/QOPSkNhYRImE/Oy1LBgFKoB1OjqW0v4ZdM0xNS0eKxELfA==) + protocol: smtp + test-connection: true + default-encoding: UTF-8 + properties: + mail.smtp.auth: true + mail.smtp.starttls.enable: true + mail.smtp.starttls.required: true + mail.smtp.ssl.enable: true + mail.display.sendmail: spring-boot-demo +# 为 jasypt 配置解密秘钥 +jasypt: + encryptor: + password: spring-boot-demo +``` + +#### 1.3.编写发送邮件代码 + +- **抽象邮件服务接口**,方便后期替换不同的客户端实现 + +```java +public interface MailService { + /** + * 发送文本邮件 + * + * @param to 收件人地址 + * @param subject 邮件主题 + * @param content 邮件内容 + * @param cc 抄送地址 + */ + void sendSimpleMail(String to, String subject, String content, String... cc); + + /** + * 发送HTML邮件 + * + * @param to 收件人地址 + * @param subject 邮件主题 + * @param content 邮件内容 + * @param cc 抄送地址 + * @throws MessagingException 邮件发送异常 + */ + void sendHtmlMail(String to, String subject, String content, String... cc) + throws MessagingException, MessagingException; + + /** + * 发送带附件的邮件 + * + * @param to 收件人地址 + * @param subject 邮件主题 + * @param content 邮件内容 + * @param filePath 附件地址 + * @param cc 抄送地址 + * @throws MessagingException 邮件发送异常 + */ + void sendAttachmentsMail(String to, String subject, String content, String filePath, String... cc) + throws MessagingException; + + /** + * 发送正文中有静态资源的邮件 + * + * @param to 收件人地址 + * @param subject 邮件主题 + * @param content 邮件内容 + * @param rscPath 静态资源地址 + * @param rscId 静态资源id + * @param cc 抄送地址 + * @throws MessagingException 邮件发送异常 + */ + void sendResourceMail(String to, String subject, String content, String rscPath, String rscId, String... cc) + throws MessagingException; +``` + +- 接口实现 + +```java + +@Service +public class MailServiceImpl implements MailService { + @Autowired + private JavaMailSender mailSender; + @Value("${spring.mail.from}") + private String from; + + /** + * 发送文本邮件 + * + * @param to 收件人地址 + * @param subject 邮件主题 + * @param content 邮件内容 + * @param cc 抄送地址 + */ + @Override + public void sendSimpleMail(String to, String subject, String content, String... cc) { + SimpleMailMessage message = new SimpleMailMessage(); + message.setFrom(from); + message.setTo(to); + message.setSubject(subject); + message.setText(content); + if (ArrayUtil.isNotEmpty(cc)) { + message.setCc(cc); + } + mailSender.send(message); + } + + /** + * 发送HTML邮件 + * + * @param to 收件人地址 + * @param subject 邮件主题 + * @param content 邮件内容 + * @param cc 抄送地址 + */ + @Override + public void sendHtmlMail(String to, String subject, String content, String... cc) throws MessagingException { + MimeEmail mimeEmail = basicMimeEmailBuilder(to, subject, content, cc); + mailSender.send(mimeEmail.message()); + } + + /** + * 发送带附件的邮件 + * + * @param to 收件人地址 + * @param subject 邮件主题 + * @param content 邮件内容 + * @param filePath 附件地址 + * @param cc 抄送地址 + * @throws MessagingException 邮件发送异常 + */ + @Override + public void sendAttachmentsMail(String to, String subject, String content, String filePath, String... cc) + throws MessagingException { + MimeEmail mimeEmail = basicMimeEmailBuilder(to, subject, content, cc); + + MimeMessageHelper helper = mimeEmail.helper(); + FileSystemResource file = new FileSystemResource(new File(filePath)); + String fileName = filePath.substring(filePath.lastIndexOf(File.separator)); + helper.addAttachment(fileName, file); + + mailSender.send(mimeEmail.message()); + } + + /** + * 发送正文中有静态资源的邮件 + * + * @param to 收件人地址 + * @param subject 邮件主题 + * @param content 邮件内容 + * @param rscPath 静态资源地址 + * @param rscId 静态资源id + * @param cc 抄送地址 + * @throws MessagingException 邮件发送异常 + */ + @Override + public void sendResourceMail(String to, String subject, String content, String rscPath, String rscId, String... cc) + throws MessagingException { + MimeEmail mimeEmail = basicMimeEmailBuilder(to, subject, content, cc); + + MimeMessageHelper helper = mimeEmail.helper(); + FileSystemResource res = new FileSystemResource(new File(rscPath)); + helper.addInline(rscId, res); + + mailSender.send(mimeEmail.message()); + } + + /** + * 富文本邮件构造器,抽取重复代码,返回一个 MimeEmail record + */ + private MimeEmail basicMimeEmailBuilder(String to, String subject, String content, String... cc) + throws MessagingException { + MimeMessage message = mailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message, true); + helper.setFrom(from); + helper.setTo(to); + helper.setSubject(subject); + helper.setText(content, true); + if (ArrayUtil.isNotEmpty(cc)) { + helper.setCc(cc); + } + return new MimeEmail(message, helper); + } + + private record MimeEmail(MimeMessage message, MimeMessageHelper helper) { + + } +} +``` + +- 其他资源文件参考 `classpath://resources` 目录 + +### 2.测试 + +参考 `MailServiceTest` 测试用例,分别运行各个方法,进行邮件测试 +> 注意: +> 1. 最好是每个方法分别运行,不要直接在测试类上一下跑所有测试用例,因为 Spring 的 `templateResolver` 一旦初始化是不允许修改的 +> 2. **强烈建议各位同学测试的时候,把邮箱改成自己的邮箱进行测试,这样才能实际体会到收到的邮件内容** +> 3. 请勿将 `spring-boot-demo@xkcoding.com` 的邮箱用于发送违法内容,否则作者将收回邮箱权限,同时提交给公安依法追究 + +### 3.参考 + +- Spring Boot 官方文档:https://docs.spring.io/spring-boot/docs/3.0.0-M4/reference/htmlsingle/#io.email +- Spring 官方文档:https://docs.spring.io/spring-framework/docs/6.0.0-M5/reference/html/integration.html#mail +- jasypt 加解密文档:https://github.com/ulisesbocchio/jasypt-spring-boot#what-to-do-first diff --git a/demo-email/pom.xml b/demo-base/demo-base-email/pom.xml similarity index 75% rename from demo-email/pom.xml rename to demo-base/demo-base-email/pom.xml index 2c7b9f4..4f69d37 100644 --- a/demo-email/pom.xml +++ b/demo-base/demo-base-email/pom.xml @@ -1,36 +1,40 @@ + + com.xkcoding + demo-base + 1.0.0-SNAPSHOT + + 4.0.0 - demo-email + demo-base-email 1.0.0-SNAPSHOT jar - demo-email + demo-base-email Demo project for Spring Boot - - com.xkcoding - spring-boot-demo - 1.0.0-SNAPSHOT - - UTF-8 - UTF-8 - 1.8 - 2.1.1 + 17 + 3.0.4 + + com.xkcoding + common-tools + + org.springframework.boot spring-boot-starter-mail - + com.github.ulisesbocchio jasypt-spring-boot-starter @@ -43,11 +47,6 @@ test - - cn.hutool - hutool-all - - org.springframework.boot @@ -56,7 +55,7 @@ - demo-email + demo-base-email org.springframework.boot diff --git a/demo-email/src/main/java/com/xkcoding/email/SpringBootDemoEmailApplication.java b/demo-base/demo-base-email/src/main/java/com/xkcoding/email/EmailApplication.java similarity index 72% rename from demo-email/src/main/java/com/xkcoding/email/SpringBootDemoEmailApplication.java rename to demo-base/demo-base-email/src/main/java/com/xkcoding/email/EmailApplication.java index 9e054fa..24389ad 100644 --- a/demo-email/src/main/java/com/xkcoding/email/SpringBootDemoEmailApplication.java +++ b/demo-base/demo-base-email/src/main/java/com/xkcoding/email/EmailApplication.java @@ -12,9 +12,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; * @date Created in 2018-11-04 22:38 */ @SpringBootApplication -public class SpringBootDemoEmailApplication { +public class EmailApplication { public static void main(String[] args) { - SpringApplication.run(SpringBootDemoEmailApplication.class, args); + SpringApplication.run(EmailApplication.class, args); } } diff --git a/demo-email/src/main/java/com/xkcoding/email/service/MailService.java b/demo-base/demo-base-email/src/main/java/com/xkcoding/email/service/MailService.java similarity index 94% rename from demo-email/src/main/java/com/xkcoding/email/service/MailService.java rename to demo-base/demo-base-email/src/main/java/com/xkcoding/email/service/MailService.java index b7e5764..a6c16dc 100644 --- a/demo-email/src/main/java/com/xkcoding/email/service/MailService.java +++ b/demo-base/demo-base-email/src/main/java/com/xkcoding/email/service/MailService.java @@ -1,6 +1,6 @@ package com.xkcoding.email.service; -import javax.mail.MessagingException; +import jakarta.mail.MessagingException; /** *

@@ -30,7 +30,7 @@ public interface MailService { * @param cc 抄送地址 * @throws MessagingException 邮件发送异常 */ - void sendHtmlMail(String to, String subject, String content, String... cc) throws MessagingException; + void sendHtmlMail(String to, String subject, String content, String... cc) throws MessagingException, MessagingException; /** * 发送带附件的邮件 diff --git a/demo-email/src/main/java/com/xkcoding/email/service/impl/MailServiceImpl.java b/demo-base/demo-base-email/src/main/java/com/xkcoding/email/service/impl/MailServiceImpl.java similarity index 75% rename from demo-email/src/main/java/com/xkcoding/email/service/impl/MailServiceImpl.java rename to demo-base/demo-base-email/src/main/java/com/xkcoding/email/service/impl/MailServiceImpl.java index 59a8e13..ea343e9 100644 --- a/demo-email/src/main/java/com/xkcoding/email/service/impl/MailServiceImpl.java +++ b/demo-base/demo-base-email/src/main/java/com/xkcoding/email/service/impl/MailServiceImpl.java @@ -2,6 +2,8 @@ package com.xkcoding.email.service.impl; import cn.hutool.core.util.ArrayUtil; import com.xkcoding.email.service.MailService; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.FileSystemResource; @@ -10,8 +12,6 @@ import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; -import javax.mail.MessagingException; -import javax.mail.internet.MimeMessage; import java.io.File; /** @@ -26,7 +26,7 @@ import java.io.File; public class MailServiceImpl implements MailService { @Autowired private JavaMailSender mailSender; - @Value("${spring.mail.username}") + @Value("${spring.mail.from}") private String from; /** @@ -57,20 +57,11 @@ public class MailServiceImpl implements MailService { * @param subject 邮件主题 * @param content 邮件内容 * @param cc 抄送地址 - * @throws MessagingException 邮件发送异常 */ @Override public void sendHtmlMail(String to, String subject, String content, String... cc) throws MessagingException { - MimeMessage message = mailSender.createMimeMessage(); - MimeMessageHelper helper = new MimeMessageHelper(message, true); - helper.setFrom(from); - helper.setTo(to); - helper.setSubject(subject); - helper.setText(content, true); - if (ArrayUtil.isNotEmpty(cc)) { - helper.setCc(cc); - } - mailSender.send(message); + MimeEmail mimeEmail = basicMimeEmailBuilder(to, subject, content, cc); + mailSender.send(mimeEmail.message()); } /** @@ -85,21 +76,14 @@ public class MailServiceImpl implements MailService { */ @Override public void sendAttachmentsMail(String to, String subject, String content, String filePath, String... cc) throws MessagingException { - MimeMessage message = mailSender.createMimeMessage(); + MimeEmail mimeEmail = basicMimeEmailBuilder(to, subject, content, cc); - MimeMessageHelper helper = new MimeMessageHelper(message, true); - helper.setFrom(from); - helper.setTo(to); - helper.setSubject(subject); - helper.setText(content, true); - if (ArrayUtil.isNotEmpty(cc)) { - helper.setCc(cc); - } + MimeMessageHelper helper = mimeEmail.helper(); FileSystemResource file = new FileSystemResource(new File(filePath)); String fileName = filePath.substring(filePath.lastIndexOf(File.separator)); helper.addAttachment(fileName, file); - mailSender.send(message); + mailSender.send(mimeEmail.message()); } /** @@ -114,9 +98,22 @@ public class MailServiceImpl implements MailService { * @throws MessagingException 邮件发送异常 */ @Override - public void sendResourceMail(String to, String subject, String content, String rscPath, String rscId, String... cc) throws MessagingException { - MimeMessage message = mailSender.createMimeMessage(); + public void sendResourceMail(String to, String subject, String content, String rscPath, String rscId, String... cc) + throws MessagingException { + MimeEmail mimeEmail = basicMimeEmailBuilder(to, subject, content, cc); + + MimeMessageHelper helper = mimeEmail.helper(); + FileSystemResource res = new FileSystemResource(new File(rscPath)); + helper.addInline(rscId, res); + + mailSender.send(mimeEmail.message()); + } + /** + * 富文本邮件构造器,抽取重复代码,返回一个 MimeEmail record + */ + private MimeEmail basicMimeEmailBuilder(String to, String subject, String content, String... cc) throws MessagingException { + MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true); helper.setFrom(from); helper.setTo(to); @@ -125,9 +122,10 @@ public class MailServiceImpl implements MailService { if (ArrayUtil.isNotEmpty(cc)) { helper.setCc(cc); } - FileSystemResource res = new FileSystemResource(new File(rscPath)); - helper.addInline(rscId, res); + return new MimeEmail(message, helper); + } + + private record MimeEmail(MimeMessage message, MimeMessageHelper helper) { - mailSender.send(message); } } diff --git a/demo-email/src/main/resources/application.yml b/demo-base/demo-base-email/src/main/resources/application.yml similarity index 76% rename from demo-email/src/main/resources/application.yml rename to demo-base/demo-base-email/src/main/resources/application.yml index 719815b..1c730b8 100644 --- a/demo-email/src/main/resources/application.yml +++ b/demo-base/demo-base-email/src/main/resources/application.yml @@ -2,9 +2,10 @@ spring: mail: host: smtp.mxhichina.com port: 465 + from: "SpringBootDemo测试<${spring.mail.username}>" username: spring-boot-demo@xkcoding.com - # 使用 jasypt 加密密码,使用com.xkcoding.email.PasswordTest.testGeneratePassword 生成加密密码,替换 ENC(加密密码) - password: ENC(OT0qGOpXrr1Iog1W+fjOiIDCJdBjHyhy) + # 使用 jasypt 加密密码,使用com.xkcoding.email.PasswordTest.testGeneratePassword 生成加密密码,替换 ENC(加密密码) + password: ENC(aef0+nM5440HO7YFAo7iUz8ZHpkjZVlR0hNw3OI/QOPSkNhYRImE/Oy1LBgFKoB1OjqW0v4ZdM0xNS0eKxELfA==) protocol: smtp test-connection: true default-encoding: UTF-8 diff --git a/demo-email/src/main/resources/email/test.html b/demo-base/demo-base-email/src/main/resources/email/test.html similarity index 100% rename from demo-email/src/main/resources/email/test.html rename to demo-base/demo-base-email/src/main/resources/email/test.html diff --git a/demo-base/demo-base-email/src/main/resources/static/xkcoding.png b/demo-base/demo-base-email/src/main/resources/static/xkcoding.png new file mode 100644 index 0000000000000000000000000000000000000000..b1e9d30ae4f075bc964bce45d2f14ee532b00191 GIT binary patch literal 94984 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 HY#;bvYZ7{# literal 0 HcmV?d00001 diff --git a/demo-email/src/main/resources/templates/welcome.html b/demo-base/demo-base-email/src/main/resources/templates/welcome.html similarity index 100% rename from demo-email/src/main/resources/templates/welcome.html rename to demo-base/demo-base-email/src/main/resources/templates/welcome.html diff --git a/demo-base/demo-base-email/src/test/java/com/xkcoding/email/EmailApplicationTests.java b/demo-base/demo-base-email/src/test/java/com/xkcoding/email/EmailApplicationTests.java new file mode 100644 index 0000000..75d643c --- /dev/null +++ b/demo-base/demo-base-email/src/test/java/com/xkcoding/email/EmailApplicationTests.java @@ -0,0 +1,13 @@ +package com.xkcoding.email; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class EmailApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/demo-email/src/test/java/com/xkcoding/email/PasswordTest.java b/demo-base/demo-base-email/src/test/java/com/xkcoding/email/PasswordTest.java similarity index 83% rename from demo-email/src/test/java/com/xkcoding/email/PasswordTest.java rename to demo-base/demo-base-email/src/test/java/com/xkcoding/email/PasswordTest.java index 3f119e9..9bd0957 100644 --- a/demo-email/src/test/java/com/xkcoding/email/PasswordTest.java +++ b/demo-base/demo-base-email/src/test/java/com/xkcoding/email/PasswordTest.java @@ -1,8 +1,9 @@ package com.xkcoding.email; import org.jasypt.encryption.StringEncryptor; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; /** *

@@ -12,7 +13,8 @@ import org.springframework.beans.factory.annotation.Autowired; * @author yangkai.shen * @date Created in 2019-08-27 16:15 */ -public class PasswordTest extends SpringBootDemoEmailApplicationTests { +@SpringBootTest +class PasswordTest { @Autowired private StringEncryptor encryptor; @@ -20,7 +22,7 @@ public class PasswordTest extends SpringBootDemoEmailApplicationTests { * 生成加密密码 */ @Test - public void testGeneratePassword() { + void testGeneratePassword() { // 你的邮箱密码 String password = "Just4Test!"; // 加密后的密码(注意:配置上去的时候需要加 ENC(加密密码)) diff --git a/demo-email/src/test/java/com/xkcoding/email/service/MailServiceTest.java b/demo-base/demo-base-email/src/test/java/com/xkcoding/email/service/MailServiceTest.java similarity index 64% rename from demo-email/src/test/java/com/xkcoding/email/service/MailServiceTest.java rename to demo-base/demo-base-email/src/test/java/com/xkcoding/email/service/MailServiceTest.java index b23d352..fba9a88 100644 --- a/demo-email/src/test/java/com/xkcoding/email/service/MailServiceTest.java +++ b/demo-base/demo-base-email/src/test/java/com/xkcoding/email/service/MailServiceTest.java @@ -1,15 +1,16 @@ package com.xkcoding.email.service; import cn.hutool.core.io.resource.ResourceUtil; -import com.xkcoding.email.SpringBootDemoEmailApplicationTests; -import org.junit.Test; +import com.xkcoding.email.EmailApplication; +import jakarta.mail.MessagingException; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.ApplicationContext; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; -import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; +import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver; -import javax.mail.MessagingException; import java.net.URL; /** @@ -20,7 +21,8 @@ import java.net.URL; * @author yangkai.shen * @date Created in 2018-11-21 13:49 */ -public class MailServiceTest extends SpringBootDemoEmailApplicationTests { +@SpringBootTest(classes = EmailApplication.class) +class MailServiceTest { @Autowired private MailService mailService; @Autowired @@ -37,19 +39,19 @@ public class MailServiceTest extends SpringBootDemoEmailApplicationTests { } /** - * 测试HTML邮件 + * 测试HTML邮件(使用 classpath://templates/ 下的模板) * * @throws MessagingException 邮件异常 */ @Test - public void sendHtmlMail() throws MessagingException { - Context context = new Context(); - context.setVariable("project", "Spring Boot Demo"); - context.setVariable("author", "Yangkai.Shen"); - context.setVariable("url", "https://github.com/xkcoding/spring-boot-demo"); + void sendHtmlMail1() throws MessagingException { + Context varContext = new Context(); + varContext.setVariable("project", "Spring Boot Demo"); + varContext.setVariable("author", "Yangkai.Shen"); + varContext.setVariable("url", "https://github.com/xkcoding/spring-boot-demo"); - String emailTemplate = templateEngine.process("welcome", context); - mailService.sendHtmlMail("237497819@qq.com", "这是一封模板HTML邮件", emailTemplate); + String emailTemplate = templateEngine.process("welcome", varContext); + mailService.sendHtmlMail("237497819@qq.com", "这是一封模板HTML邮件(templates目录)", emailTemplate); } /** @@ -58,7 +60,7 @@ public class MailServiceTest extends SpringBootDemoEmailApplicationTests { * @throws MessagingException 邮件异常 */ @Test - public void sendHtmlMail2() throws MessagingException { + void sendHtmlMail2() throws MessagingException { SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); templateResolver.setApplicationContext(context); @@ -68,13 +70,13 @@ public class MailServiceTest extends SpringBootDemoEmailApplicationTests { templateEngine.setTemplateResolver(templateResolver); - Context context = new Context(); - context.setVariable("project", "Spring Boot Demo"); - context.setVariable("author", "Yangkai.Shen"); - context.setVariable("url", "https://github.com/xkcoding/spring-boot-demo"); + Context varContext = new Context(); + varContext.setVariable("project", "Spring Boot Demo"); + varContext.setVariable("author", "Yangkai.Shen"); + varContext.setVariable("url", "https://github.com/xkcoding/spring-boot-demo"); - String emailTemplate = templateEngine.process("test", context); - mailService.sendHtmlMail("237497819@qq.com", "这是一封模板HTML邮件", emailTemplate); + String emailTemplate = templateEngine.process("test", varContext); + mailService.sendHtmlMail("237497819@qq.com", "这是一封模板HTML邮件(自定义目录)", emailTemplate); } /** @@ -83,7 +85,7 @@ public class MailServiceTest extends SpringBootDemoEmailApplicationTests { * @throws MessagingException 邮件异常 */ @Test - public void sendAttachmentsMail() throws MessagingException { + void sendAttachmentsMail() throws MessagingException { URL resource = ResourceUtil.getResource("static/xkcoding.png"); mailService.sendAttachmentsMail("237497819@qq.com", "这是一封带附件的邮件", "邮件中有附件,请注意查收!", resource.getPath()); } @@ -94,7 +96,7 @@ public class MailServiceTest extends SpringBootDemoEmailApplicationTests { * @throws MessagingException 邮件异常 */ @Test - public void sendResourceMail() throws MessagingException { + void sendResourceMail() throws MessagingException { String rscId = "xkcoding"; String content = "这是带静态资源的邮件
"; URL resource = ResourceUtil.getResource("static/xkcoding.png"); diff --git a/demo-base/pom.xml b/demo-base/pom.xml index 4df90f9..3b5fa19 100644 --- a/demo-base/pom.xml +++ b/demo-base/pom.xml @@ -23,6 +23,7 @@ demo-base-properties demo-base-async demo-base-exception + demo-base-email diff --git a/demo-email/.gitignore b/demo-email/.gitignore deleted file mode 100644 index 82eca33..0000000 --- a/demo-email/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -/target/ -!.mvn/wrapper/maven-wrapper.jar - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/build/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ \ No newline at end of file diff --git a/demo-email/README.md b/demo-email/README.md deleted file mode 100644 index 85d037e..0000000 --- a/demo-email/README.md +++ /dev/null @@ -1,441 +0,0 @@ -# spring-boot-demo-email - -> 此 demo 主要演示了 Spring Boot 如何整合邮件功能,包括发送简单文本邮件、HTML邮件(包括模板HTML邮件)、附件邮件、静态资源邮件。 - -## pom.xml - -```xml - - - 4.0.0 - - spring-boot-demo-email - 1.0.0-SNAPSHOT - jar - - spring-boot-demo-email - Demo project for Spring Boot - - - com.xkcoding - spring-boot-demo - 1.0.0-SNAPSHOT - - - - UTF-8 - UTF-8 - 1.8 - 2.1.1 - - - - - - org.springframework.boot - spring-boot-starter-mail - - - - - com.github.ulisesbocchio - jasypt-spring-boot-starter - ${jasypt.version} - - - - org.springframework.boot - spring-boot-starter-test - test - - - - cn.hutool - hutool-all - - - - - org.springframework.boot - spring-boot-starter-thymeleaf - - - - - spring-boot-demo-email - - - org.springframework.boot - spring-boot-maven-plugin - - - - - -``` - -## application.yml - -```yaml -spring: - mail: - host: smtp.mxhichina.com - port: 465 - username: spring-boot-demo@xkcoding.com - # 使用 jasypt 加密密码,使用com.xkcoding.email.PasswordTest.testGeneratePassword 生成加密密码,替换 ENC(加密密码) - password: ENC(OT0qGOpXrr1Iog1W+fjOiIDCJdBjHyhy) - protocol: smtp - test-connection: true - default-encoding: UTF-8 - properties: - mail.smtp.auth: true - mail.smtp.starttls.enable: true - mail.smtp.starttls.required: true - mail.smtp.ssl.enable: true - mail.display.sendmail: spring-boot-demo -# 为 jasypt 配置解密秘钥 -jasypt: - encryptor: - password: spring-boot-demo - -``` - -## MailService.java - -```java -/** - *

- * 邮件接口 - *

- * - * @author yangkai.shen - * @date Created in 2018-11-21 11:16 - */ -public interface MailService { - /** - * 发送文本邮件 - * - * @param to 收件人地址 - * @param subject 邮件主题 - * @param content 邮件内容 - * @param cc 抄送地址 - */ - void sendSimpleMail(String to, String subject, String content, String... cc); - - /** - * 发送HTML邮件 - * - * @param to 收件人地址 - * @param subject 邮件主题 - * @param content 邮件内容 - * @param cc 抄送地址 - * @throws MessagingException 邮件发送异常 - */ - void sendHtmlMail(String to, String subject, String content, String... cc) throws MessagingException; - - /** - * 发送带附件的邮件 - * - * @param to 收件人地址 - * @param subject 邮件主题 - * @param content 邮件内容 - * @param filePath 附件地址 - * @param cc 抄送地址 - * @throws MessagingException 邮件发送异常 - */ - void sendAttachmentsMail(String to, String subject, String content, String filePath, String... cc) throws MessagingException; - - /** - * 发送正文中有静态资源的邮件 - * - * @param to 收件人地址 - * @param subject 邮件主题 - * @param content 邮件内容 - * @param rscPath 静态资源地址 - * @param rscId 静态资源id - * @param cc 抄送地址 - * @throws MessagingException 邮件发送异常 - */ - void sendResourceMail(String to, String subject, String content, String rscPath, String rscId, String... cc) throws MessagingException; - -} -``` - -## MailServiceImpl.java - -```java -/** - *

- * 邮件接口 - *

- * - * @author yangkai.shen - * @date Created in 2018-11-21 13:49 - */ -@Service -public class MailServiceImpl implements MailService { - @Autowired - private JavaMailSender mailSender; - @Value("${spring.mail.username}") - private String from; - - /** - * 发送文本邮件 - * - * @param to 收件人地址 - * @param subject 邮件主题 - * @param content 邮件内容 - * @param cc 抄送地址 - */ - @Override - public void sendSimpleMail(String to, String subject, String content, String... cc) { - SimpleMailMessage message = new SimpleMailMessage(); - message.setFrom(from); - message.setTo(to); - message.setSubject(subject); - message.setText(content); - if (ArrayUtil.isNotEmpty(cc)) { - message.setCc(cc); - } - mailSender.send(message); - } - - /** - * 发送HTML邮件 - * - * @param to 收件人地址 - * @param subject 邮件主题 - * @param content 邮件内容 - * @param cc 抄送地址 - * @throws MessagingException 邮件发送异常 - */ - @Override - public void sendHtmlMail(String to, String subject, String content, String... cc) throws MessagingException { - MimeMessage message = mailSender.createMimeMessage(); - MimeMessageHelper helper = new MimeMessageHelper(message, true); - helper.setFrom(from); - helper.setTo(to); - helper.setSubject(subject); - helper.setText(content, true); - if (ArrayUtil.isNotEmpty(cc)) { - helper.setCc(cc); - } - mailSender.send(message); - } - - /** - * 发送带附件的邮件 - * - * @param to 收件人地址 - * @param subject 邮件主题 - * @param content 邮件内容 - * @param filePath 附件地址 - * @param cc 抄送地址 - * @throws MessagingException 邮件发送异常 - */ - @Override - public void sendAttachmentsMail(String to, String subject, String content, String filePath, String... cc) throws MessagingException { - MimeMessage message = mailSender.createMimeMessage(); - - MimeMessageHelper helper = new MimeMessageHelper(message, true); - helper.setFrom(from); - helper.setTo(to); - helper.setSubject(subject); - helper.setText(content, true); - if (ArrayUtil.isNotEmpty(cc)) { - helper.setCc(cc); - } - FileSystemResource file = new FileSystemResource(new File(filePath)); - String fileName = filePath.substring(filePath.lastIndexOf(File.separator)); - helper.addAttachment(fileName, file); - - mailSender.send(message); - } - - /** - * 发送正文中有静态资源的邮件 - * - * @param to 收件人地址 - * @param subject 邮件主题 - * @param content 邮件内容 - * @param rscPath 静态资源地址 - * @param rscId 静态资源id - * @param cc 抄送地址 - * @throws MessagingException 邮件发送异常 - */ - @Override - public void sendResourceMail(String to, String subject, String content, String rscPath, String rscId, String... cc) throws MessagingException { - MimeMessage message = mailSender.createMimeMessage(); - - MimeMessageHelper helper = new MimeMessageHelper(message, true); - helper.setFrom(from); - helper.setTo(to); - helper.setSubject(subject); - helper.setText(content, true); - if (ArrayUtil.isNotEmpty(cc)) { - helper.setCc(cc); - } - FileSystemResource res = new FileSystemResource(new File(rscPath)); - helper.addInline(rscId, res); - - mailSender.send(message); - } -} -``` - -## MailServiceTest.java - -```java -/** - *

- * 邮件测试 - *

- * - * @author yangkai.shen - * @date Created in 2018-11-21 13:49 - */ -public class MailServiceTest extends SpringBootDemoEmailApplicationTests { - @Autowired - private MailService mailService; - @Autowired - private TemplateEngine templateEngine; - @Autowired - private ApplicationContext context; - - /** - * 测试简单邮件 - */ - @Test - public void sendSimpleMail() { - mailService.sendSimpleMail("237497819@qq.com", "这是一封简单邮件", "这是一封普通的SpringBoot测试邮件"); - } - - /** - * 测试HTML邮件 - * - * @throws MessagingException 邮件异常 - */ - @Test - public void sendHtmlMail() throws MessagingException { - Context context = new Context(); - context.setVariable("project", "Spring Boot Demo"); - context.setVariable("author", "Yangkai.Shen"); - context.setVariable("url", "https://github.com/xkcoding/spring-boot-demo"); - - String emailTemplate = templateEngine.process("welcome", context); - mailService.sendHtmlMail("237497819@qq.com", "这是一封模板HTML邮件", emailTemplate); - } - - /** - * 测试HTML邮件,自定义模板目录 - * - * @throws MessagingException 邮件异常 - */ - @Test - public void sendHtmlMail2() throws MessagingException { - - SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); - templateResolver.setApplicationContext(context); - templateResolver.setCacheable(false); - templateResolver.setPrefix("classpath:/email/"); - templateResolver.setSuffix(".html"); - - templateEngine.setTemplateResolver(templateResolver); - - Context context = new Context(); - context.setVariable("project", "Spring Boot Demo"); - context.setVariable("author", "Yangkai.Shen"); - context.setVariable("url", "https://github.com/xkcoding/spring-boot-demo"); - - String emailTemplate = templateEngine.process("test", context); - mailService.sendHtmlMail("237497819@qq.com", "这是一封模板HTML邮件", emailTemplate); - } - - /** - * 测试附件邮件 - * - * @throws MessagingException 邮件异常 - */ - @Test - public void sendAttachmentsMail() throws MessagingException { - URL resource = ResourceUtil.getResource("static/xkcoding.png"); - mailService.sendAttachmentsMail("237497819@qq.com", "这是一封带附件的邮件", "邮件中有附件,请注意查收!", resource.getPath()); - } - - /** - * 测试静态资源邮件 - * - * @throws MessagingException 邮件异常 - */ - @Test - public void sendResourceMail() throws MessagingException { - String rscId = "xkcoding"; - String content = "这是带静态资源的邮件
"; - URL resource = ResourceUtil.getResource("static/xkcoding.png"); - mailService.sendResourceMail("237497819@qq.com", "这是一封带静态资源的邮件", content, resource.getPath(), rscId); - } -} -``` - -## welcome.html - -> 此文件为邮件模板,位于 resources/templates 目录下 - -```html - - - - - SpringBootDemo(入门SpringBoot的首选Demo) - - - -
-

欢迎使用 - Powered By

- -
-
- 如果对你有帮助,请任意打赏 -
-
-
-
-
-
- -
-
微信打赏
-
-
-
-
-
支付宝打赏
-
-
-
-
- -
- - -``` - -## 参考 - -- Spring Boot 官方文档:https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#boot-features-email -- Spring Boot 官方文档:https://docs.spring.io/spring/docs/5.1.2.RELEASE/spring-framework-reference/integration.html#mail diff --git a/demo-email/src/main/resources/static/xkcoding.png b/demo-email/src/main/resources/static/xkcoding.png deleted file mode 100644 index da90425dbbb6e3d166e76c2d7cea526b699a781b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7310 zcmV;99C71`P)sdPg$y%f zaPec9A%lz`!weZ_$Y7(uAY0F{4H;(0Afvz_Th5TzkhkA{djyic=RNNkWF+ovIpYLF z#v5#ainzyJO3%{0?Y(+q3KAP`5eb=FyDHLI__dh@>by>Ae# zX!Msb4LOD&a*Q};oO zOww$(-FD52E3Q~%8^ea0ksopl0aGB05&!wmf8Jbl(M8RxufEzt{11HK1I>Ett=Fu! z+G@>YlT9|PA!7==|Ni&Ci&O_{0y|zY;;(=GYjf+Zw>B4EcwuwjefN!|0q%S6d*9p4 zKmYv2$a|f1(naI}am`RGSK+AO)`lFgUC^rhw_ANfeR9n(GQ-eHFHp+b>d1b6-IZ+~l! zJMOsVm%sdFu@i`ii1D`C1P!r$9_C;SEm>iO6`J+eU%#1h$|;M%$8g#-n65#mA*4Gn z^^0HpqB-)&Bb%3Bez}Kcae2KF#`H!|8eptA0AbYh(@$Rv{};aSg&xB{%#ex#k@UK> zj{*Dbw_kJJb=UP^{kugpOvI!$)>xz2bkj|X=G1eK?YL#EYe*Opt@!hw|J>}h+iuOh z_ugA1{N2X*7=0SE%reU~-}%mWN-&wZ-tGB16{JDI5X6Axk3RZnv*(_BHh=u%9~*{z z!U-oFA?ogQbECSShF}QWvdAKfG&}FSbMwIuez0iD|Gb6-=ee_c@4a_(`|YZbZ-4vSC9oXl8Ip~Y${1|gfaqR*?)QTZI;aGBi8pG49gitd zcb(4)fsGwQ9)lkk4AYHSj~`D9^Ecjj<7R^mHYkEnuQ^(l@!xF$X=`!UA*(i+Hp-2) zIjM4x;rA=Ays{YafBy5Ig{WZ?hIu}(HACxoBMi&t!u&8O^VCF*%?NWD_aFcG$IYB` z&e{C-x4&&JzWCyjCtGNtg_`A;U%r`Ys;LG9(gutnjP)63oKXTh@x*-d%~zxnvA*@K zZ#94V)1O8P2*ZpirkJ9nLHu~{AOHAA`FZih7Yp;EX(6sLC!c)s#&cdlAJ5JGe&T5* z0L4hp&O7hC&F4S=`R1oT{b`T38DCJj!wx%?`MaGP>pDYhTCRx(Tz>iGBM@)F1s7~S z^{G#lT1K6n&8Fv?Yp!PHl~-=&m}8FSLm&E3ffJvRT#@@-cimManMdaG`E0Yz)~vMB zO3m!E&)&>1!wkhSh-N+e?6bv2iTEFQ;DH`@7ehG_sig$gqER%BHzq$ePC(WTH{8(d zw9`(-R@kf`n-3B4UZ82>i6?Hh-g@hXu|?42o_Z_MzVy;d&AI2E+njgadCf)}ZB#P7 z7!!NZ&S4=k4L$wz)5{NG@m^pp+VcMQzrU<6%v@}-#hL>SIG~K(&1*n3guxbd+hAgspQ9~^36g3rsqFyN$;zzZnrp6U_TPX10)Jtg&lq=+{2_-N z()|ASzc1r|@{^xzjyU3o0$bg@281Dd?6F64)m2v&f*pG3p#|6X-FM&S&O7fcM2GP< z?Pu3rcWstiZn?7OI0Q+5^v1*_BLjKHXnSEX^3kvw9#1^+#OA1@j_RSY>@dR9+8_Mj z2hG)2UtK<9Uk*F$u(F44UR`Gh;v9bX;ms+hoKhHZ{PD*(GtWG8NsAzUU<{E2X*`{Y zCYq=~1};K{inmoWE-tLk6tj3!Yz$4Hu`j&vLMi_~_~3(Oe7P<$ ziQp8o;ySOr_S((1+iu(SIHl_hv7wu8x~Y^|5&vT!`&jW>e*NoTH}~9gPmi#ncT|*Lp@&6h2>#x7QcsbKfJ8g6G%{TW%&fMRF z4?eiOUtfWYI{(xOL*ly95LaT5jN$!fo_VJE`q#f+ATqFb?6Jodp|KuNTL+t+%-`v~ zU1vzr6T$M=UVE)DLBuZo?QeMg=}&*U7)_dz5^kPJ@-d!JC!5UtwSy3&9>+m^LPMT> z^2r`8AUJ7~sK@AbuB+<|X%jCMKX4#s;I1M#Ll4W7b8be5_bcE6@qKH3uR3h!f34xd~-Q<6J-rNG$?|%2YJ@c-+?z+u4 zzxmCP;H{hM$k+qU5YQ33AU5Ig#DHsb+iI(=dg71dvbsqK_Dmou-be<4{g8SY8}*R^ zXNXP5(DJq<7Jv7<-xcqNXTy%nGRrLGZ?M+wRE3Vm-FM%;z!k0i;upVI7!>z+bX@H* z5ugEQNPDrOb;;^f4B&Q z)}a{kZYLAl^cd?5vGGeTxunD?@=~-%78sKd#+ACp==4#=?M@Jw(ya{f4}bW>5`1C? z8z#l`xzByBKxhh8qay%AT0>H%6*Ox3k-bGA8lrJ}*=3h4!C0VUbO@B-kf!kJuDIfg z((5JDi$OF)x z`OIfZDn+~^l9q^Ur`l;9n}i8ZJ@r&!gkqKeux^-JWtCM*H={P7I&kbS@w#!}pks(l zBB(<+++tZ_6|*pf=CBj&5N3cT4D}lLVHX5_OD(lj`AG&jc4lk@ zf`iVew>2c*O^EaS^Us$eksltJzft&oc0r5SBjsRKKRGkZbcMm~v`RN5 zzrSM+N)5^46R(8W{suc>&G``dFiA`COnSL!3xZ1uf;DyZ>86`*gm@-7vffxAy_{)QRSUrG(oeZveHAVT+dngM4>-4PN`r(Wzp=Z^YIsDZj~ zz!{R%dpw;ws7r)AP@5l&^9(pcq9GxEVu^Go4r@rFq5%?@B>E6ufG3U+D+IoV8PXv` z0yT-G(@d>dAvKKj$)+n&PRZ|*gHj!Sml`?dm}5$AO7bv?y)k7-+eS&l7+bMRT7?kU zgZ``>WlKlNE3dp#8s`L;g310=EEBYb9vLg=TRP1acMMtCa8evfpb~+~o;m0Ugswx$Y1#7D3VJE@3In?p1`Z7P9oxOPA!ONdMYv zuPt5p4?p~HY4mn{x<3r54<6}Sk)g)ST%GQxV}_&?5a%WT5Z59!xheAKLPuaZCzU^` z^v{0wvn8v#@WKlh0?5Gr_?94LS@UUNF#icBoKPAn zBp)?bNXM+T)>=h-G{w;%d+n=)ob--Yl}EIgamE>Y8pS)^OUDdJb8{G?eQr76;5KWt~Jz)j07P>r|R9?X2TE!l5(esdV}@m(iT}>60FS!oH?AshPkJ1 z)DCp^wWb;(^X(GtPWw)n(lJBYgQozLhH6K^z|unxJye*Zj**Nkc)S1p`%CK&W+9I6 zBZ-SS{4 z=R4R2nsCc4w=}!#vP&U|bLKuanQ!u6JyrjdxF5a=rvTh zA90O|Iki^Q#B*YbHgf>nXH5Oa=bUp+fnp9!PGngt?c-@;2;s4Zt{e3>w`1gQyY05- z```b5aVwQt8A=AO?Nx&+M+_Kv1agm9#kNg19H0>OSK@ zAn<9YomS=qKMvZ|&=%(CB?g(F_{1lQy<1|5B?@7!A-JFQ<_HB(%sCcwfKYv8T;p`= z2LZs9{X5J`tI{FUb%xj|Y0F_s6T(l@4UZ9t+EQb+?-=U@ggi4uU}SC1hS&>4L?GS} zEkHPgw|PuTM+mpoSq+-u-ZV(C2Ov0 zkx9GOAV{G17z{ep<6G=#SsJpPFb0PN2fWn{;$4TVND;=c35=$WKMT>;4|9Fv&N}O? zQ&g5QS4#EVfd?K~AjdO{F1l!`dxIK=+`&)~r7DO}x2Gj&(n%*37_3tU~vkpT`a{(5L*!rC%w`E zV_$?sM+=x^19@~{tJ6TYV=oJVK#bE*VhzW>U_9KnaL_@WwS$rvQpT+WV;FZw}Z~Q?l(oHLU@fnFX zChfo=UJ-APQvm*G5<(jXlVlT&MG*A+UUu1K#d%=|9rGqY&;p}#1kb)O7c}?fHn+8> zV}>x^R6d6xX@<>6iy#;#!D@KR7Eo)_&J)5A@d^!swG6w@cwY=9?+h_)oZS$_VKQbS z6asn$OP~tu#JzPd=n-{0KF90RZ{^$~nB=ipC(q9up2KAJfj!|BNu|gha7cW617eIr{g4t zfocL_O8qq&>=SlEPEp5rZXuR|DKoXoVf@EVQT zn|o^Z-%rX#$x!_QXcY4bI(0@$wT?d30x;NB2&ax`V{rHJsxQbQM)vW z)VLamk}gJy#(l$sctr3eTN??oY4wr7r1xow9|A%+0%D}q z!4#^gd9y%{E;rf7>PlNL{~N}Yd|~QZ^LR9>{+y(u&5;3 zo79cEO$;`Op%@^Q2-j2jh0>`|%+ib$n^G;7cIK-TVY?F8lhw!IxmL*o5J z`W%KtJjEBuvibmKZOm#fBnFQ3(kP2YFeTR78>(GJsRyt^%3-=HUl;-oFa+Ze7D0WM zZ>TdL31<&nGOnJF{UT82cd-$kkAzC`&>o4+lxjVfLRaeSqhbiirPbCW!O1?^V;u(6 zW*`DTsu-nL$ci6LO5~GFCPkiukt4ZL)f<`It4uIl#Y9poL)-SEG8Zk9vPX0pK#~)e zC>DV|3;+kwklLqr4{dg())M@uA<6>&#t?!L4@rGR`knK@{z(c-Oj^JDJZJq9nKTje zXnQWnNqI9i(0&oSN%yRBNR!nctsV3ibEEr@B`o55YG%1czO~YO_Brrld;G+$zC4hpy7!us}$}J^f zY7``p2h!XB88klpw`iYxCRp4bh9Dv=XGb^|UfF=aQeTk8W`RT+V%?z8-c>b?oo~70 zX|^>4loLAl5jss8^%Z*1*h??Hv>=D-D2BxQ`mYR$J83i2mk|{R#`{T4B5)81h{si@ zLKLa;g^~{Fvm;zv49NX{w=e|ZP1!uC1CL>Tts%4kG{t`8uqWtHgi>kb&DH&2ci(+? zu?5ES8hc`lK$iIlOuRR*h)tH%PFWOe7!Py^s@ev-ZPaUT zP#l2}h(?pOtr|P_u2Y6YV!MDIvN3k|SMK!!xFi8eSG(|eWX8GY z-X!65se}@$9LKg!Ld_ea`D{&$`bb`9JKI}F>fxM8c@kP(xPWphA3<;Fh$wDb3 zULTEVLsq_Fg56=0R_vUvDLL=i!=Xl;+j5s?SxR0^#)9oF>MflGj zsXNyJP8M7n=otDK#(FXfNOW8?IUA163ij9DTGTo@vfPv6lV{_x)DRE>2@C~d+@H02 zt;emMjc95hFm$N*-cAnIIk*T;*4sAXy+!MNYDn#x)lp~djl_UwLMlon^K5KNU?BI` zNywTJZLO^HYjK-vXstcH5IP#^vz+LPfMgvtfkGUgxoY9ceVH?wUpH?=pla_g0vK3| zVAPE0(}uJ)M0#3ESwEBiK0_t_4Q8?+6S6M-g148CFx4D}vNm$ikZIXaMq zrTfEp3P%hhm)VB7mT(%wrE#^#+*QPq za5SIRBKz^Iz*XHwYb&^x#1vnt5wWFU6D)x#5XpT9?dX)4%xCB*Ko0HtWJqAiAU4Je zlBk3I7=nQOY3U911NS2eqs^$oYQ0VHU9BQ*l~N)wnt?Pnh9HoXeqbU}?Q`D^Z-a~obpftQ7xbdhM`VOSuaNeGp$FTSfI$7#Z?>(1n2#kiyLk{?lc#g z0LXN69HdVO7cYu!6Lcc5Ijo0J^0ve@><%~Ih53SC^%&)YFrrU})YwfLqzf!G>IyTZ z8@RvH%p@8CNP{{AqA;4Y0W%e}ECxYHQM@z~#vj-2Su-SdK~akVMlkLoLSl$$7qe*> z$+s?UwfT(6gJV?PN8$+y!*89W**I|yWOoUf@pMj#vk+g(hvydU#B?pP)=vuwR{D~~ zALb^6q#r|QHmx;h7=rNTkPT=00awsNqO#r z0;JsY#zhaGLlF05J!!_kOiYd|9rxY1wUf{%Lwa+2$L03|(b;1g(+7I*eY(8^y`X`f oZC4x8UO$Q diff --git a/demo-email/src/test/java/com/xkcoding/email/SpringBootDemoEmailApplicationTests.java b/demo-email/src/test/java/com/xkcoding/email/SpringBootDemoEmailApplicationTests.java deleted file mode 100644 index 2b74a39..0000000 --- a/demo-email/src/test/java/com/xkcoding/email/SpringBootDemoEmailApplicationTests.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.xkcoding.email; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest -public class SpringBootDemoEmailApplicationTests { - - @Test - public void contextLoads() { - } - -} diff --git a/pom.xml b/pom.xml index c0fa61a..18fa06e 100644 --- a/pom.xml +++ b/pom.xml @@ -43,7 +43,6 @@ -