spring-boot-devtools 自动重启不起作用

spr*_*aff 6 java docker spring-boot spring-boot-devtools

我有一个使用 mvn 构建的有效 Spring Boot 2.25 应用程序。根据此文档,我添加

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
Run Code Online (Sandbox Code Playgroud)

从文档:

由于 DevTools 监控类路径资源,触发重启的唯一方法是更新类路径。更新类路径的方式取决于您使用的 IDE。在 Eclipse 中,保存修改后的文件会导致更新类路径并触发重新启动。在 IntelliJ IDEA 中,构建项目(Build -> Build Project)具有相同的效果。

随着应用程序的运行,我尝试了一个简单的

touch /path/to/app.jar
Run Code Online (Sandbox Code Playgroud)

期待应用程序重新启动,但什么也没发生。

好吧,也许它正在做一些更聪明的事情。我修改了一些源 .java,重新编译了 .jar,然后用cp它来替换正在运行的 .jar 文件,然后……什么也没发生。

也来自文档

DevTools 依靠应用程序上下文的关闭挂钩在重新启动期间关闭它。如果您禁用了关闭钩子 (SpringApplication.setRegisterShutdownHook(false)),它就不能正常工作。

我不是在做这个。

DevTools 需要自定义ApplicationContext 使用的ResourceLoader。如果您的应用程序已经提供了一个,它将被包装。不支持直接覆盖 ApplicationContext 上的 getResource 方法。

我不是在做这个。

如果这很重要,我正在 Docker 容器中运行它。从文档:

运行完全打包的应用程序时,开发人员工具会自动禁用。如果您的应用程序是从 java -jar 启动的,或者是从特殊的类加载器启动的,则它被视为“生产应用程序”。如果这不适用于您(即,如果您从容器运行您的应用程序),请考虑排除 devtools 或设置 -Dspring.devtools.restart.enabled=false 系统属性。

我不明白这意味着什么,或者它是否相关。

我想重新编译一个 .jar 并在正在运行的 docker 容器中替换它,并在不重新启动容器的情况下触发和应用程序重新启动。我怎样才能做到这一点?

编辑:我正在使用mvn重建 jar,然后docker cp在正在运行的容器中替换它。(IntelliJ IDEA 声称要重建项目,但实际上并未触及jar 文件,但那是另一回事了。)我正在寻找非 IDE 特定的解决方案。

jcc*_*ero 5

Spring Boot Devtools 为 Spring Boot 应用程序提供了 IntelliJ 等 IDE 中通常提供的功能,例如,您可以在某些类或资源发生更改时重新启动应用程序或强制实时浏览器重新加载。这在应用程序的开发阶段非常有用。

它通常与 IDE 结合使用,当在类路径中检测到并且未禁用它时,Spring Boot 将与应用程序的其余部分一起启动它。

尽管您可以将其配置为监视更多资源,但它通常会查找应用程序代码、类和资源中的更改。

重要的是,据我所知,Devtools 将以爆炸方式监视您自己的类和资源,我的意思是,如果您覆盖整个应用程序 jar,只有覆盖目录中的某些资源,重新启动过程将不起作用classes

可以使用 Maven 测试此功能。请考虑从Spring Initializr下载一个简单的蓝图,例如使用 Spring Boot、Spring Boot Devtools 和 Spring Web,以便保持应用程序运行。从终端,在包含该pom.xml文件的目录中,运行您的应用程序,例如,借助以下插件spring-boot-maven-plugin中包含的插件pom.xml

mvn spring-boot:run
Run Code Online (Sandbox Code Playgroud)

该命令将下载项目依赖项、编译并运行您的应用程序。

现在,在您的类或资源中对源代码执行任何修改,然后从另一个终端在同一目录中重新编译您的资源:

mvn compile
Run Code Online (Sandbox Code Playgroud)

如果您查看第一个终端窗口,您将看到应用程序已重新启动以反映更改。

如果您使用 docker 进行应用程序部署,尝试重现此行为可能会很棘手。

