【问题标题】:Spring development on DockerDocker 上的 Spring 开发
【发布时间】:2020-03-26 11:49:44
【问题描述】:

我对 Java 和 Spring 还是很陌生。我正在寻找一个容器化的解决方案来监视src 文件夹,重建项目并利用 Spring devtools hotswap 重新加载更改的类。

我搜索过,但我一直在寻找可用于生产的容器,构建和运行的步骤是分开的。我尝试使用 2 个不同的容器,一个带有持续构建的 Gradle (gradle build --continuous),另一个用于执行构建结果:

version: '3.7'
services:

  builder:
    image: gradle:jdk11
    working_dir: /home/gradle/project
    volumes:
      - ./:/home/gradle/project
    command: gradle build --continuous

  api:
    image: openjdk:11-slim
    volumes:
    - ./build/classes:/app
    command: java -classpath /app/java/main com.example.Application

它失败是因为 Java 在 api 容器中找不到依赖项(Spring、devtools、h2 等),而且我不知道如何让 Gradle 将外部 jar 包含在构建文件夹中。我想做点事情like this,只是例子已经过时了。

不过,我一直认为可能会有更优雅、更简单的解决方案。不一定要使用 Gradle,如果可以,它可以是 Maven! :)

我知道很多 IDE 都支持自动构建和开发工具,我只是想在 Docker 上实现它。这样,我将拥有一个在存储库上而不是在 IDE 配置上的开发工作流,并且几乎与任何开发环境兼容。这是个坏主意吗?

【问题讨论】:

  • VSCode 允许在 container 内进行开发。例如,您可以构建一个已安装所有依赖项的开发映像。关于热插拔,我不确定它如何与 spring 一起工作,但它与 node 一起工作得很好,我想它与 Java 差不多。
  • 不,在这个用例中使用 Docker 并不是一个坏主意。 gradle --continuous build 将起作用。我不确定 Gradle 无法找到 Spring 库是什么意思。如果它作为依赖项列在您的build.gradle 中,则应在运行gradle --continuous build 时安装它。至于您的api 服务,最好设置ports 以便您可以在容器外访问它。这是我的docker-compose.dev.yml 的示例。
  • 是的,我在 Node 的每一种风格中都广泛地看到了它,所以我很惊讶这样一个基本的功能很难用 Java 实现。
  • @NeelKamath 感谢您分享您的示例。据我所知,您的 Gradle 容器在启动时构建,然后运行。如果代码更改,它不会重建和重新运行,是吗?为了清楚起见,我编辑了关于 Gradle 和 Spring 依赖项的部分。
  • @AlessandroCappello 我在较新的 cmets 中链接到的文件中使用了 --continuous。我的第一条评论的docker-compose.dev.yml 使用-t 而不是--continuous,但它们是同一个标志。

标签: java spring maven docker gradle


【解决方案1】:

最后,我找到了一个效果很好的解决方案,只有一个警告。 这当然是一个开发环境,旨在快速更改文件、自动构建和刷新 Spring 应用程序。 不适用于生产。

构建过程被委托给一个 Gradle 容器来监视更改。由于 Gradle 具有 Incremental Compilation,因此即使对于大型项目,它也应该可以很好地扩展。

应用程序本身在openjdk:11-slim 上执行。由于它运行 .class 文件,SpringBoot 知道它是 dev-env 并激活它的devtools

这是我的docker-compose.yml

version: '3.7'
services:

  builder:
    image: gradle:jdk11
    working_dir: /home/gradle/project
    volumes:
      - ./build:/home/gradle/project/build
      - ./src:/home/gradle/project/src
      - ./build.gradle:/home/gradle/project/build.gradle
    command: gradle build --continuous -x test -x testClasses

  api:
    image: openjdk:13-alpine
    volumes:
    - ./build:/app
    depends_on:
      - builder
    command: java -cp "/app/classes/java/main:/app/dependencies/*:/app/resources/main" com.example.Application

这是我的build.gradle

plugins {
    id 'org.springframework.boot' version '2.2.1.RELEASE'
    id 'io.spring.dependency-management' version '1.0.8.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.1.0-SNAPSHOT'
sourceCompatibility = '11'

configurations {
    developmentOnly
    runtimeClasspath {
        extendsFrom developmentOnly
    }
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

task copyLibs(type: Copy) {
    from configurations.runtimeClasspath
    into "${buildDir}/dependencies"
}

build.dependsOn(copyLibs)

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'com.h2database:h2'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

test {
    useJUnitPlatform()
}

总而言之,重建和热交换源代码更改需要 5 秒。不像 webpack 那样快,但仍然可以接受。以这种方式拥有它的最大优势在于它驻留在代码上,每个人都可以让它工作,无论他们的工作站如何。

警告?第一次运行时,构建文件夹为空,api 容器无法启动。您必须等待builder 完成其工作,然后重新启动api

我仍然希望有更好的解决方案,我鼓励您发布所有比这更顺畅的内容。

【讨论】:

  • 你可以拥有api服务depends_on: []builder服务。这当然只会等待容器启动,而不仅仅是服务,因此您必须使用wait-for-it.sh 之类的东西。使用额外的 shell 脚本可能会稍微复杂一些,因此对于开发环境等更简单的设置,我只需 sleep 10 或类似的(例如,docker-compose.yml 中的 command: bash -c 'sleep 10 && gradle run')。
猜你喜欢
  • 2020-05-03
  • 2020-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-30
  • 1970-01-01
  • 2015-11-25
  • 2019-01-14
相关资源
最近更新 更多