重拾后端之Spring Boot(六) -- 热加载、容器和多项目

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

在 IDEA 的 registry 中勾选 compiler.automake.allow.when.app.running (macOS 下使用 option + command + shift + / ,Windows 下使用 Ctrl + Alt + Shift + / 调出 registry 菜单)

用快捷键调出 registry 菜单
用快捷键调出 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:8090Working Directory 填写 $MODULE_DIR$,而 Use classpath of module 选择当前项目即可。

创建一个 Spring Boot 的 Run/Debug 模板配置
创建一个 Spring Boot 的 Run/Debug 模板配置

远程调试

IDEA 提供了非常良好的远程调试支持,添加远程调试的话,可以去 Run/Debug Configurations 中新建一个 Remote 类型的配置,其中默认端口为 8000,Transport 选择 SocketDebug Mode 选择 Attach 即可。这种远程调试可以支持在 Docker 容器中进行调试,方便团队的环境容器化。

添加远程调试
添加远程调试

容器支持

使用容器(Docker)来发布 Spring Boot 项目非常简单。如果你采用 Gradle 构建的话,可以不用写 Dockerfile ,直接在 build.gradle 中建立一个 buildDocker 的任务即可。当然要支持这样的任务的话,我们需要首先在 buildscriptdependencies 中引入 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 就成为了根项目,而 commonapireport 就是其之下的子项目。接下来,我们看一下根项目的 build.gradle,对于多项目构建来说,根项目的 build.gradle 中应该尽可能的配置各子项目中共同的配置,从而让子项目只配置自己不同的东西。

// 一个典型的根项目的构建文件结构
buildscript {
    /*
     * 构建脚本段落可以配置整个项目需要的插件,构建过程中的依赖以及依赖类库的版本号等
     */
}

allprojects {
    /*
     * 在这个段落中你可以声明对于所有项目(含根项目)都适用的配置,比如依赖性的仓储等
     */
}

subprojects {
    /*
     * 在这个段落中你可以声明适用于各子项目的配置(不包括根项目哦)
     */
    version = "0.0.1"
}

/*
 * 对于子项目的特殊配置
 */
project(':common') {
    
}

project(':api') {
    
}

project(':report') {
    
}

其中,buildscript 段落用于配置 gradle 脚本生成时需要的东西,比如配置整个项目需要的插件,构建过程中的依赖以及在其他部分需要引用的依赖类库的版本号等,就像下面这样,我们在 ext 中定义了一些变量来集中配置了所有依赖的版本号,无论是根项目还是子项目都可以使用这些变量来指定版本号。这样做的好处是当依赖的版本更新时,我们无需四处更改散落在各处的版本号。此外在这个段落中我们还提供了项目所需的第三方 Gradle 插件所需的依赖:spring-boot-gradle-plugingradle-dockerdependency-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 中可以声明对于所有项目(含根项目)都适用的配置,比如依赖性的仓储等。而 subprojectsallprojects 的区别在于 subprojecrts 只应用到子项目,而非根项目。所以大部分通用型配置可以通过 subprojectsallprojects 来完成。下面列出的样例配置中,我们为所有的项目包括根项目配置了依赖仓储以及软件的 group,同时为每个子项目配置了 javaidea 两个插件、版本号和通用的测试依赖。

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}") }
}

慕课网 Angular 视频课上线: http://coding.imooc.com/class/123.html?mc_marking=1fdb7649e8a8143e8b81e221f9621c4a&mc_channel=banner

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,612评论 5 471
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,345评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,625评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,022评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,974评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,227评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,688评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,358评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,490评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,402评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,446评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,721评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,802评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,013评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,504评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,080评论 2 341

推荐阅读更多精彩内容