郵件服務是常用的服務之一,作用很多,對外可以給用戶發送活動、營銷廣告等;對內可以發送系統監控報告與告警。
本文將介紹Springboot如何整合郵件服務,并給出不同郵件服務商的整合配置。
如圖所示:
Springboot搭建
Springboot的搭建非常簡單,我們使用 https://start.spring.io/來構建,十分方便,選擇需要用到的模塊,就能快速完成項目的搭建:
引入依賴
為了使用郵件服務,我們需要引入相關的依賴,對于Springboot加入下面的依賴即可:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
配置文件
需要配置郵件服務提供商的相關參數,如服務地址、用戶名及密碼等。下面的例子是QQ的配置,其中密碼并不是QQ密碼,而是QQ授權碼,后續我們再講怎么獲得。
Springboot的配置文件application.yml如下:
server: port: 8080 spring: profiles: active: qq --- spring: profiles: qq mail: host: smtp.qq.com username: xxx@qq.com password: xxx properties: mail: smtp: auth: true starttls: enable: true required: true --- spring: profiles: netEase mail: host: smtp.163.com username: xxx@163.com password: xxx properties: mail: smtp: auth: true starttls: enable: true required: true
實現發送服務
將JavaMailSender注入,組裝Message后,就可以發送最簡單的文本郵件了。
@Autowired private JavaMailSender emailSender; public void sendNormalText(String from, String to, String subject, String text) { SimpleMailMessage message = new SimpleMailMessage(); message.setFrom(from); message.setTo(to); message.setSubject(subject); message.setText(text); emailSender.send(message); }
調用接口
服務調用實現后,通過Controller對外暴露REST接口,具體代碼如下:
@Value("${spring.mail.username}") private String username; @Autowired private MailService mailService; @GetMapping("/normalText") public Mono<String> sendNormalText() { mailService.sendNormalText(username, username, "Springboot Mail(Normal Text)", "This is a mail from Springboot!"); return Mono.just("sent"); }
把實現的MailService注入到Controller里,調用對應的方法即可。本次的郵件發送人和收件人都是同一個帳戶,實際實現可以靈活配置。
通過Postman調用接口來測試一下能不能正常發送:
成功返回"sent",并收到了郵件,測試通過。
簡單文本郵件
簡單文本郵件如何發送,剛剛已經講解,不再贅述。
HTML郵件
純文本雖然已經能滿足很多需求,但很多時候也需要更加豐富的樣式來提高郵件的表現力。這時HTML類型的郵件就非常有用。
Service代碼如下:
public void sendHtml(String from, String to, String subject, String text) throws MessagingException { MimeMessage message = emailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(text, true); emailSender.send(message); }
與簡單的文本不同的是,本次用到了MimeMessage和MimeMessageHelper,這是非常有用的類,后續我們經常會用到,組合使用能大大豐富郵件表現形式。
Controller的代碼如下:
@GetMapping("/html") public Mono<String> sendHtml() throws MessagingException { mailService.sendHtml(username, username, "Springboot Mail(HTML)", "<h1>This is a mail from Springboot!</h1>"); return Mono.just("sent"); }
帶附件郵件
郵件發送文件再正常不過,發送附件需要使用MimeMessageHelper.addAttachment(String attachmentFilename, InputStreamSource inputStreamSource)方法,第一個參數為附件名,第二參數為文件流資源。Service代碼如下:
public void sendAttachment(String from, String to, String subject, String text, String filePath) throws MessagingException { MimeMessage message = emailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(text, true); FileSystemResource file = new FileSystemResource(new File(filePath)); helper.addAttachment(filePath, file); emailSender.send(message); }
Controller代碼如下:
@GetMapping("/attachment") public Mono<String> sendAttachment() throws MessagingException { mailService.sendAttachment(username, username, "Springboot Mail(Attachment)", "<h1>Please check the attachment!</h1>", "/Pictures/postman.png"); return Mono.just("sent"); }
帶靜態資源郵件
我們訪問的網頁其實也是一個HTML,是可以帶很多靜態資源的,如圖片、視頻等。Service代碼如下:
public void sendStaticResource(String from, String to, String subject, String text, String filePath, String contentId) throws MessagingException { MimeMessage message = emailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(text, true); FileSystemResource file = new FileSystemResource(new File(filePath)); helper.addInline(contentId, file); emailSender.send(message); }
其中,contentId為HTML里靜態資源的ID,需要對應好。
Controller代碼如下:
@GetMapping("/inlinePicture") public Mono<String> sendStaticResource() throws MessagingException { mailService.sendStaticResource(username, username, "Springboot Mail(Static Resource)", "<html><body>With inline picture<img src='cid:picture' /></body></html>", "/Pictures/postman.png", "picture"); return Mono.just("sent"); }
模板郵件
Java的模板引擎很多,著名的有Freemarker、Thymeleaf、Velocity等,這不是本點的重點,所以只以Freemarker為例使用。
Service代碼如下:
@Autowired private FreeMarkerConfigurer freeMarkerConfigurer; public void sendTemplateFreemarker(String from, String to, String subject, Map<String, Object> model, String templateFile) throws Exception { MimeMessage message = emailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); Template template = freeMarkerConfigurer.getConfiguration().getTemplate(templateFile); String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model); helper.setText(html, true); emailSender.send(message); }
注意需要注入FreeMarkerConfigurer,然后使用FreeMarkerTemplateUtils解析模板,返回String,就可以作為內容發送了。
Controller代碼如下:
@GetMapping("/template") public Mono<String> sendTemplateFreemarker() throws Exception { Map<String, Object> model = new HashMap<>(); model.put("username", username); model.put("templateType", "Freemarker"); mailService.sendTemplateFreemarker(username, username, "Springboot Mail(Template)", model, "template.html"); return Mono.just("sent"); }
注意模板文件template.html要放在resources/templates/目錄下面,這樣才能找得到。
模板內容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>Hello ${username}</h1> <h1>This is a mail from Springboot using ${templateType}</h1> </body> </html>
其中${username}和${templateType}為需要替換的變量名,Freemarker提供了很多豐富的變量表達式,這里不展開講了。
郵件服務的提供商很多,國內最常用的應該是QQ郵箱和網易163郵箱了。
集成QQ郵件需要有必備的賬號,還要開通授權碼。開通授權碼后配置一下就可以使用了,官方的文檔如下:
https://service.mail.qq.com/cgi-bin/help?subtype=1&&no=1001256&&id=28
需要注意的是,開通授權碼是需要使用綁定的手機號發短信到特定號碼的,如果沒有綁定手機或者綁定手機不可用,那都會影響開通。
開通之后,授權碼就要以作為密碼配置到文件中。
163
網易的開通方式與QQ沒有太大差別,具體的指導可以看如下官方文檔:
http://help.mail.163.com/faqDetail.do?code=d7a5dc8471cd0c0e8b4b8f4f8e49998b374173cfe9171305fa1ce630d7f67ac2cda80145a1742516
同樣也是需要綁定手機進行操作。
本次例子發送后收到郵件如圖所示:
郵件功能強大,Springboot也非常容易整合。技術利器,善用而不濫用。
面完成了API服務(雖然這個API沒什么用)。接下去來個WEB服務,在前面項目中加上個頁面。這章目標是通過訪問一個URL展示一個界面,從服務端傳遞參數值到界面中展示動態數據。這里還會涉及到webjars的應用。
目錄與文件
在項目src/main/resources目錄下創建兩個目錄,分別是templates各static,templates 存放模板文件,static 存放靜態文件。
目錄
在static目錄放入一張圖片,圖片名稱為001.jpg,啟動項目。打開瀏覽器訪問http://localhost/001.jpg
訪問資源
spring boot會默認從static查找靜態資源,在這里還可以放css,js,html等文件,都可以直接訪問到。但是,這里的html文件只能是靜態頁面,服務端不能傳值到界面中。
templates中加入一個index.html,內容如下<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>這里是標題</title> </head> <body> <div> <p>這是首頁</p> </div> </body> </html>
重啟服務,瀏覽器中訪問http://localhost/index.html
404
找不到頁面,如果index.html放到static目錄中是可以訪問的。templates目錄中的文件是不能直接訪問。下面講到這么訪問templates中的文件。
當前目錄
目錄
使用模板
訪問templates文件,首先要引入模板引擎。這里使用thymeleaf,在pom.xml文件中包含thymeleaf組件。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
如圖
增加package:com.biboheart.demos.web,在package中創建class:PageController
package com.biboheart.demos.web; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class PageController { @RequestMapping(value = {"/", "/home"}) public String home() { return "index"; } }
@Controller表示這是一個SpringMVC的控制器
@RequestMapping(value = {"/", "/home"}) 表示訪問路徑"/"或"/home"都指向home函數,home返回"index"頁面,即templates/index.html模板生成的頁面。
重新啟動服務,在瀏覽器中訪問 http://localhost/home
home頁面
這時候,頁面展示的是index.html的內容。向頁面傳值
這里用Model對象傳值到模板中
調整controller的實現
package com.biboheart.demos.web; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class PageController { @RequestMapping(value = {"/", "/home"}) public String home(Model model, String name) { model.addAttribute("name", name); return "index"; } }
在函數中增加兩個參數,Model model用于向模板傳遞值,name用于接收請求參數。model.addAttribute("name", name);將接收到的值通過model傳遞到模板中。
模板文件index.html中接收并顯示name的值。
<!DOCTYPE html> <html lang="zh-CN" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>這里是標題</title> </head> <body> <div> <p>這是首頁</p> <p>歡迎你:<span th:text="${name}"></span></p> </div> </body> </html>
重新啟動服務,在瀏覽器中訪問http://localhost/home?name=biboheart
參數
index.html中的<span th:text="${name}"></span>,展示Model中的name的值。使用webjars
在pom.xml中包含webjars,并且包含jquery,bootstrap
<dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator</artifactId> <version>0.34</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.3.1</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>3.3.7-1</version> </dependency>
界面中使用bootstrap
<!DOCTYPE html> <html lang="zh-CN" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>這里是標題</title> <script src="/webjars/jquery/jquery.min.js"></script> <script src="/webjars/bootstrap/js/bootstrap.min.js"></script> <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.min.css"/> </head> <body> <div class="container"> <div class="jumbotron"> <h1>歡迎你:<span th:text="${name}"></span></h1> <p>這是首頁</p> </div> </div> </body> </html>
重新啟動項目,在瀏覽器中訪問http://localhost/home?name=biboheart
bootstrap效果
模板中包含靜態資源
將靜態資源001.jpg圖片在模板中顯示,修改后index.html文件如下
者 | 唐亞峰
責編 | 胡巍巍
Spring Boot 是為了簡化 Spring 應用的創建、運行、調試、部署等一系列問題而誕生的產物, 自動裝配的特性讓我們可以更好的關注業務本身而不是外部的 XML 配置,我們只需遵循規范,引入相關的依賴就可以輕易的搭建出一個 Web 工程。
未接觸 SpringBoot 之前,搭建一個普通的 Web 工程往往需要花費30分鐘左右,如果遇到點奇葩的問題耽擱的時間會更長一點,但自從用了 SpringBoot 后,真正體會到什么叫分分鐘搭建一個 Web,讓我擁有更多的時間跟我的小伙伴們嘮嗑了。
使用 SpringBoot 后發現一切是如此的簡單(還記得讀書那會被JAR包,XML 支配的恐懼嗎,如今都可以說 good bye)。
設計的目標
前提
SpringBoot 為我們提供了一系列的依賴包,所以需要構建工具的支持: Maven 或 Gradle。由于本人更習慣使用 Maven,所以后續案例都是基于 Maven 與 IntelliJ IDEA,同時這里是基于最新的 SpringBoot2 編寫的哦......
創建項目
初次接觸,我們先來看看如何創建一個 SpringBoot項目,這里以 IntelliJ IDEA為例,其他的IDE工具小伙伴們自行搜索創建方式。
創建完項目后,各位小伙伴請認真、細心的對比下與傳統的 Web 工程有何區別(如:目錄結構)。
點擊 File->Project
如果用過 Eclipse/IDEA 等工具的,對創建項目肯定不會陌生,但為了照顧第一次使用的我貼上了圖文。
選擇 Spring Initializr
到這一步選擇的時候,如圖中選項的是 Spring Initializr(官方的構建插件,需要聯網),第二個是自己選擇 Maven 構建,為了更好的適合初學者,我們將在本章用插件構建。
填寫項目基本信息
擇包
Spring Initializr 為我們提供了很多的選項,不同的選項有不同的作用,在初期我們只需要依賴 Web->Web就可以了,選擇好依賴包之后點擊 Next -> Finish。
目錄結果
src -main -java -package #主函數,啟動類,運行它如果運行了 Tomcat、Jetty、Undertow 等容器 -SpringbootApplication -resouces #存放靜態資源 js/css/images 等 - statics #存放 html 模板文件 - templates #主要的配置文件,SpringBoot啟動時候會自動加載application.yml/application.properties - application.yml #測試文件存放目錄 -test # pom.xml 文件是Maven構建的基礎,里面包含了我們所依賴JAR和Plugin的信息 - pom
pom.xml 依賴
因為使用了 Spring Initializr 插件,所以如下的配置都不需要我們自己去寫啦,需要注意的是版本要選擇 RELEASE ,穩定版本 Bug 少。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.battcn</groupId> <artifactId>chapter1</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>chapter1</name> <description>我的用第一個SpringBoot工程</description> <!--版本采用的是最新的 2.0.1.RELEASE TODO 開發中請記得版本一定要選擇 RELEASE 哦 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <!-- 默認就內嵌了Tomcat 容器,如需要更換容器也極其簡單--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 測試包,當我們使用 mvn package 的時候該包并不會被打入,因為它的生命周期只在 test 之內--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <!-- 編譯插件 --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
其它的依賴可以參考:官方文檔。
主函數入口
注意事項: 一個項目中切記不要出現多個 main 函數,否在在打包的時候 Spring-Boot-Maven-plugin 將找不到主函數(主動指定打包主函數入口除外......)。
/** * 我的第一個SpringBoot程序 * 其中 @RestController 等同于 (@Controller 與 @ResponseBody) * * @author Levin */ @RestController @SpringBootApplication public class Chapter1Application { public static void main(String[] args) { SpringApplication.run(Chapter1Application.class, args); } @GetMapping("/demo1") public String demo1() { return "Hello battcn"; } @Bean public CommandLineRunner commandLineRunner(ApplicationContext ctx) { // 目的是 return args -> { System.out.println("來看看 SpringBoot 默認為我們提供的 Bean:"); String[] beanNames = ctx.getBeanDefinitionNames(); Arrays.sort(beanNames); Arrays.stream(beanNames).forEach(System.out::println); }; } }
初窺配置文件
從啟動日志中可以發現, SpringBoot 默認的端口是 8080 ,那么如果端口被占用了怎么辦呢?不要慌,問題不大,配置文件分分鐘解決你的困擾......
2018-04-20 16:14:46.725 INFO 11184 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
修改默認配置。
# 默認的 8080 我們將它改成 9090 server.port=9090 # 未定義上下文路徑之前 地址是 http://localhost:8080 定義了后 http://localhost:9090 你能在tomcat做的事情,配置文件都可以 server.servlet.context-path=/chapter1
再啟動一次看看日志。
2018-04-20 16:47:05.716 INFO 12108 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9090 (http) with context path '/chapter1'
測試
本次測試采用Junit 進行,當然也可以啟動項目后直接訪問 http://localhost:9090/chapter/demo1 進行測試。
import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringRunner; import java.net.URL; import static org.junit.Assert.assertEquals; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class Chapter1ApplicationTests { @LocalServerPort private int port; private URL base; @Autowired private TestRestTemplate template; @Before public void setUp() throws Exception { // TODO 因為我們修改了 content-path 所以請求后面要帶上 this.base = new URL("http://localhost:" + port + "/chapter1/demo1"); } @Test public void demo1() throws Exception { ResponseEntity<String> response = template.getForEntity(base.toString(), String.class); assertEquals(response.getBody(), "Hello battcn"); } }
拓展知識
自定義Banner
SpringBoot 啟動的時候我們可以看到如下內容,這一塊其實是可以自定義的哦,而且在 2.X 版本中,它支持的格式從文本擴展到 banner.txt、banner.jpg、banner.gif、banner.jpeg 等等,只需要在 resouces 目錄下添加指定命名的文件即可。
. ____ _ __ _ _ /\ / ___'_ __ _ _(_)_ __ __ _ ( ( )___ | '_ | '_| | '_ / _` | \/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |___, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.0.1.RELEASE)
總結
目前很多大佬都寫過關于 SpringBoot 的教程了,如有雷同,請多多包涵,本教程基于最新的 Spring-Boot-Starter-parent:2.0.1.RELEASE編寫,包括新版本的特性都會一起介紹...
作者:唐亞峰 | battcn。分享技術、記錄生活、專注 Spring Boot、Spring Cloud、微服務等技術分享,歡迎一起交流探討。從零開始、以實戰落地為主,不定期分享干貨。漫漫架構之路,讓我們一起見證!
本文系作者投稿,版權歸作者所有。文章內容不代表CSDN立場。
“征稿啦”
CSDN 公眾號秉持著「與千萬技術人共成長」理念,不僅以「極客頭條」、「暢言」欄目在第一時間以技術人的獨特視角描述技術人關心的行業焦點事件,更有「技術頭條」專欄,深度解讀行業內的熱門技術與場景應用,讓所有的開發者緊跟技術潮流,保持警醒的技術嗅覺,對行業趨勢、技術有更為全面的認知。
如果你有優質的文章,或是行業熱點事件、技術趨勢的真知灼見,或是深度的應用實踐、場景方案等的新見解,歡迎聯系 CSDN 投稿,聯系方式:微信(guorui_1118,請備注投稿+姓名+公司職位),郵箱(guorui@csdn.net)。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。