重拾后端之Spring Boot(一):REST API的搭建可以這樣簡(jiǎn)單
重拾后端之Spring Boot(二):MongoDb的無(wú)縫集成
重拾后端之Spring Boot(三):找回熟悉的Controller,Service
重拾后端之Spring Boot(四):使用 JWT 和 Spring Security 保護(hù) REST API
重拾后端之Spring Boot(五):跨域、自定義查詢及分頁(yè)
重拾后端之Spring Boot(六):熱加載、容器和多項(xiàng)目
這一章主要總結(jié)一下 Spring Boot 相關(guān)的環(huán)境配置和工具支持。
Spring Boot 的熱加載 (Live Load)
Spring Boot 內(nèi)建提供了支持熱加載的功能,這個(gè)機(jī)制是通過(guò) spring-dev-tools 來(lái)實(shí)現(xiàn)的。要實(shí)現(xiàn)這樣的功能,需要以下幾個(gè)步驟
第一步:在項(xiàng)目依賴中添加 spring-dev-tools:在 build.gradle 中添加
dependencies {
compile("org.springframework.boot:spring-boot-devtools")
}
第二步:由于我們會(huì)連接到應(yīng)用的一個(gè)自動(dòng)裝載器上,所以需要提供一個(gè)共享密鑰:在 application.yml 或 application.properties 中添加
如果是 application.yml 的話,請(qǐng)按此填寫:
spring:
devtools:
remote:
secret: thisismysecret
如果是 application.properties 的話,請(qǐng)按此填寫:
# 如果是 application.properties 請(qǐng)按此填寫
spring.devtools.remote.secret=thisismysecret
第三步:在 Intellij IDEA 當(dāng)中的 Preference -> Build, Execution, Deployment -> Compiler 中勾選 Build project automatically

在 IDEA 的 registry 中勾選 compiler.automake.allow.when.app.running (macOS 下使用 option + command + shift + / ,Windows 下使用 Ctrl + Alt + Shift + / 調(diào)出 registry 菜單)

然后尋找到 compiler.automake.allow.when.app.running,進(jìn)行勾選

最后重啟 IDE,在 Run/Debug Configurations 中新建一個(gè) Spring Boot 模板的配置。其中 Main Class 填入 org.springframework.boot.devtools.RemoteSpringApplication (注意哦,點(diǎn)右邊的省略號(hào)按鈕勾選 include non-project classes 的選擇才有效)。然后在 Program arguments 中填入服務(wù)地址,比如你的服務(wù)端口是 8090 就填 http://localhost:8090; Working Directory 填寫 $MODULE_DIR$,而 Use classpath of module 選擇當(dāng)前項(xiàng)目即可。

遠(yuǎn)程調(diào)試
IDEA 提供了非常良好的遠(yuǎn)程調(diào)試支持,添加遠(yuǎn)程調(diào)試的話,可以去 Run/Debug Configurations 中新建一個(gè) Remote 類型的配置,其中默認(rèn)端口為 8000,Transport 選擇 Socket,Debug Mode 選擇 Attach 即可。這種遠(yuǎn)程調(diào)試可以支持在 Docker 容器中進(jìn)行調(diào)試,方便團(tuán)隊(duì)的環(huán)境容器化。