一方面,我不知道这是否有意义,但您可以尝试创建一个基于 Maven 的映像并在其中运行代码,如上所述。您的Dockerfile外观可能与此类似:

FROM maven:3.5-jdk-8 as maven

WORKDIR /app

# Copy project pom
COPY ./pom.xml ./pom.xml

# Fetch (and cache) dependencies
RUN mvn dependency:go-offline -B

# Copy source files
COPY ./src ./src

# Run your application
RUN mvn springboot:run
Run Code Online (Sandbox Code Playgroud)

docker cp通过此设置,您可以将资源复制到该/app/target目录,它将触发应用程序重新启动。作为替代方案,请考虑在容器中安装卷而不是使用docker cp.

更好的是,考虑到覆盖应用程序 jar 可能不起作用的事实,您可以尝试复制类和库依赖项,并以分解方式运行应用程序。考虑以下Dockerfile

FROM maven:3.5-jdk-8 as maven

WORKDIR /app

# Copy your project pom
COPY ./pom.xml ./pom.xml

# Fetch (and cache) dependencies
RUN mvn dependency:go-offline -B

# Copy source files
COPY ./src ./src

# Compile application and library dependencies
# The dependencies will, by default, be copied to target/dependency
RUN mvn clean compile dependency:copy-dependencies -Dspring-boot.repackage.skip=true

# Final run image (based on /sf/ask/3758424701/)
FROM openjdk:8u171-jre-alpine
# OPTIONAL: copy dependencies so the thin jar won't need to re-download them
# COPY --from=maven /root/.m2 /root/.m2

# Change working directory
WORKDIR /app

# Copy classes from maven image
COPY --from=maven /app/target/classes ./classes

# Copy dependent libraries
COPY --from=maven /app/target/dependency ./lib

EXPOSE 8080

# Please, modify your main class name as appropriate
ENTRYPOINT ["java", "-cp", "/app/classes:/app/lib/*", "com.example.demo.DemoApplication"]
Run Code Online (Sandbox Code Playgroud)

其中重要的一行Dockerfile是:

mvn clean compile dependency:copy-dependencies -Dspring-boot.repackage.skip=true
Run Code Online (Sandbox Code Playgroud)

它将指示 maven 编译您的资源并复制所需的库。尽管对于目标运行的典型 Maven 阶段来说是多余的spring-boot-maven-plugin repackage,但该标志spring-boot.repackage.skip=true将指示此插件不要重新打包应用程序。

有了这个,构建你的图像(例如,Dockerfile让我们标记它):devtools-demo

docker build -t devtools-demo .
Run Code Online (Sandbox Code Playgroud)

并运行它:

docker run devtools-demo:latest
Run Code Online (Sandbox Code Playgroud)

通过此设置,如果现在您更改类和/或资源,并mvn在本地运行:

mvn compile
Run Code Online (Sandbox Code Playgroud)

您应该能够使用以下docker cp命令在容器中强制重新启动机制:

docker cp classes <container name>:/app/classes
Run Code Online (Sandbox Code Playgroud)

请再次考虑在容器中安装一个卷,而不是使用docker cp.

我测试了设置并且它工作正常。

要记住的重要想法是替换分解的资源,而不是整个应用程序 jar。

作为另一种选择,您可以采用与评论中所示类似的方法,并以远程模式运行 Devtools:

FROM maven:3.5-jdk-8 as maven

WORKDIR /app

# Copy project pom
COPY ./pom.xml ./pom.xml

# Fetch (and cache) dependencies
RUN mvn dependency:go-offline -B

# Copy source files
COPY ./src ./src

# Build jar
RUN mvn package && cp target/your-app-version.jar app.jar

# Final run image (based on /sf/ask/3758424701/)
FROM openjdk:8u171-jre-alpine
# OPTIONAL: copy dependencies so the thin jar won't need to re-download them
# COPY --from=maven /root/.m2 /root/.m2

