乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 Spring Boot 概述

    • 章节导读:Spring Boot概述与核心理念
    • Spring Boot是什么
    • Spring Boot与Spring Framework的关系
    • 约定优于配置
  • 第2章 快速入门与第一个应用

    • 章节导读:快速入门与第一个应用
    • SpringApplication
    • 第一个Spring Boot应用
  • 第3章 起步依赖与版本管理

    • 章节导读:起步依赖与版本管理
    • 起步依赖
    • BOM版本管理
  • 第4章 自动配置原理

    • 章节导读:自动配置原理
    • 自动配置原理
    • AutoConfigurationImportSelector
    • 自动配置报告
  • 第5章 核心注解

    • 章节导读:核心注解
    • @SpringBootApplication
    • @EnableAutoConfiguration
    • @ConditionalOnClass
    • @ConditionalOnMissingBean
    • @ConditionalOnBean
    • @ConditionalOnProperty
  • 第6章 外部化配置与属性绑定

    • 章节导读:外部化配置与属性绑定
    • 外部化配置
    • @ConfigurationProperties
    • @Value
    • 配置属性优先级
  • 第7章 Profile 与环境切换

    • 章节导读:Profile与环境切换
    • @Profile
    • 多环境配置文件
  • 第8章 内嵌服务器与部署

    • 章节导读:内嵌服务器与部署
    • 内嵌服务器
    • Fat Jar
  • 第9章 Actuator 与监控

    • 章节导读:Actuator与监控
    • Actuator Health
    • Actuator Info
    • Actuator Metrics
    • 自定义Endpoint
    • 自定义HealthIndicator
  • 第10章 开发工具与最佳实践

    • 章节导读:开发工具与最佳实践
    • Banner自定义
    • 热部署

热部署

一句话定位:spring-boot-devtools 通过双 ClassLoader 机制和文件变更监听,在开发阶段实现代码修改后的自动重启与静态资源实时刷新,将传统"修改-停止-启动"的分钟级反馈循环缩短到秒级。


定义与作用

在传统 SSM 开发中,修改一个 Service 方法后需要:停止 Tomcat(5 秒)→ 重新编译(10 秒)→ 启动 Tomcat(30 秒)→ 验证效果。一次微小调整可能消耗近一分钟。这种长反馈循环严重拖慢开发效率。

Spring Boot 的 spring-boot-devtools 模块专为开发环境设计,提供两项核心能力:

能力说明触发条件
自动重启(Auto Restart)代码变更后自动重启 Spring 容器classpath 下的类文件发生变化
LiveReload浏览器自动刷新页面静态资源(HTML/CSS/JS)发生变化

核心设计:devtools 使用两个 ClassLoader——restart ClassLoader 加载项目自身的类(变化频繁),base ClassLoader 加载第三方依赖(相对稳定)。重启时只需丢弃并重建 restart ClassLoader,大量依赖无需重新加载,因此比冷启动快得多。


适用位置与常用属性

spring-boot-devtools 作为依赖引入,无需代码注解。所有行为通过配置控制。

依赖引入(pom.xml)

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
        <scope>runtime</scope>
    </dependency>
</dependencies>

<optional>true</optional> 的作用:确保其他项目依赖本模块时,不会传递引入 devtools。避免 devtools 被带入生产环境。

常用配置属性