容器支持
使用容器(Docker)來(lái)發(fā)布 Spring Boot 項(xiàng)目非常簡(jiǎn)單。如果你采用 Gradle 構(gòu)建的話,可以不用寫 Dockerfile ,直接在 build.gradle 中建立一個(gè) buildDocker 的任務(wù)即可。當(dāng)然要支持這樣的任務(wù)的話,我們需要首先在 buildscript 的 dependencies 中引入 se.transmode.gradle:gradle-docker 的類庫(kù),然后應(yīng)用 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 的『組名/項(xiàng)目名』
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 中的幾個(gè)參數(shù)含義分別是
-Xdebug -Xrunjdwp:server=y,transport=dt_socket,suspend=n 是讓容器可以支持 IDEA 遠(yuǎn)程調(diào)試
-Dspring.data.mongodb.uri=mongodb://mongodb/taskmgr 是指定 mongodb 的連接 URL,因?yàn)槲覀兊?mongodb 也是容器,所以連接方式上需要使用『協(xié)議/主機(jī)名/數(shù)據(jù)庫(kù)』的方式。
兩個(gè)容器如果互相需要通信的話,如果在不同主機(jī),指定 IP 是可以的,但在同一個(gè)主機(jī)上怎么破呢?這時(shí)候我們就需要利用 --link 指定要連接的容器(該選項(xiàng)后跟的是容器名稱),比如我們要連接 mongodb 容器,就寫成下面的樣子就行了。
docker run -p 80:8090 --name taskmgr-backend wpcfan/taskmgr-backend --link mongodb
多項(xiàng)目構(gòu)建
在大型軟件開(kāi)發(fā)中,一個(gè)項(xiàng)目解決所有問(wèn)題顯然不可取的,因?yàn)榇嬖谔嗟拈_(kāi)發(fā)團(tuán)隊(duì)共同協(xié)作,所以對(duì)項(xiàng)目進(jìn)行拆分,形成多個(gè)子項(xiàng)目的形式是普遍存在的。而且一個(gè)子項(xiàng)目只專注自己的邏輯也易于維護(hù)和拓展,現(xiàn)在隨著容器和微服務(wù)的理念逐漸獲得大家的認(rèn)可,多個(gè)子項(xiàng)目分別發(fā)布到容器和形成多個(gè)微服務(wù)也逐漸成為趨勢(shì)。
我們的項(xiàng)目使用 Gradle 來(lái)處理多項(xiàng)目的構(gòu)建,包括大項(xiàng)目和子項(xiàng)目的依賴管理以及容器的建立等。對(duì)于 Gradle 項(xiàng)目來(lái)說(shuō),我們會(huì)有一個(gè)根項(xiàng)目,這個(gè)根項(xiàng)目下會(huì)建立若干子項(xiàng)目,具體文件結(jié)構(gòu)如下:
|--spring-boot-tut (根項(xiàng)目)
|----common (共享子項(xiàng)目)
|------src (子項(xiàng)目源碼目錄)
|--------main (子項(xiàng)目開(kāi)發(fā)源碼目錄)
|----------java(子項(xiàng)目開(kāi)發(fā) Java 類源碼目錄)
|----------resources(子項(xiàng)目資源類源碼目錄)
|------build.gradle (子項(xiàng)目 gradle 構(gòu)建文件)
|----api (API 子項(xiàng)目)
|------src
|--------main
|----------java
|----------resources
|------build.gradle
|----report (報(bào)表子項(xiàng)目)
|------src
|--------main
|----------java
|----------resources
|------build.gradle
|--build.gradle (根項(xiàng)目構(gòu)建文件)
|--settings.gradle (根項(xiàng)目設(shè)置文件)
要讓 Gradle 支持多項(xiàng)目的話,首先需要把 settings.gradle 改成
include 'common'
include 'api'
include 'report'
rootProject.name = 'spring-boot-tut'
這樣 spring-boot-tut 就成為了根項(xiàng)目,而 common 、api 和 report 就是其之下的子項(xiàng)目。接下來(lái),我們看一下根項(xiàng)目的 build.gradle,對(duì)于多項(xiàng)目構(gòu)建來(lái)說(shuō),根項(xiàng)目的 build.gradle 中應(yīng)該盡可能的配置各子項(xiàng)目中共同的配置,從而讓子項(xiàng)目只配置自己不同的東西。
// 一個(gè)典型的根項(xiàng)目的構(gòu)建文件結(jié)構(gòu)
buildscript {
/*
* 構(gòu)建腳本段落可以配置整個(gè)項(xiàng)目需要的插件,構(gòu)建過(guò)程中的依賴以及依賴類庫(kù)的版本號(hào)等
*/
}
allprojects {
/*
* 在這個(gè)段落中你可以聲明對(duì)于所有項(xiàng)目(含根項(xiàng)目)都適用的配置,比如依賴性的倉(cāng)儲(chǔ)等
*/
}
subprojects {
/*
* 在這個(gè)段落中你可以聲明適用于各子項(xiàng)目的配置(不包括根項(xiàng)目哦)
*/
version = "0.0.1"
}
/*
* 對(duì)于子項(xiàng)目的特殊配置
*/
project(':common') {
}
project(':api') {
}
project(':report') {
}
其中,buildscript 段落用于配置 gradle 腳本生成時(shí)需要的東西,比如配置整個(gè)項(xiàng)目需要的插件,構(gòu)建過(guò)程中的依賴以及在其他部分需要引用的依賴類庫(kù)的版本號(hào)等,就像下面這樣,我們?cè)?ext 中定義了一些變量來(lái)集中配置了所有依賴的版本號(hào),無(wú)論是根項(xiàng)目還是子項(xiàng)目都可以使用這些變量來(lái)指定版本號(hào)。這樣做的好處是當(dāng)依賴的版本更新時(shí),我們無(wú)需四處更改散落在各處的版本號(hào)。此外在這個(gè)段落中我們還提供了項(xiàng)目所需的第三方 Gradle 插件所需的依賴:spring-boot-gradle-plugin、gradle-docker 和 dependency-management-plugin,這樣在后面,各子項(xiàng)目可以簡(jiǎn)單的使用諸如 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 中可以聲明對(duì)于所有項(xiàng)目(含根項(xiàng)目)都適用的配置,比如依賴性的倉(cāng)儲(chǔ)等。而 subprojects 和 allprojects 的區(qū)別在于 subprojecrts 只應(yīng)用到子項(xiàng)目,而非根項(xiàng)目。所以大部分通用型配置可以通過(guò) subprojects 和 allprojects 來(lái)完成。下面列出的樣例配置中,我們?yōu)樗械捻?xiàng)目包括根項(xiàng)目配置了依賴倉(cāng)儲(chǔ)以及軟件的 group,同時(shí)為每個(gè)子項(xiàng)目配置了 java 和 idea 兩個(gè)插件、版本號(hào)和通用的測(cè)試依賴。
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 的用法, 我們這個(gè)例子里把每個(gè)子項(xiàng)目的依賴放到根 build.gradle 中的 project(':子項(xiàng)目名') 中列出,這樣做有好處也有缺點(diǎn),好處是依賴性的管理統(tǒng)一在根 build.gradle 完成,對(duì)于依賴的情況一目了然。當(dāng)然缺點(diǎn)是每個(gè)項(xiàng)目更改依賴時(shí)都會(huì)造成根 gradle 的更新,這樣的話如果一個(gè)項(xiàng)目有非常多的子項(xiàng)目時(shí),會(huì)在協(xié)作上出現(xiàn)一些問(wèn)題。所以請(qǐng)根據(jù)具體情況決定把依賴放到根 build.gradle 中的 project(':子項(xiàng)目名') 中還是放到各子項(xiàng)目的 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 中如何構(gòu)建類庫(kù)工程
Spring Boot 的一大優(yōu)點(diǎn)就是把應(yīng)用做成了一個(gè) Fat Jar,這種方式在部署時(shí)有極大的優(yōu)勢(shì)。但如何在 Spring Boot 的多項(xiàng)目構(gòu)建中建立一個(gè)類庫(kù)工程,而不是應(yīng)用工程呢?當(dāng)然前提是我們還能繼續(xù)享受 Spring Boot 帶來(lái)的配置便利性。
首先,我們需要在根工程的 build.gradle 中添加一個(gè) 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}")
}
}
然后在類庫(kù)子項(xiàng)目中的 build.gradle 中添加下面這句即可。
dependencyManagement {
imports { mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}") }
}
慕課網(wǎng) Angular 視頻課上線: http://coding.imooc.com/class/123.html?mc_marking=1fdb7649e8a8143e8b81e221f9621c4a&mc_channel=banner