# Change working directory
WORKDIR /app

# Copy artifact from the maven image
COPY --from=maven /app/app.jar ./app.jar

ENV JAVA_DOCKER_OPTS "-agentlib:jdwp=transport=dt_socket,server=y,address=*:8000,suspend=n"

ENV JAVA_OPTS "-Dspring.devtools.restart.enabled=true"

EXPOSE 8000

EXPOSE 8080

ENTRYPOINT ["/bin/bash", "-lc", "exec java $JAVA_DOCKER_OPTS $JAVA_OPTS -jar /app/app.jar"]
Run Code Online (Sandbox Code Playgroud)

为了使 Spring Boot Devtools 远程模式正常工作,您需要几件事(Opri 在他/她的回答中也指出了其中一些)。

首先,您需要配置spring-boot-maven-plugin以将devtools 包含在您的应用程序 jar 中(否则默认情况下它将被排除):

mvn spring-boot:run
Run Code Online (Sandbox Code Playgroud)

然后,您需要为配置属性设置一个值spring.devtools.remote.secret。此属性与 Spring Boot Devtools 中远程调试的工作方式有关。

远程调试功能由客户端和服务器两部分组成。基本上,客户端是服务器代码的副本,它使用spring.devtools.remote.secret配置属性的值来针对服务器进行自身身份验证。

该客户端代码应该从 IDE 运行,并且您将 IDE 调试进程附加到从该客户端公开的本地服务器。

请记住,在客户端监视的资源中执行的每个更改与在服务器中相同,都会被推送到远程服务器,并且如有必要,它将触发重新启动。

正如您所看到的,从开发的角度来看,此功能再次更合适。

如果您需要通过覆盖 jar 应用程序文件来实际重新启动应用程序,也许更好的方法是将 docker 容器配置为在您的ENTRYPOINTCMD. 此 shell 脚本将监视某个目录中的 jar 副本。如果该资源发生变化,由于您的docker cp,此 shell 脚本将停止当前正在运行的应用程序版本 - 该应用程序应该从不同的位置运行,以避免更新 jar 时出现问题 - 用新的 jar 替换当前的 jar,然后启动新的应用程序版本。不一样,但是请考虑阅读这个相关的 SO 答案

无论如何,当您在容器中运行应用程序时,您都在尝试为其提供一致且独立于平台的部署方式。从这个角度来看,更方便的方法可能是生成并部署包含这些新更改的新版本容器映像,而不是监视 docker 容器中的更改。使用 Jenkins、Travis 等工具可以极大地自动化此过程。这些工具允许您定义 CI/CD 管道,例如,响应代码提交,可以使用您的代码动态生成 docker 映像,并进行相应配置,稍后将此映像部署到某些 docker 风格或Kubernetes,本地或云端。其中一些,尤其是 Kubernetes,但也有swarm甚至docker 组合,将允许您执行滚动更新,而无需或最小化应用程序服务中断。

总而言之,它可能不适合您的需求,但请注意,您可以spring-boot-starter-actuator直接使用或与Spring Boot Admin一起使用,例如,重新启动您的应用程序。

最后,正如 Spring Boot Devtools 文档中已经指出的那样,您可以尝试不同的选项,不是基于重新启动,而是基于应用程序重新加载和热交换。此功能由JRebel等商业产品提供,尽管也有一些开源替代品,主要是dcevmHotswapAgent这篇相关文章提供了有关后两种产品如何工作的一些见解。此 Github 项目提供了有关如何在 docker 容器中运行它的补充信息。


小智 4

我在使用intellij idea时遇到了类似的问题,我在某个地方看到你必须使用构建按钮才能使其工作。

在此输入图像描述

在jsp中,应用程序重新加载文件,它不是完全自动的,因为intellij自动保存->这种行为是默认的,但我认为有一种方法可以改变它。-> 手动录制,然后自动重新加载。

仅适用于 jsp 应用程序,如果您在标准应用程序中尝试此操作,它将创建双帧执行(swing)