Spring Boot Devtools 为 Spring Boot 应用程序提供了通常在 IntelliJ 等 IDE 中可用的功能,例如,当某些类或资源发生更改时,您可以在其中重新启动应用程序或强制重新加载实时浏览器。这在应用程序的开发阶段非常有用。
它通常与 IDE 结合使用,这样当在类路径中检测到它并且没有被禁用时,它将由 Spring Boot 与应用程序的其余部分一起启动。
尽管您可以将其配置为监视更多资源,但它通常会在您的应用程序代码、类和资源中查找更改。
重要的是,AFAIK,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
该命令将下载项目依赖项,编译并运行您的应用程序。
现在,在您的源代码中执行任何修改,无论是在您的类中还是在您的资源中,并从另一个终端,在同一目录中,重新编译您的资源:
mvn compile
如果您查看第一个终端窗口,您将看到应用程序已重新启动以反映更改。
如果您使用 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
使用此设置,您可以将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 https://stackoverflow.com/questions/53691781/how-to-cache-maven-dependencies-in-docker)
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"]
中的重要行Dockerfile
是这样的:
mvn clean compile dependency:copy-dependencies -Dspring-boot.repackage.skip=true
它将指示 maven 编译您的资源并复制所需的库。尽管对于目标运行的典型 Maven 阶段是多余的spring-boot-maven-plugin
repackage
,但该标志spring-boot.repackage.skip=true
将指示此插件不重新打包应用程序。
有了这个Dockerfile
,构建你的图像(让我们标记它devtools-demo
,例如):
docker build -t devtools-demo .
并运行它:
docker run devtools-demo:latest
使用此设置,如果您现在更改类和/或资源,并在mvn
本地运行:
mvn compile
您应该能够使用以下docker cp
命令强制容器中的重启机制:
docker cp classes <container name>:/app/classes
请再次考虑在您的容器中安装一个卷,而不是使用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 https://stackoverflow.com/questions/53691781/how-to-cache-maven-dependencies-in-docker)
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"]
要使 Spring Boot Devtools 远程模式正常工作,您需要几件事(Opri 在他/她的回答中也指出了其中一些)。
首先,您需要将devtoolsspring-boot-maven-plugin
配置为包含在您的应用程序 jar 中(否则默认情况下将被排除):
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludeDevtools>false</excludeDevtools>
</configuration>
</plugin>
然后,您需要为配置属性设置一个值spring.devtools.remote.secret
。此属性与 Spring Boot Devtools 中远程调试的工作方式有关。
远程调试功能由两部分组成,客户端和服务器。基本上,客户端是您的服务器代码的副本,它使用spring.devtools.remote.secret
配置属性的值对服务器进行身份验证。
此客户端代码应该从 IDE 运行,并且您将 IDE 调试过程附加到从该客户端公开的本地服务器。
请记住,在客户端监控的资源中执行的每个更改都与在您的服务器中一样,被推送到远程服务器,并且在必要时会触发重新启动。
如您所见,从开发的角度来看,此功能再次更合适。
如果您需要通过覆盖您的 jar 应用程序文件来实际重新启动您的应用程序,也许更好的方法是配置您的 docker 容器以在您的ENTRYPOINT
或CMD
. 此 shell 脚本将监视某个目录中的 jar 副本。如果该资源发生更改,由于您的docker cp
,此 shell 脚本将停止当前正在运行的应用程序版本 - 此应用程序应该从不同的位置运行以避免更新 jar 时出现问题 - 用新的 jar 替换当前 jar,然后启动新的应用程序版本。不一样,但请考虑阅读这个相关的 SO 答案。
在任何情况下,当您在容器中运行应用程序时,您都在尝试为其提供一致且独立于平台的部署方式。从这个角度来看,与其监视 docker 容器中的更改,更方便的方法可能是生成并部署具有这些新更改的容器映像的新版本。使用 Jenkins、Travis 等工具可以极大地自动化此过程。这些工具允许您定义 CI/CD 管道,例如,响应代码提交,可以使用您的代码动态生成一个 docker 映像,并进行相应配置,稍后将该映像部署到某些 docker 风格的服务或Kubernetes,在本地或云端。其中一些,尤其是 Kubernetes,但聚集了一个均匀的docker compose同样,将允许您执行滚动更新,而不会或最小的应用程序服务中断。
总而言之,它可能不符合您的需求,但请注意,您可以spring-boot-starter-actuator
直接使用或与Spring Boot Admin一起使用,例如,重新启动您的应用程序。
最后,正如 Spring Boot Devtools 文档中已经指出的那样,您可以尝试不同的选项,不是基于重新启动,而是基于应用程序重新加载,在热交换中。此功能由JRebel等商业产品提供,尽管也有一些开源替代品,主要是dcevm和HotswapAgent。这篇相关文章提供了有关这最后两种产品如何工作的一些见解。这个 Github 项目提供了有关如何在 docker 容器中运行它的补充信息。