重拾後端之Spring Boot(一):REST API的搭建可以這樣簡單
重拾後端之Spring Boot(二):MongoDb的無縫整合
重拾後端之Spring Boot(三):找回熟悉的Controller,Service
重拾後端之Spring Boot(四):使用 JWT 和 Spring Security 保護 REST API
重拾後端之 Spring Boot(五) -- 跨域、自定義查詢及分頁
重拾後端之Spring Boot(六) -- 熱載入、容器和多專案
這一章主要總結一下 Spring Boot 相關的環境配置和工具支援。
Spring Boot 的熱載入 (Live Load)
Spring Boot 內建提供了支援熱載入的功能,這個機制是通過 spring-dev-tools
來實現的。要實現這樣的功能,需要以下幾個步驟
第一步:在專案依賴中新增 spring-dev-tools
:在 build.gradle
中新增
dependencies {
compile("org.springframework.boot:spring-boot-devtools")
}複製程式碼
第二步:由於我們會連線到應用的一個自動裝載器上,所以需要提供一個共享金鑰:在 application.yml
或 application.properties
中新增
如果是 application.yml
的話,請按此填寫:
spring:
devtools:
remote:
secret: thisismysecret複製程式碼
如果是 application.properties
的話,請按此填寫:
# 如果是 application.properties 請按此填寫
spring.devtools.remote.secret=thisismysecret複製程式碼
第三步:在 Intellij IDEA 當中的 Preference
-> Build, Execution, Deployment
-> Compiler
中勾選 Build project automatically
在 IDEA 的 registry 中勾選 compiler.automake.allow.when.app.running
(macOS 下使用 option
+ command
+ shift
+ /
,Windows 下使用 Ctrl
+ Alt
+ Shift
+ /
調出 registry 選單)
然後尋找到 compiler.automake.allow.when.app.running
,進行勾選
最後重啟 IDE,在 Run/Debug Configurations
中新建一個 Spring Boot
模板的配置。其中 Main Class
填入 org.springframework.boot.devtools.RemoteSpringApplication
(注意哦,點右邊的省略號按鈕勾選 include non-project classes
的選擇才有效)。然後在 Program arguments
中填入服務地址,比如你的服務埠是 8090
就填 http://localhost:8090
; Working Directory
填寫 $MODULE_DIR$
,而 Use classpath of module
選擇當前專案即可。
遠端除錯
IDEA 提供了非常良好的遠端除錯支援,新增遠端除錯的話,可以去 Run/Debug Configurations
中新建一個 Remote
型別的配置,其中預設埠為 8000
,Transport 選擇 Socket
,Debug Mode
選擇 Attach
即可。這種遠端除錯可以支援在 Docker 容器中進行除錯,方便團隊的環境容器化。
容器支援
使用容器(Docker)來發布 Spring Boot 專案非常簡單。如果你採用 Gradle 構建的話,可以不用寫 Dockerfile ,直接在 build.gradle
中建立一個 buildDocker
的任務即可。當然要支援這樣的任務的話,我們需要首先在 buildscript
的 dependencies
中引入 se.transmode.gradle:gradle-docker
的類庫,然後應用 docker
外掛(apply plugin: 'docker'
)
buildscript {
// 省略其他部分
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath('se.transmode.gradle:gradle-docker:1.2')
}
}
apply plugin: 'docker'
// docker 的 group
group = 'wpcfan'
// 建立 docker image
task buildDocker(type: Docker, dependsOn: build) {
baseImage = 'frolvlad/alpine-oraclejdk8:slim' // 基於 jdk 的映象擴充
tag = 'wpcfan/taskmgr-backend' // 要推送到 docker hub 的『組名/專案名』
push = true
applicationName = jar.baseName
addFile {
from jar
rename {'app.jar'}
}
entryPoint([
'java',
'-Xdebug -Xrunjdwp:server=y,transport=dt_socket,suspend=n',
'-Dspring.data.mongodb.uri=mongodb://mongodb/taskmgr',
'-Djava.security.egd=file:/dev/./urandom',
'-jar',
'/app.jar'
])
exposePort(8090)
}複製程式碼
其中 entryPoint
中的幾個引數含義分別是
-Xdebug -Xrunjdwp:server=y,transport=dt_socket,suspend=n
是讓容器可以支援 IDEA 遠端除錯-Dspring.data.mongodb.uri=mongodb://mongodb/taskmgr
是指定 mongodb
的連線 URL,因為我們的 mongodb 也是容器,所以連線方式上需要使用『協議/主機名/資料庫』的方式。
兩個容器如果互相需要通訊的話,如果在不同主機,指定 IP 是可以的,但在同一個主機上怎麼破呢?這時候我們就需要利用 --link
指定要連線的容器(該選項後跟的是容器名稱),比如我們要連線 mongodb
容器,就寫成下面的樣子就行了。
docker run -p 80:8090 --name taskmgr-backend wpcfan/taskmgr-backend --link mongodb複製程式碼
多專案構建
在大型軟體開發中,一個專案解決所有問題顯然不可取的,因為存在太多的開發團隊共同協作,所以對專案進行拆分,形成多個子專案的形式是普遍存在的。而且一個子專案只專注自己的邏輯也易於維護和擴充,現在隨著容器和微服務的理念逐漸獲得大家的認可,多個子專案分別釋出到容器和形成多個微服務也逐漸成為趨勢。
我們的專案使用 Gradle 來處理多專案的構建,包括大專案和子專案的依賴管理以及容器的建立等。對於 Gradle 專案來說,我們會有一個根專案,這個根專案下會建立若干子專案,具體檔案結構如下:
|--spring-boot-tut (根專案)
|----common (共享子專案)
|------src (子專案原始碼目錄)
|--------main (子專案開發原始碼目錄)
|----------java(子專案開發 Java 類原始碼目錄)
|----------resources(子專案資源類原始碼目錄)
|------build.gradle (子專案 gradle 構建檔案)
|----api (API 子專案)
|------src
|--------main
|----------java
|----------resources
|------build.gradle
|----report (報表子專案)
|------src
|--------main
|----------java
|----------resources
|------build.gradle
|--build.gradle (根專案構建檔案)
|--settings.gradle (根專案設定檔案)複製程式碼
要讓 Gradle 支援多專案的話,首先需要把 settings.gradle
改成
include 'common'
include 'api'
include 'report'
rootProject.name = 'spring-boot-tut'複製程式碼
這樣 spring-boot-tut
就成為了根專案,而 common
、api
和 report
就是其之下的子專案。接下來,我們看一下根專案的 build.gradle
,對於多專案構建來說,根專案的 build.gradle
中應該儘可能的配置各子專案中共同的配置,從而讓子專案只配置自己不同的東西。
// 一個典型的根專案的構建檔案結構
buildscript {
/*
* 構建指令碼段落可以配置整個專案需要的外掛,構建過程中的依賴以及依賴類庫的版本號等
*/
}
allprojects {
/*
* 在這個段落中你可以宣告對於所有專案(含根專案)都適用的配置,比如依賴性的倉儲等
*/
}
subprojects {
/*
* 在這個段落中你可以宣告適用於各子專案的配置(不包括根專案哦)
*/
version = "0.0.1"
}
/*
* 對於子專案的特殊配置
*/
project(':common') {
}
project(':api') {
}
project(':report') {
}複製程式碼
其中,buildscript
段落用於配置 gradle
指令碼生成時需要的東西,比如配置整個專案需要的外掛,構建過程中的依賴以及在其他部分需要引用的依賴類庫的版本號等,就像下面這樣,我們在 ext
中定義了一些變數來集中配置了所有依賴的版本號,無論是根專案還是子專案都可以使用這些變數來指定版本號。這樣做的好處是當依賴的版本更新時,我們無需四處更改散落在各處的版本號。此外在這個段落中我們還提供了專案所需的第三方 Gradle 外掛所需的依賴:spring-boot-gradle-plugin
、gradle-docker
和 dependency-management-plugin
,這樣在後面,各子專案可以簡單的使用諸如 apply plugin: 'io.spring.dependency-management'
、 apply plugin: 'docker'
等即可。
buildscript {
ext {
springBootVersion = '1.5.4.RELEASE'
springCtxSupportVersion = '4.2.0.RELEASE'
lombokVersion = '1.16.16'
jjwtVersion = '0.7.0'
jasperVersion = '6.4.0'
poiVersion = '3.16'
itextVersion = '2.1.7'
olap4jVersion = '1.2.0'
gradleDockerVersion = '1.2'
gradleDMVersion = '1.0.3.RELEASE'
}
repositories {
jcenter()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("se.transmode.gradle:gradle-docker:${gradleDockerVersion}")
classpath("io.spring.gradle:dependency-management-plugin:${gradleDMVersion}")
}
}複製程式碼
allprojects
中可以宣告對於所有專案(含根專案)都適用的配置,比如依賴性的倉儲等。而 subprojects
和 allprojects
的區別在於 subprojecrts
只應用到子專案,而非根專案。所以大部分通用型配置可以通過 subprojects
和 allprojects
來完成。下面列出的樣例配置中,我們為所有的專案包括根專案配置了依賴倉儲以及軟體的 group
,同時為每個子專案配置了 java
和 idea
兩個外掛、版本號和通用的測試依賴。
allprojects {
group = 'spring-tut'
repositories() {
jcenter()
}
}
subprojects {
apply plugin: 'java'
apply plugin: 'idea'
version = "0.0.1"
dependencies {
testCompile("org.springframework.boot:spring-boot-starter-test")
}
}複製程式碼
除此之外呢,為了展示一下 project
的用法, 我們這個例子裡把每個子專案的依賴放到根 build.gradle
中的 project(':子專案名')
中列出,這樣做有好處也有缺點,好處是依賴性的管理統一在根 build.gradle
完成,對於依賴的情況一目瞭然。當然缺點是每個專案更改依賴時都會造成根 gradle
的更新,這樣的話如果一個專案有非常多的子專案時,會在協作上出現一些問題。所以請根據具體情況決定把依賴放到根 build.gradle
中的 project(':子專案名')
中還是放到各子專案的 build.gradle
中。
project(':common') {
dependencies {
compile("org.springframework.boot:spring-boot-starter-data-rest")
compile("org.springframework.boot:spring-boot-starter-data-mongodb")
compile("org.projectlombok:lombok:${lombokVersion}")
}
}
project(':api') {
dependencies {
compile project(':common')
compile("org.springframework.boot:spring-boot-devtools")
compile("org.springframework.boot:spring-boot-starter-security")
compile("io.jsonwebtoken:jjwt:${jjwtVersion}")
compile("org.projectlombok:lombok:${lombokVersion}")
}
}
project(':report') {
dependencies {
compile project(':common')
compile("org.springframework.boot:spring-boot-devtools")
// the following 5 are required by jasperreport rendering
compile files(["lib/simsun.jar"])
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework:spring-context-support:${springCtxSupportVersion}")
compile("net.sf.jasperreports:jasperreports:${jasperVersion}")
compile("com.lowagie:itext:${itextVersion}")
compile("org.apache.poi:poi:${poiVersion}")
compile("org.olap4j:olap4j:${olap4jVersion}")
}
}複製程式碼
Spring Boot 中如何構建類庫工程
Spring Boot 的一大優點就是把應用做成了一個 Fat Jar,這種方式在部署時有極大的優勢。但如何在 Spring Boot 的多專案構建中建立一個類庫工程,而不是應用工程呢?當然前提是我們還能繼續享受 Spring Boot 帶來的配置便利性。
首先,我們需要在根工程的 build.gradle
中新增一個 Spring Boot 依賴管理的外掛:
buildscript {
ext {
springBootVersion = '1.5.4.RELEASE'
gradleDMVersion = '1.0.3.RELEASE'
}
repositories {
jcenter()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("io.spring.gradle:dependency-management-plugin:${gradleDMVersion}")
}
}複製程式碼
然後在類庫子專案中的 build.gradle
中新增下面這句即可。
dependencyManagement {
imports { mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}") }
}複製程式碼