阅读 1336

重拾后端之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}") }
}复制代码
关注下面的标签,发现更多相似文章
评论
说说你的看法