作者:泥瓦匠
本文目錄:
Spring 2.0
- Spring 2.0 是什么
- 開發(fā)環(huán)境和 IDE
- 使用 Spring Initializr 快速入門
Starter 組件
- Web:REST API & 模板引擎
- Data:JPA -> H2
- ...
生產(chǎn)指標(biāo)監(jiān)控 Actuator
內(nèi)嵌式容器 Tomcat / Jetty / Undertow
Spring 5 & Spring WebFlux
大家看到目錄,這么多內(nèi)容,簡直一本書的節(jié)奏。如果很詳細(xì),確實(shí)是。 可是只有一篇文章我大致講講每個(gè)點(diǎn),其是什么,其主要業(yè)界的使用場景是什么,然后具體會(huì)有對應(yīng)的博客教程。恩,下面我們聊聊 Spring 2.0。
一、Spring 2.0
spring.io 官網(wǎng)有句醒目的話是:
BUILD ANYTHING WITH SPRING BOOT
Java 程序員都知道 Spring 是什么,Spring 走過了這么多個(gè)年頭。Spring 是 Java 應(yīng)用程序平臺(tái)開發(fā)框架,肯定也是跨平臺(tái)的。同樣,它也是 Java EE 輕量級(jí)框架,為 Java 開發(fā)這提供了全面的基礎(chǔ)設(shè)施支持。
從 SSH(Strusts / Spring / Hibernate) 到 SSM(Spring MVC / Spring / Mabtis) ,到現(xiàn)在一個(gè) S (Spring)就夠了的年代。 可見 Spring 越來越重要了。那 Spring Boot 是什么?
1. Spring 2.0 是什么
先看看世界上最好的文檔來源自官方的《Spring Boot Reference Guide》,是這樣介紹的:
Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”... Most Spring Boot applications need very little Spring configuration.
Spring Boot(英文中是“引導(dǎo)”的意思),是用來簡化Spring應(yīng)用的搭建到開發(fā)的過程。應(yīng)用開箱即用,只要通過 “just run”(可能是 java -jar 或 tomcat 或 maven插件run 或 shell腳本),就可以啟動(dòng)項(xiàng)目。二者,Spring Boot 只要很少的Spring配置文件(例如那些xml,property)。
因?yàn)椤傲?xí)慣優(yōu)先于配置”的原則,使得Spring Boot在快速開發(fā)應(yīng)用和微服務(wù)架構(gòu)實(shí)踐中得到廣泛應(yīng)用。
Spring 目前是 2.0.0 M5 版本,馬上 2.0 release 了。如圖:Spring 2.0 架構(gòu)圖
最明顯的是 Reactor 模型。
插個(gè)小故事,曾經(jīng)有人問:Spring 這種阻塞的編程技術(shù)以及金字塔的結(jié)構(gòu)已經(jīng)開始被很多公司所擯棄?
我的回答自然是那個(gè)圖。Spring Boot 2.0 基于 Spring 5 Framework ,提供了 異步非阻塞 IO 的響應(yīng)式 Stream 、非堵塞的函數(shù)式 Reactive Web 框架 Spring WebFlux。本文最后我會(huì)詳細(xì)介紹的。
響應(yīng)式 Stream
http://www.reactive-streams.org/
webflux 官方文檔
https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#spring-webflux
2. 開發(fā)環(huán)境和 IDE
大致介紹了 Spring Boot 2.0 是什么,下面我們快速入門下 Spring Boot 2.0。常言道,磨刀不誤砍柴工砍柴工。在搭建一個(gè) Spring Boot 工程應(yīng)用前,需要配置好開發(fā)環(huán)境及安裝好開發(fā)工具:
JDK 1.8+
Spring Boot 2.x 要求 JDK 1.8 環(huán)境及以上版本。另外,Spring Boot 2.x 只兼容 Spring Framework 5.0 及以上版本。Maven 3.2+
為 Spring Boot 2.x 提供了相關(guān)依賴構(gòu)建工具是 Maven,版本需要 3.2 及以上版本。使用 Gradle 則需要 1.12 及以上版本。Maven 和 Gradle 大家各自挑選下喜歡的就好。IntelliJ IDEA
IntelliJ IDEA (簡稱 IDEA)是常用的開發(fā)工具,也是本書推薦使用的。同樣使用 Eclipse IDE 自然也是可以的。
這里額外介紹下,對于 Java 新手或者剛剛認(rèn)識(shí) Spring 的小伙伴。Spring Boot CLI 是一個(gè)學(xué)習(xí) Spring Boot 的很好的工具。Spring Boot CLI 是 Spring Boot Commad Line 的縮寫,是 Spring Boot 命令行工具。在 Spring Boot CLI 可以跑 Groovy 腳本,通過簡單的 Java 語法就可以快速而又簡單的學(xué)習(xí) Spring Boot 原型。
Spring Boot CLI 具體快速入門看下我寫的地址:https://www.bysocket.com/?p=1982
那最常用,最推薦的入門當(dāng)然是使用 Spring Initializr 。下面介紹下如何使用 Spring Initializr。
3. 使用 Spring Initializr 快速入門
打開 start.spring.io 進(jìn)行很快速的,Spring Boot 骨架工程生成吧。
Spring 官方提供了名為 Spring Initializr 的網(wǎng)站,去引導(dǎo)你快速生成 Spring Boot 應(yīng)用。網(wǎng)站地址為:https://start.spring.io,操作步驟如下:
第一步,選擇 Maven 或者 Gradle 構(gòu)建工具,開發(fā)語言 Java 、Kotlin 或者 Groovy,最后確定 Spring Boot 版本號(hào)。這里默認(rèn)選擇 Maven 構(gòu)建工具、Java 開發(fā)語言和 Spring Boot 2.0.0。
第二步,輸入 Maven 工程信息,即項(xiàng)目組 groupId 和名字 artifactId。這里對應(yīng) Maven 信息為:
- groupId:demo.springboot
- artifactId:spring-boot-quickstart
這里默認(rèn)版本號(hào) version 為 0.0.1-SNAPSHOT 。三個(gè)屬性在 Maven 依賴倉庫是唯一標(biāo)識(shí)的。
第三步,選擇工程需要的 Starter 組件和其他依賴。最后點(diǎn)擊生成按鈕,即可獲得骨架工程壓縮包。如圖 :
在對應(yīng)的 *Application 增加對應(yīng)的代碼如下:(這來自是官方 demo)
@Controller
@EnableAutoConfiguration
public class Application {
@RequestMapping("/")
@ResponseBody
String home() {
return "Hello World!";
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
在 IDEA 中直接執(zhí)行應(yīng)用啟動(dòng)類,來運(yùn)行 Spring Boot 應(yīng)用,會(huì)得到下面成功的控制臺(tái)輸出:
... 省略
2017-10-15 10:05:19.994 INFO 17963 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http)
2017-10-15 10:05:20.000 INFO 17963 --- [ main] demo.springboot.QuickStartApplication : Started QuickStartApplication in 5.544 seconds (JVM running for 6.802)
訪問地址 localhost:8080 ,成功返回 "Hello World!" 的字符串。
這就是 Spring Boot 使用,是不是很方便。介紹下工程的目錄結(jié)構(gòu):
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── demo
│ │ └── springboot
│ │ └── Application.java
│ └── resources
│ ├── application.properties
│ ├── static
│ └── templates
└── test
└── java
└── demo
└── springboot
└── QuickstartApplication Tests.java
這是默認(rèn)的工程結(jié)構(gòu),java 目錄中是編寫代碼的源目錄,比如三層模型大致會(huì)新建三個(gè)包目錄,web 包負(fù)責(zé) web 服務(wù),service 包負(fù)責(zé)業(yè)務(wù)邏輯,domain 包數(shù)據(jù)源服務(wù)。對應(yīng) java 目錄的是 test 目錄,編寫單元測試的目錄。
resources 目錄會(huì)有 application.properties 應(yīng)用配置文件,還有默認(rèn)生成的 static 和 templates 目錄,static 用于存放靜態(tài)資源文件,templates 用于存放模板文件。可以在 application.properties 中自定義配置資源和模板目錄
二、Starter 組件
什么是 Starter 組件?
Starter 組件是可被加載在應(yīng)用中的 Maven 依賴項(xiàng)。Spring Boot 提供了很多 “開箱即用” 的 Starter 組件。只需要在 Maven 配置中添加對應(yīng)的依賴配置,即可使用對應(yīng)的 Starter 組件。例如,添加 spring-boot-starter-web 依賴,就可用于構(gòu)建 RESTful Web 服務(wù),其包含了 Spring MVC 和 Tomcat 內(nèi)嵌容器等。
開發(fā)中,很多功能是通過添加 Starter 組件的方式來進(jìn)行實(shí)現(xiàn)。下面都是常用的組件,還有很多事務(wù)、消息、安全、監(jiān)控、大數(shù)據(jù)等支持,這里就不介紹了。大家可以同理可得去學(xué)習(xí)哈。
1. Web:REST API & 模板引擎
在 Web 開發(fā)中,常見的場景有傳統(tǒng)的 Web MVC 架構(gòu)和前后端分離架構(gòu)。
Web MVC 架構(gòu)
Web MVC 模式很適合 Spring Boot 來開發(fā),View 使用 JSP 或者其他模板引擎(默認(rèn)支持:FreeMarker 、 Groovy 、 Thymeleaf 、 Mustache)。
傳統(tǒng)模式比如獲取用戶,是從用戶 view 層發(fā)送獲取用戶請求到 Spring Boot 提供的用戶控制層,然后獲取數(shù)據(jù)封裝進(jìn) Model,最后將 model 返回到 View。因?yàn)?Spring Boot 基于 Spring ,所以 Spring 能做的,Spring Boot 就能做,而且更加方便,更近快速。
前后端分離架構(gòu)
前后端分離架構(gòu),免不了的是 API 文檔作為中間的橋梁。Spring Boot 很方便的開發(fā) REST API,前端通過調(diào)用 REST API 獲取數(shù)據(jù)。數(shù)據(jù)形式可能是 JSON 或者 XML 等。然后進(jìn)行視圖渲染,這里前端也有對應(yīng)的前端模板引擎。其實(shí) H5 ,PC,APP 都可采取類似的方式實(shí)現(xiàn)。這里的 API 文檔可以使用 Swagger2 或者 APIDOC 來實(shí)現(xiàn)。
那具體聊聊 REST API
RESTful是什么?RESTful(Representational State Transfer)架構(gòu)風(fēng)格,是一個(gè)Web自身的架構(gòu)風(fēng)格,底層主要基于HTTP協(xié)議(ps:提出者就是HTTP協(xié)議的作者),是分布式應(yīng)用架構(gòu)的偉大實(shí)踐理論。RESTful架構(gòu)是無狀態(tài)的,表現(xiàn)為請求-響應(yīng)的形式,有別于基于Bower的SessionId不同。Spring Boot 的注解 @RestController 支持實(shí)現(xiàn) RESTful 控制層。
那有個(gè)問題?權(quán)限怎么控制?
RESTful是無狀態(tài)的,所以每次請求就需要對起進(jìn)行認(rèn)證和授權(quán)。
認(rèn)證
身份認(rèn)證,即登錄驗(yàn)證用戶是否擁有相應(yīng)的身份。簡單的說就是一個(gè)Web頁面點(diǎn)擊登錄后,服務(wù)端進(jìn)行用戶密碼的校驗(yàn)。權(quán)限驗(yàn)證(授權(quán))
也可以說成授權(quán),就是在身份認(rèn)證后,驗(yàn)證該身份具體擁有某種權(quán)限。即針對于某種資源的CRUD,不同用戶的操作權(quán)限是不同的。 一般簡單項(xiàng)目:做個(gè)sign(加密加鹽參數(shù))+ 針對用戶的access_token 復(fù)雜的話,加入 SLL ,并使用OAuth2進(jìn)行對token的安全傳輸。
再聊聊模板引擎
有人在我博客上評(píng)論 模板語言 現(xiàn)在沒人用了吧。我們還是先了解下,什么是模板語言再說吧。常見的模板語言都包含以下幾個(gè)概念:數(shù)據(jù)(Data)、模板(Template)、模板引擎(Template Engine)和結(jié)果文檔(Result Documents)。
數(shù)據(jù)
數(shù)據(jù)是信息的表現(xiàn)形式和載體,可以是符號(hào)、文字、數(shù)字、語音、圖像、視頻等。數(shù)據(jù)和信息是不可分離的,數(shù)據(jù)是信息的表達(dá),信息是數(shù)據(jù)的內(nèi)涵。數(shù)據(jù)本身沒有意義,數(shù)據(jù)只有對實(shí)體行為產(chǎn)生影響時(shí)才成為信息。模板
模板,是一個(gè)藍(lán)圖,即一個(gè)與類型無關(guān)的類。編譯器在使用模板時(shí),會(huì)根據(jù)模板實(shí)參對模板進(jìn)行實(shí)例化,得到一個(gè)與類型相關(guān)的類。模板引擎
模板引擎(這里特指用于Web開發(fā)的模板引擎)是為了使用戶界面與業(yè)務(wù)數(shù)據(jù)(內(nèi)容)分離而產(chǎn)生的,它可以生成特定格式的文檔,用于網(wǎng)站的模板引擎就會(huì)生成一個(gè)標(biāo)準(zhǔn)的HTML文檔。結(jié)果文檔
一種特定格式的文檔,比如用于網(wǎng)站的模板引擎就會(huì)生成一個(gè)標(biāo)準(zhǔn)的HTML文檔。
模板語言用途廣泛,常見的用途如下:
- 頁面渲染
- 文檔生成
- 代碼生成
- 所有 “數(shù)據(jù)+模板=文本” 的應(yīng)用場景
所以大家看到這個(gè)用途,應(yīng)該不會(huì)說沒有用了吧。具體按模板語言 Thymeleaf 為例,使用如下
pom.xml Thymeleaf 依賴
使用模板引擎,就在 pom.xml 加入 Thymeleaf 組件依賴:
<!-- 模板引擎 Thymeleaf 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Thymeleaf 依賴配置
在 Spring Boot 項(xiàng)目中加入 Thymeleaf 依賴,即可啟動(dòng)其默認(rèn)配置。如果想要自定義配置,可以在 application.properties 配置如下
spring.thymeleaf.cache=true # Enable template caching.
spring.thymeleaf.check-template=true # Check that the template exists before rendering it.
spring.thymeleaf.check-template-location=true # Check that the templates location exists.
spring.thymeleaf.enabled=true # Enable Thymeleaf view resolution for Web frameworks.
spring.thymeleaf.encoding=UTF-8 # Template files encoding.
spring.thymeleaf.excluded-view-names= # Comma-separated list of view names that should be excluded from resolution.
spring.thymeleaf.mode=HTML5 # Template mode to be applied to templates. See also StandardTemplateModeHandlers.
spring.thymeleaf.prefix=classpath:/templates/ # Prefix that gets prepended to view names when building a URL.
spring.thymeleaf.reactive.max-chunk-size= # Maximum size of data buffers used for writing to the response, in bytes.
spring.thymeleaf.reactive.media-types= # Media types supported by the view technology.
spring.thymeleaf.servlet.content-type=text/html # Content-Type value written to HTTP responses.
spring.thymeleaf.suffix=.html # Suffix that gets appended to view names when building a URL.
spring.thymeleaf.template-resolver-order= # Order of the template resolver in the chain.
spring.thymeleaf.view-names= # Comma-separated list of view names that can be resolved.
Controller 的使用方式,和以前的 Spring 方式一致,具體的Tymeleaf 的語法糖,大家可以看看官方文檔http://www.thymeleaf.org/documentation.html。本案例具體整合教程:https://www.bysocket.com/?p=1973。
2. Data:JPA 、Mybatis
Data,顧名思義是數(shù)據(jù)。數(shù)據(jù)存儲(chǔ)有 SQL 和 NoSQL:
- SQL:MySQL 、H2 等
- NoSQL:Redis、MongoDB、Cassandra、Elasticsearch 等
自然互聯(lián)網(wǎng)常見使用的 SQL 關(guān)系型數(shù)據(jù)庫是 MySQL。ORM 框架流行的有 Hibernate 和 Mybatis 等,JPA 底層實(shí)現(xiàn)使用 Hibernate。下面分別介紹下兩者的使用方式
Spring Data JPA 依賴 spring-boot-starter-data-jpa
Spring Data JPA 是 Spring Data 的子項(xiàng)目,用于簡化數(shù)據(jù)訪問層的實(shí)現(xiàn),可以方便的實(shí)現(xiàn)增刪改查、分頁、排序。還有很多常見的其他依賴:Spring Data MongoDB、Spring Data Redis 等。這里 Spring Boot 也有對應(yīng)的 Starter 組件,名為 spring-boot-starter-data-jpa。
具體整合教程:https://www.bysocket.com/?p=1950
Spring Boot Mybatis 依賴 mybatis-spring-boot-starter
Mybatis 也是業(yè)界互聯(lián)網(wǎng)流行的數(shù)據(jù)操作層框架。有兩種形式進(jìn)行使用 Mybatis。第一、純 Annotation,第二、使用 xml 配置 SQL。個(gè)人推薦使用 xml 配置 SQL, SQL 和業(yè)務(wù)代碼應(yīng)該隔離,方便和 DBA 校對 SQL。二者 XML 對較長的 SQL 比較清晰。
雖然 XML 形式是我比較推薦的,但是注解形式也是方便的。尤其一些小系統(tǒng),快速的 CRUD 輕量級(jí)的系統(tǒng)。這里可見,mybatis-spring-boot-starter依賴是非官方提供的。
Springboot 整合 Mybatis 的完整 Web 案例教程:https://www.bysocket.com/?p=1610
Spring Boot 整合 Mybatis Annotation 注解的完整 Web 案例教程:https://www.bysocket.com/?p=1811
另外,搜索常用 ES,spring-data-elasticsearch 是 Spring Data 的 Community modules 之一,是 Spring Data 對 Elasticsearch 引擎的實(shí)現(xiàn)。 Elasticsearch 默認(rèn)提供輕量級(jí)的 HTTP Restful 接口形式的訪問。相對來說,使用 HTTP Client 調(diào)用也很簡單。
但 spring-data-elasticsearch 可以更快的支持構(gòu)建在 Spring 應(yīng)用上,比如在 application.properties 配置 ES 節(jié)點(diǎn)信息和 spring-boot-starter-data-elasticsearch 依賴,直接在 Spring Boot 應(yīng)用上使用。
我也寫了點(diǎn)系列博客在:https://www.bysocket.com/?tag=elasticsearch
還有很多組件無法一一介紹了,比如常用的 Redis 做緩存操作等。
三、生產(chǎn)指標(biāo)監(jiān)控 Actuator
Starter 組件 Actuator 提供了生產(chǎn)級(jí)監(jiān)控的特性,可以使用 Actuator 來監(jiān)控應(yīng)用的一切。使用方式也很簡單,加入對應(yīng)的依賴,然后訪問各個(gè) HTTP 請求就可以獲取需要的信息。具體提供的信息有:
autoconfig 自動(dòng)配置相關(guān)信息
beans Spring 容器中的 Bean 相關(guān)信息
configprops 配置項(xiàng)信息
dump 當(dāng)前線程信息
env 當(dāng)前環(huán)境變量信息
health 應(yīng)用的健康信息
info 應(yīng)用基本信息
metrics 性能指標(biāo)信息
mappings 請求路徑信息
trace 請求調(diào)用信息
至于可視化的話,可以使用 http/ssh/telnet 等方式拉監(jiān)控?cái)?shù)據(jù)到可視化 UI 上即可。進(jìn)一步可以使用 prometheus 采集 springboot 應(yīng)用指標(biāo),用 Grafana 可視化監(jiān)控?cái)?shù)據(jù)。
這里我?guī)煹軐懸槐榻坛滩诲e(cuò):http://www.itdecent.cn/p/7ecb57a3f326
四、內(nèi)嵌式容器 Tomcat / Jetty / Undertow
Spring Boot 運(yùn)行的應(yīng)用是獨(dú)立的一個(gè) Jar 應(yīng)用,實(shí)際上在運(yùn)行時(shí)啟動(dòng)了應(yīng)用內(nèi)部的內(nèi)嵌容器,容器初始化 Spring 環(huán)境及其組件并啟動(dòng)應(yīng)用。但我們也可以自定義配置 內(nèi)嵌式容器,比如將 Tomcat 換成 Jetty。Spring Boot 能這樣工作,主要靠下面兩點(diǎn):自動(dòng)配置和外化配置。
自動(dòng)配置
Spring Boot 在不需要任何配置情況下,就直接可以運(yùn)行一個(gè)應(yīng)用。實(shí)際上,Spring Boot 框架的 spring-boot-autoconfigure 依賴做了很多默認(rèn)的配置項(xiàng),即應(yīng)用默認(rèn)值。這種模式叫做 “自動(dòng)配置”。Spring Boot 自動(dòng)配置會(huì)根據(jù)添加的依賴,自動(dòng)加載依賴相關(guān)的配置屬性并啟動(dòng)依賴。例如,默認(rèn)用的內(nèi)嵌式容器是 Tomcat ,端口默認(rèn)設(shè)置為 8080。
外化配置
Spring Boot 簡化了配置,在 application.properties 文件配置常用的應(yīng)用屬性。Spring Boot 可以將配置外部化,這種模式叫做 “外化配置”。將配置從代碼中分離外置,最明顯的作用是只要簡單地修改下外化配置文件,就可以在不同環(huán)境中,可以運(yùn)行相同的應(yīng)用代碼。
所以,Spring Boot 啟動(dòng)應(yīng)用,默認(rèn)情況下是自動(dòng)啟動(dòng)了內(nèi)嵌容器 Tomcat,并且自動(dòng)設(shè)置了默認(rèn)端口為 8080。另外還提供了對 Jetty、Undertow 等容器的支持。開發(fā)者自行在添加對應(yīng)的容器 Starter 組件依賴,即可配置并使用對應(yīng)內(nèi)嵌容器實(shí)例。具體操作如下:
第一步,將 tomcat 依賴排除:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
spring-boot-starter-web 依賴默認(rèn)使用 Tomcat 作為內(nèi)嵌容器
第二步,加入對應(yīng)的 jetty 依賴(這里也可以是 Undertow 依賴)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
上面大致介紹了 Spring Boot 2.0 有的那些特性,也就是那些事,最后入門介紹下 WebFlux。
五、Spring 5 & Spring WebFlux
最后,Spring Boot 2.0 支持 Spring 5,Spring 5 具有了強(qiáng)大的特性:WebFlux/Reactor。所以最后來聊聊 WebFlux ,就算入個(gè)門吧。
對照下 Spring Web MVC ,Spring Web MVC 是基于 Servlet API 和 Servlet 容器設(shè)計(jì)的。那么 Spring WebFlux 肯定不是基于前面兩者,它基于 Reactive Streams API 和 Servlet 3.1+ 容器設(shè)計(jì)。
那 Reactive Streams API 是什么?
先理解 Stream 流是什么?流是序列,是生產(chǎn)者生產(chǎn),一個(gè)或多個(gè)消費(fèi)者消費(fèi)的元素序列。這種具體的設(shè)計(jì)模式成為發(fā)布訂閱模式。常見的流處理機(jī)制是 pull / push 模式。背壓是一種常用策略,使得發(fā)布者擁有無限制的緩沖區(qū)存儲(chǔ) item,用于確保發(fā)布者發(fā)布 item 太快時(shí),不會(huì)去壓制訂閱者。
Reactive Streams (響應(yīng)式流)是提供處理非阻塞背壓異步流的一種標(biāo)準(zhǔn)。主要針對的場景是運(yùn)行時(shí)環(huán)境(包括 JVM 和 JS)和網(wǎng)絡(luò)。同樣,JDK 9 java.util.concurrent 包提供了兩個(gè)主要的 API 來處理響應(yīng)流:
- Flow
- SubmissionPublisher<T>
為啥只能運(yùn)行在 Servlet 3.1+ 容器?
大家知道,3.1 規(guī)范其中一個(gè)新特性是異步處理支持。
異步處理支持:Servlet 線程不需一直阻塞,即不需要到業(yè)務(wù)處理完畢再輸出響應(yīng),然后結(jié)束 Servlet線程。異步處理的作用是在接收到請求之后,Servlet 線程可以將耗時(shí)的操作委派給另一個(gè)線程來完成,在不生成響應(yīng)的情況下返回至容器。主要應(yīng)用場景是針對業(yè)務(wù)處理較耗時(shí)的情況,可以減少服務(wù)器資源的占用,并且提高并發(fā)處理速度。
所以 WebFlux 支持的容器有 Tomcat、Jetty(Non-Blocking IO API) ,也可以像 Netty 和 Undertow 的本身就支持異步容器。在容器中 Spring WebFlux 會(huì)將輸入流適配成 Mono 或者 Flux 格式進(jìn)行統(tǒng)一處理。
Spring WebFlux 是什么
先看這張圖,上面我們了解了容器、響應(yīng)流。這里介紹下 Spring WebFlux 是什么? Spring WebFlux 是 Spring 5 的一個(gè)新模塊,包含了響應(yīng)式 HTTP 和 WebSocket 的支持,另外在上層服務(wù)端支持兩種不同的編程模型:
- 基于 Spring MVC 注解 @Controller 等
- 基于 Functional 函數(shù)式路由
下面是兩個(gè)實(shí)現(xiàn)小案例,首先在 pom.xml 加入對應(yīng)的依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
基于 Spring MVC 注解 RESTful API
官方案例很簡單,如下:
@RestController
public class PersonController {
private final PersonRepository repository;
public PersonController(PersonRepository repository) {
this.repository = repository;
}
@PostMapping("/person")
Mono<Void> create(@RequestBody Publisher<Person> personStream) {
return this.repository.save(personStream).then();
}
@GetMapping("/person")
Flux<Person> list() {
return this.repository.findAll();
}
@GetMapping("/person/{id}")
Mono<Person> findById(@PathVariable String id) {
return this.repository.findOne(id);
}
}
但是 PersonRepository 這種 Spring Data Reactive Repositories 不支持 MySQL,進(jìn)一步也不支持 MySQL 事務(wù)。所以用了 Reactivey 原來的 spring 事務(wù)管理就不好用了。jdbc jpa 的事務(wù)是基于阻塞 IO 模型的,如果 Spring Data Reactive 沒有升級(jí) IO 模型去支持 JDBC,生產(chǎn)上的應(yīng)用只能使用不強(qiáng)依賴事務(wù)的。也可以使用透明的事務(wù)管理,即每次操作的時(shí)候以回調(diào)形式去傳遞數(shù)據(jù)庫連接 connection。
Spring Data Reactive Repositories 目前支持 Mongo、Cassandra、Redis、Couchbase 。
如果應(yīng)用只能使用不強(qiáng)依賴數(shù)據(jù)事務(wù),依舊使用 MySQL ,可以使用下面的實(shí)現(xiàn),代碼如下:
@RestController
@RequestMapping(value = "/city")
public class CityRestController {
@Autowired
private CityService cityService;
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Mono<City> findOneCity(@PathVariable("id") Long id) {
return Mono.create(cityMonoSink -> cityMonoSink.success(cityService.findCityById(id)));
}
@RequestMapping(method = RequestMethod.GET)
public Flux<City> findAllCity() {
return Flux.create(cityFluxSink -> {
cityService.findAllCity().forEach(city -> {
cityFluxSink.next(city);
});
cityFluxSink.complete();
});
}
@RequestMapping(method = RequestMethod.POST)
public Mono<Long> createCity(@RequestBody City city) {
return Mono.create(cityMonoSink -> cityMonoSink.success(cityService.saveCity(city)));
}
@RequestMapping(method = RequestMethod.PUT)
public Mono<Long> modifyCity(@RequestBody City city) {
return Mono.create(cityMonoSink -> cityMonoSink.success(cityService.updateCity(city)));
}
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public Mono<Long> modifyCity(@PathVariable("id") Long id) {
return Mono.create(cityMonoSink -> cityMonoSink.success(cityService.deleteCity(id)));
}
}
findAllCity 方法中,利用 Flux.create 方法對響應(yīng)進(jìn)行創(chuàng)建封裝成 Flux 數(shù)據(jù)。并且使用 lambda 寫數(shù)據(jù)流的處理函數(shù)會(huì)十分的方便。
Service 層依舊是以前那套邏輯,業(yè)務(wù)服務(wù)層接口如下:
public interface CityService {
/**
* 獲取城市信息列表
*
* @return
*/
List<City> findAllCity();
/**
* 根據(jù)城市 ID,查詢城市信息
*
* @param id
* @return
*/
City findCityById(Long id);
/**
* 新增城市信息
*
* @param city
* @return
*/
Long saveCity(City city);
/**
* 更新城市信息
*
* @param city
* @return
*/
Long updateCity(City city);
/**
* 根據(jù)城市 ID,刪除城市信息
*
* @param id
* @return
*/
Long deleteCity(Long id);
}
具體案例在我的 Github:https://github.com/JeffLi1993/springboot-learning-example
基于 Functional 函數(shù)式路由實(shí)現(xiàn) RESTful API
創(chuàng)建一個(gè) Route 類來定義 RESTful HTTP 路由:
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
@Configuration
public class Routes {
private CityService cityService;
public Routes(CityService cityService) {
this.cityService = cityService;
}
@Bean
public RouterFunction<?> routerFunction() {
return route(
GET("/api/city").and(accept(MediaType.APPLICATION_JSON)), cityService:: findAllCity).and(route(
GET("/api/user/{id}").and(accept(MediaType.APPLICATION_JSON)), cityService:: findCityById)
);
}
}
RoouterFunction 類似 Spring Web MVC 的 @RequestMapping ,用來定義路由信息,每個(gè)路由會(huì)映射到一個(gè)處理方法,當(dāng)接受 HTTP 請求時(shí)候會(huì)調(diào)用該處理方法。
創(chuàng)建 HttpServerConfig 自定義 Http Server,這里創(chuàng)建一個(gè) Netty HTTP 服務(wù)器:
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import reactor.ipc.netty.http.server.HttpServer;
@Configuration
public class HttpServerConfig {
@Autowired
private Environment environment;
@Bean
public HttpServer httpServer(RouterFunction<?> routerFunction) {
HttpHandler httpHandler = RouterFunctions.toHttpHandler(routerFunction);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
HttpServer server = HttpServer.create("localhost", Integer.valueOf(environment.getProperty("server.port")));
server.newHandler(adapter);
return server;
}
}
自然推薦 Netty 來運(yùn)行 Reactive 應(yīng)用,因?yàn)?Netty 是基于異步和事件驅(qū)動(dòng)的。
從案例中,大家也簡單入門了解 WebFlux