# application.yml
spring:
  devtools:
    restart:
      enabled: true              # 是否启用自动重启(默认 true)
      additional-paths:            # 额外监听路径
        - src/main/java
        - src/main/resources
      exclude:                   # 排除不监听的文件/路径
        - static/**
        - public/**
      poll-interval: 1000          # 轮询间隔(毫秒)
      quiet-period: 400            # 静默期,文件变更停止后等待多久触发重启
    livereload:
      enabled: true                # 是否启用 LiveReload(默认 true)
      port: 35729                  # LiveReload 浏览器插件通信端口
属性说明默认值
spring.devtools.restart.enabled自动重启总开关true
spring.devtools.restart.additional-paths额外监听目录[]
spring.devtools.restart.exclude排除模式(不触发重启)META-INF/maven/**,META-INF/resources/**,resources/**,static/**,public/**,templates/**
spring.devtools.restart.poll-interval文件系统轮询间隔1000 ms
spring.devtools.restart.quiet-period变更停止后延迟重启400 ms
spring.devtools.livereload.enabledLiveReload 开关true
spring.devtools.livereload.portLiveReload 服务端口35729

全局设置:在 Windows 下,可在用户主目录创建 C:\Users\{用户名}\.spring-boot-devtools.properties,设置所有本地项目共享的配置(如 spring.devtools.restart.trigger-file=.reloadtrigger)。


核心原理

双 ClassLoader 重启机制

机制解读:

  1. 应用启动时,devtools 创建两个类加载器:base 加载第三方 JAR,restart 加载项目自身类。
  2. 后台线程持续监视 classpath 文件变化(轮询或事件监听)。
  3. 当检测到项目类文件(.class)或配置文件变化时,先销毁旧的 restart ClassLoader,再创建新的 restart ClassLoader 重新加载业务类。
  4. 调用 SpringApplication.run() 重新初始化 Spring 容器和内嵌服务器。由于 base ClassLoader 未重建,第三方依赖无需重新加载,重启时间显著缩短(通常 2-5 秒,视项目规模而定)。

重启时序流程


完整示例

场景说明

飞翔科技的后端开发小崔正在开发学生成绩查询接口。他频繁调整 StudentService 中的查询逻辑和日志输出,每次手动重启需要 30 秒以上。架构师白歌建议引入 spring-boot-devtools 提升开发效率,要求小崔验证:修改 Service 类后应用能否在数秒内自动重启,且静态资源修改不需要重启。

操作前:无 devtools 的手动重启

<!-- 操作前:pom.xml 中没有 devtools -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

小崔修改 StudentService.java 后,需要:

  1. 在 IDEA 中点击停止按钮(或按 Ctrl+C)
  2. 重新运行 StudentManagementApplication.main()
  3. 等待 Tomcat 启动完成(约 30 秒)
  4. 刷新浏览器验证

整个反馈周期约 35-40 秒,开发体验极差。

使用该特性的完整代码

步骤一:在 pom.xml 中引入 devtools:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

步骤二:配置 application.yml(可选,使用默认值即可)

spring:
  devtools:
    restart:
      enabled: true
      exclude: static/**,public/**  # 静态资源变更不触发重启
    livereload:
      enabled: true

步骤三:保持业务代码正常开发

package com.feixiang.student.service;

import org.springframework.stereotype.Service;

@Service
public class StudentService {

    public String queryScore(Long studentId) {
        // 小崔正在频繁调整这里的逻辑和日志
        return "学生 " + studentId + " 的成绩查询成功(当前时间:" + System.currentTimeMillis() + ")";
    }
}

操作后运行结果及分析

小崔修改 StudentService.queryScore() 方法体,在 IDEA 中按 Ctrl+F9(Build Project)编译后,控制台自动输出:

Restarting due to 1 class path change (com/feixiang/student/service/StudentService.class)

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.7.18)

2024-05-20 14:10:25.123  INFO 12345 --- [  restartedMain] c.f.s.StudentManagementApplication : Started StudentManagementApplication in 2.456 seconds

关键日志解读:

  1. Restarting due to 1 class path change:devtools 检测到 StudentService.class 文件变更,自动触发重启。
  2. restartedMain:线程名带有 restarted 前缀,区别于首次启动的 main 线程。
  3. Started ... in 2.456 seconds:重启耗时约 2.5 秒,比冷启动快 10 倍以上(因为第三方依赖未重新加载)。

小崔立即刷新浏览器验证新逻辑,整个过程无需手动停止或启动。

修改 src/main/resources/static/index.html 后,控制台无重启日志,浏览器通过 LiveReload 自动刷新(需安装 LiveReload 浏览器插件或 IDE 内置支持)。

分析:

  1. 双 ClassLoader 机制生效:只有 restart ClassLoader 重建,Spring Boot 核心库和 Tomcat 仍在 base ClassLoader 中,无需重新初始化。
  2. exclude: static/** 生效:静态资源变更不触发容器重启,避免不必要的重启开销。
  3. LiveReload 生效:前端资源修改实时反馈到浏览器,前后端开发效率均提升。

易错场景与面试考点

易错场景一:生产环境误引入 devtools

<!-- 错误示范:未标记 optional,导致传递依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

后果:如果该模块被其他项目依赖,devtools 会传递到生产环境。生产环境 JAR 中携带 devtools 后,会启动文件监听线程和 LiveReload 服务,造成性能损耗。更严重的是,Spring Boot 1.x/2.x 早期版本存在远程触发重启的安全漏洞(Spring Boot 2.7.x 已修复远程访问,但仍不建议携带)。

正确做法:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

同时确保打包时排除 devtools:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <excludeDevtools>true</excludeDevtools>
    </configuration>
</plugin>

易错场景二:修改配置文件未触发重启

小崔修改 application.yml 中的 server.port 后,等待重启却未发生。

原因:Spring Boot 的默认配置中,application.yml 本身在 base ClassLoader 或标准资源加载路径中,某些 IDE 的编译行为不会触发 restart ClassLoader 感知的文件变更。此外,部分配置变更即使重启也无法热生效(如端口号变更需全新进程)。

正确做法:对于 application.yml 的修改,若未自动触发,可手动触发一次重启(在 IDEA 中重新 Build)。或使用 spring.devtools.restart.trigger-file 机制,通过创建/修改触发文件强制重启。最根本的端口等配置变更,仍需完全停止并重新启动 JVM。

面试考点

Q:spring-boot-devtools 的自动重启原理是什么?

devtools 使用两个 ClassLoader:base 加载第三方依赖(不变),restart 加载项目自身类(常变)。当文件系统检测到 classpath 中的类文件变更时,丢弃旧的 restart ClassLoader,创建新的 restart ClassLoader 重新加载业务类,并重新执行 SpringApplication.run()。第三方依赖无需重新加载,因此重启速度远快于冷启动。

Q:为什么 devtools 建议标记为 <optional>true</optional>?

防止 devtools 通过 Maven 传递依赖进入其他模块或生产环境。optional 表示该依赖仅当前模块使用,不会强制传递给依赖本模块的项目。

Q:devtools 会重启内嵌 Tomcat 吗?

不会。Tomcat 由 base ClassLoader 加载,在重启过程中保持存活。restart ClassLoader 只重建 Spring 容器和 DispatcherServlet 的映射关系,Tomcat 本身继续监听原端口。这也是重启速度快的核心原因之一。

Q:LiveReload 和自动重启有什么区别?

自动重启针对 Java 类文件变更,触发 Spring 容器重新初始化。LiveReload 针对静态资源(HTML/CSS/JS)变更,通过浏览器插件或 WebSocket 通知浏览器刷新页面,不涉及服务器重启。两者互补:改后端代码自动重启,改前端代码实时刷新。

上一页
Banner自定义