重拾后端之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

重拾后端之Spring Boot(六) -- 热加载、容器和多项目_第1张图片
IDEA 中勾选 Build project automatically

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

重拾后端之Spring Boot(六) -- 热加载、容器和多项目_第2张图片
用快捷键调出 registry 菜单

然后寻找到 compiler.automake.allow.when.app.running,进行勾选

重拾后端之Spring Boot(六) -- 热加载、容器和多项目_第3张图片
勾选程序运行时允许编译器自动构建

最后重启 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(六) -- 热加载、容器和多项目_第4张图片
创建一个 Spring Boot 的 Run/Debug 模板配置

远程调试

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

重拾后端之Spring Boot(六) -- 热加载、容器和多项目_第5张图片
添加远程调试

容器支持

使用容器(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

你可能感兴趣的:(重拾后端之Spring Boot(六) -- 热加载、容器和多项目)