写点什么

SpringBoot 魔法堂:应用热部署实践与原理浅析

用户头像
云流
关注
发布于: 2020 年 12 月 15 日

前言

后端开发的同学想必每天都在重复经历着修改代码、执行代码编译,等待……重启 Tomcat 服务,等待……最后测试发现还是有 bug,然后上述流程再来一遍(我听不见)😦

能不能像前端开发的同学那样,修改代码保存文件后自动编译、重新加载应用呢?Spring Boot 给了我们一个大大的 Yes!

本文我们就一起来探索 Spring Boot 的热部署功能提升开发效率吧!

长话短说

热部署作为开发阶段的特性,由 spring-boot-devtools 模块提供,用于在修改类、配置文件和页面等静态资源后,自动编译 Spring Boot 应用和加载应用和页面静态资源,从而提高开发流程自动化程度提升开发效率。

那么第一步当然是在 pom.xml 中添加配置:

<dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-devtools</artifactId>  <option>true</option></dependency>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> <!-- 默认值为false,必须设置为true才能启用热部署功能(具体原因请见下文) --> </configuration> </plugin> </plugins></build>
复制代码

静态资源热部署

对于 HTML 页面、图片、CSS 样式文件这些显然不需要编译的静态资源,Spring Boot Devtools 模块通过内置的 livereload 服务端和浏览器的 LiveReload 插件共同实现热部署。

  1. 服务端配置

spring:  devtools:    livereload:      enabled: true # 启用LiveReload服务端      port: 35729 # LiveReload服务端口
复制代码

默认仅触发 LiveReload 事件的默认路径如下: /META-INF/maven,/META-INF/resources,/resources,/static,/public/templates

浏览器配置

无论时 FireFox 还是 Chrome 都有相应的 LiveReload 插件,按步骤安装就可以了。

Java 类资源热部署

Spring Boot Devtools 模块是通过监听 Java 类资源变化触发应用热部署,请注意这里监听的是 Java 类资源而不是 Java 源代码文件,那么什么是 Java 类资源**呢?其实就是.class文件。

这样从保存 Java 源代码文件到 Spring Boot Devtools 监听到 Java 类资源变化之间,就有一道不可逾越的鸿沟了。我们必须通过额外手段填平:

手动方式:修改 Java 源代码文件后,执行mvn compile

自动方式:配置 IDEA 监听 Java 源代码文件变化,触发重新编译

2.1. 右键点击 SpringBootApplication 入口类文件,并点击Create XXXX.main(),创建 Application 类型的 Configuration;

2.2. 勾选File/Settings/Compiler/Build Project automatically

2.3. 按ctrl+shift+alt+/,然后选择Registry并勾选Compiler autoMake allow when app running

2.4. 通过 IDEA 左上角绿色的运行按钮启动 Spring Boot 应用,然后修改 Java 源代码文件后 IDEA 会自动重新编译项目,从而触发 Spring Boot Devtools 热部署。

更多配置配置项

spring:  devtools:  restart:    enabled: true # 启用热部署    exclude: main/static/** # 除默认路径外,添加文件变化不触发热部署的路径列表,多个路径之间通过逗号分隔。    additional: assets/** # 添加文件变化会触发热部署的路径列表,多个路径之间通过逗号分隔。    additional-exclude: assets/public/** # 设置additional属性指定的路径下某些路径的文件变化,不触发热部署,多个路径之间通过逗号分隔。
复制代码

默认不触发热部署的路径有:/META-INF/maven,/META-INF/resources,/resources,/static,/public/templates

除了通过 yml 文件配置是否启用热部署功能外,还可以通过环境变量设置。在 SpringBootApplication 入口方法中加入

System.setProperty("spring.devtools.restart.enabled", "false");SpringApplication.run(MyApp.class, args);
复制代码

疑难解答

在 IDEA 中修改文件后报 Maven Resource Compiler: Maven project configuration required for module 'lkm-api' isn't available. Compilation of Maven projects is supported only if build is started from an IDE.

答:请使用 IDEA 那个绿色的运行按钮启动 Spring Boot 应用。

在 IDEA 中修改文件后没有反应

答:请稍等数秒自然会触发重新编译和热部署的。

为什么是热部署而不是热替换呢?

开发过 React 或 Vue 的同学对热替换应该不陌生吧,可以粗线条地理解为将应用以比文件更细粒度的模块或函数来组织,当源代码发生变化时仅仅替换发生变化的模块或函数以及依赖它们的模块或函数,通过最小化变更达到快速更新应用状态。

而 Spring Boot Devtools 并没有做成像 React 和 Vue 的开发工具那么细粒度的更新,而是采取通过基类加载器重启类加载器两个类加载器来实现热部署:

  1. 基类加载器,用于加载第三方依赖等开发阶段不经常发生变化的 Java 类资源。

  2. 重启类加载器,用于加载当前项目的 Java 类资源。若当前项目的 Java 类资源发生变化时,正在运行的重启类加载器会被丢弃,并另外创建一个重启类加载器并加载最新的 Java 类资源。

为什么 pom.xml 文件中的 spring-boot-maven-plugin 要设置为独立 JVM 进程运行呢(<fork>true</fork>)?

默认情况下<fork>false</fork>表示 Maven 采用运行自身的 JVM 虚拟机运行插件,而通过<fork>true</fork>则告知 Maven 启动一个新的 JVM 虚拟机进程运行插件。

那么为什么要耗费资源启动新 JVM 虚拟机执行插件呢?直接运行不香吗?

场景 1——使用不同的 JDK 运行插件

执行mvn -v会显示当前 Maven 运行的 JDK 版本信息,假设为 JDK1.8 且编码方式为 UTF-8。

由于 Maven 3.8.1 必须运行在 JDK1.8 以上,而项目只能在 JDK1.6 上编译运行,因此需要通过如下方式执行插件:

<plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-compiler-plugin</artifactId>  <version>3.8.1</version>  <configuration>    <fork>true</fork>    <!-- 指定JDK家目录,默认为环境变量PATH中的路径 -->    <executable>/path/to/jdk1.6</executable>    <compilerVersion>1.3</compilerVersion>  </configuration></plugin>
复制代码

场景 2——采用不同的 JVM 配置运行插件

<plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-compiler-plugin</artifactId>  <version>3.8.1</version>  <configuration>      <fork>true</fork>      <meminitial>128m</meminitial>      <maxmem>1024m</maxmem>      <compilerArgs>        <arg>-XX:MaxPermSize=256m</arg>      </compilerArgs>  </configuration></plugin>
复制代码

场景 3——插件需要特定的 JVM 配置来运行

像 spring-boot-maven-plugin 那样在启用 spring-boot-devtools 模块时需要特定 JVM 配置来运行,并且运行途中还会对重启类加载器惨下杀手的,自然也要创建新的 JVM 虚拟机进程来运行才可以了。

总结

Spring Boot 不单单通过约定由于配置的原则简化了过去 Spring MVC 那些繁琐的配置文件,还提供各种显著提升开发效率的自动化工具,而 spring-boot-devtools 就是其中一个。

倘若你所在的团队还没用上 Spring Boot 那么是不是就无法享受这份便捷呢?我想 JRebel IDEA 插件应该是你需要的:)

转载请注明来自:https://www.cnblogs.com/fsjohnhuang/p/14136491.html —— 肥仔 John


用户头像

云流

关注

还未添加个人签名 2020.09.02 加入

还未添加个人简介

评论

发布
暂无评论
SpringBoot魔法堂:应用热部署实践与原理浅析