重拾后端之Spring Boot(六) -- 熱加載、容器和多項(xiàng)目

重拾后端之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.ymlapplication.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 中勾選 Build project automatically
IDEA 中勾選 Build project automatically

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

用快捷鍵調(diào)出 registry 菜單
用快捷鍵調(diào)出 registry 菜單

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

勾選程序運(yùn)行時(shí)允許編譯器自動(dòng)構(gòu)建
勾選程序運(yùn)行時(shí)允許編譯器自動(dòng)構(gòu)建

最后重啟 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)目即可。

創(chuàng)建一個(gè) Spring Boot 的 Run/Debug 模板配置
創(chuàng)建一個(gè) Spring Boot 的 Run/Debug 模板配置

遠(yuǎn)程調(diào)試

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

添加遠(yuǎn)程調(diào)試
添加遠(yuǎn)程調(diào)試

容器支持

使用容器(Docker)來(lái)發(fā)布 Spring Boot 項(xiàng)目非常簡(jiǎn)單。如果你采用 Gradle 構(gòu)建的話,可以不用寫 Dockerfile ,直接在 build.gradle 中建立一個(gè) buildDocker 的任務(wù)即可。當(dāng)然要支持這樣的任務(wù)的話,我們需要首先在 buildscriptdependencies 中引入 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 、apireport 就是其之下的子項(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-plugingradle-dockerdependency-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ǔ)等。而 subprojectsallprojects 的區(qū)別在于 subprojecrts 只應(yīng)用到子項(xiàng)目,而非根項(xiàng)目。所以大部分通用型配置可以通過(guò) subprojectsallprojects 來(lái)完成。下面列出的樣例配置中,我們?yōu)樗械捻?xiàng)目包括根項(xiàng)目配置了依賴倉(cāng)儲(chǔ)以及軟件的 group,同時(shí)為每個(gè)子項(xiàng)目配置了 javaidea 兩個(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容