Spring Boot开发初体验
在Java开发的世界里,Spring Boot无疑是近年来最受欢迎的框架之一。它的出现彻底改变了我们构建Java应用的方式,让复杂的配置变得简单,让繁琐的部署变得优雅。
想象一下,你只需要几分钟就能搭建起一个可运行的Web应用,无需编写XML配置文件,无需手动配置Tomcat服务器,这就是Spring Boot带给我们的魅力。
本文将带你深入了解Spring Boot的核心特性和最佳实践。我们会从构建系统的选择开始,一步步探讨自动配置的奥秘,学习如何优雅地组织代码结构,最后掌握应用的运行和部署技巧。
如果你是Spring Boot的新手,建议先阅读开发你的第一个Spring Boot应用程序教程,有了基础体验后再来深入学习本文内容。
# 一、构建系统的选择
在开始Spring Boot之旅之前,我们首先要解决一个基础问题:用什么工具来管理我们的项目?
对于Spring Boot项目,我强烈推荐选择Maven或Gradle这两个现代化的构建工具。它们不仅能自动管理项目依赖,还能无缝对接Maven中央仓库的丰富资源。
虽然Spring Boot理论上也支持Ant等传统构建工具,但说实话,那体验就像是在21世纪还用着上世纪的工具——能用,但绝对不是最优选择。在当前项目复杂度下,没有自动依赖管理的构建工具基本上就是在给自己找麻烦。
# 二、依赖管理的艺术
这是Spring Boot最让人爱不释手的特性之一——智能依赖管理。
# 1、版本管理不再是噩梦
还记得以前管理项目依赖时的痛苦吗?各种版本冲突、兼容性问题让人头疼不已。Spring Boot很好地解决了这个问题:
- 自动版本管理:每个Spring Boot版本都精心测试了一套完整的依赖库组合,确保它们能够完美协作
- 一致性升级:当你升级Spring Boot版本时,所有相关依赖都会同步升级,避免了版本不匹配的问题
- 灵活覆盖:如果确实需要使用特定版本的库,你依然可以手动指定版本来覆盖默认选择
# 2、核心原理
这套机制基于Maven的BOM(Bill of Materials)概念,通过spring-boot-dependencies
这个特殊的POM文件来实现。无论你使用Maven还是Gradle,都能享受到这种便利。
⚠️ 重要提醒:Spring Boot的每个版本都与特定的Spring Framework版本绑定,请不要手动指定Spring Framework的版本,否则可能导致不可预知的问题。
# 三、Starters - 开箱即用的依赖包
如果说依赖管理是Spring Boot的基础特性,那么Starters就是它的杀手锏。
# 1、什么是Starters?
Starters可以理解为"功能套餐"。想要开发Web应用?加个spring-boot-starter-web
就搞定了。需要数据库操作?spring-boot-starter-data-jpa
一键解决。
它的核心理念是:你告诉我想要什么功能,我给你准备好所有相关的依赖。
# 2、为什么Starters这么香?
还记得没有Spring Boot的时代吗?想要搭建一个简单的Web项目,你需要:
- 手动选择Spring MVC的版本
- 找到兼容的Tomcat版本
- 配置JSON处理库
- 添加日志框架
- 确保所有依赖版本互相兼容
现在,一个spring-boot-starter-web
就包含了所有这些,而且保证兼容性。这种体验就像是从拼装电脑变成了买品牌机——省心、高效、可靠。
# 3、传递性依赖的智慧
Starters不仅仅是依赖的简单打包,它们还提供了完整的传递性依赖管理。这意味着当你引入一个Starter时,它会自动带入所有必需的底层依赖库,并且版本都经过精心测试和验证。
# 4、命名规则与识别技巧
# 4.1、官方Starters的命名模式
官方Starters都遵循spring-boot-starter-*
的命名规律,其中*
部分描述了具体的功能领域。这种统一的命名让我们很容易识别和查找需要的依赖。
在现代IDE中,这种命名规则带来了极大的便利。你只需要在依赖编辑器中输入spring-boot-starter
,IDE就会自动提示所有可用的Starters,比手动查文档要高效得多。
# 4.2、第三方Starters的命名约定
为了避免命名冲突,第三方开发者创建的Starters不能以spring-boot
开头。它们通常采用项目名-spring-boot-starter
的格式。
比如阿里巴巴的Druid数据源Starter就叫druid-spring-boot-starter
,MyBatis的Starter叫mybatis-spring-boot-starter
。这种命名方式既避免了冲突,又能清晰地表明它们的归属。
# 5、官方Starters大全
Spring Boot官方提供了丰富的Starters来覆盖各种开发场景。下面按功能分类列出了主要的Starters:
# 5.1、应用程序Starters
名称 | 描述 |
---|---|
spring-boot-starter | 核心Starter,包括自动配置支持、日志记录和YAML |
spring-boot-starter-activemq | 用于使用Apache ActiveMQ进行JMS消息传递的Starter |
spring-boot-starter-amqp | 用于使用Spring AMQP和Rabbit MQ的Starter |
spring-boot-starter-aop | 用于使用Spring AOP和AspectJ进行面向切面编程的Starter |
spring-boot-starter-artemis | 用于使用Apache Artemis进行JMS消息传递的Starter |
spring-boot-starter-batch | 用于使用Spring Batch的Starter |
spring-boot-starter-cache | 用于使用Spring框架缓存支持的Starter |
spring-boot-starter-data-cassandra | 用于使用Cassandra分布式数据库和Spring Data Cassandra的Starter |
spring-boot-starter-data-cassandra-reactive | 用于使用Cassandra分布式数据库和Spring Data Cassandra Reactive的Starter |
spring-boot-starter-data-couchbase | 用于使用Couchbase文档数据库和Spring Data Couchbase的Starter |
spring-boot-starter-data-couchbase-reactive | 用于使用Couchbase文档数据库和Spring Data Couchbase Reactive的Starter |
spring-boot-starter-data-elasticsearch | 用于使用Elasticsearch搜索和分析引擎以及Spring Data Elasticsearch的Starter |
spring-boot-starter-data-jdbc | 用于使用Spring Data JDBC的Starter |
spring-boot-starter-data-jpa | 用于结合Hibernate使用Spring Data JPA的Starter |
spring-boot-starter-data-ldap | 用于使用Spring Data LDAP的Starter |
spring-boot-starter-data-mongodb | 用于使用MongoDB文档数据库和Spring Data MongoDB的Starter |
spring-boot-starter-data-mongodb-reactive | 用于使用MongoDB文档数据库和Spring Data MongoDB Reactive的Starter |
spring-boot-starter-data-neo4j | 用于使用Neo4j图数据库和Spring Data Neo4j的Starter |
spring-boot-starter-data-r2dbc | 用于使用Spring Data R2DBC的Starter |
spring-boot-starter-data-redis | 用于使用Redis键值存储、Spring Data Redis和Lettuce客户端的Starter |
spring-boot-starter-data-redis-reactive | 用于使用Redis键值存储、Spring Data Redis Reactive和Lettuce客户端的Starter |
spring-boot-starter-data-rest | 用于使用Spring Data REST和Spring MVC通过REST暴露Spring Data仓库的Starter |
spring-boot-starter-freemarker | 用于使用FreeMarker视图构建MVC Web应用程序的Starter |
spring-boot-starter-graphql | 用于使用Spring GraphQL构建GraphQL应用程序的Starter |
spring-boot-starter-groovy-templates | 用于使用Groovy模板视图构建MVC Web应用程序的Starter |
spring-boot-starter-hateoas | 用于使用Spring MVC和Spring HATEOAS构建基于超媒体的RESTful Web应用程序的Starter |
spring-boot-starter-integration | 用于使用Spring Integration的Starter |
spring-boot-starter-jdbc | 用于结合HikariCP连接池使用JDBC的Starter |
spring-boot-starter-jersey | 用于使用JAX - RS和Jersey构建RESTful Web应用程序的Starter。是spring-boot-starter-web 的替代方案 |
spring-boot-starter-jooq | 用于使用jOOQ结合JDBC访问SQL数据库的Starter。是spring-boot-starter-data-jpa 或spring-boot-starter-jdbc 的替代方案 |
spring-boot-starter-json | 用于读写JSON的Starter |
spring-boot-starter-mail | 用于使用Java Mail和Spring框架邮件发送支持的Starter |
spring-boot-starter-mustache | 用于使用Mustache视图构建Web应用程序的Starter |
spring-boot-starter-oauth2-authorization-server | 用于使用Spring Authorization Server功能的Starter |
spring-boot-starter-oauth2-client | 用于使用Spring Security的OAuth2/OpenID Connect客户端功能的Starter |
spring-boot-starter-oauth2-resource-server | 用于使用Spring Security的OAuth2资源服务器功能的Starter |
spring-boot-starter-pulsar | 用于使用Spring for Apache Pulsar的Starter |
spring-boot-starter-pulsar-reactive | 用于使用Spring for Apache Pulsar Reactive的Starter |
spring-boot-starter-quartz | 用于使用Quartz调度器的Starter |
spring-boot-starter-rsocket | 用于构建RSocket客户端和服务器的Starter |
spring-boot-starter-security | 用于使用Spring Security的Starter |
spring-boot-starter-test | 用于使用JUnit Jupiter、Hamcrest和Mockito等库测试Spring Boot应用程序的Starter |
spring-boot-starter-thymeleaf | 用于使用Thymeleaf视图构建MVC Web应用程序的Starter |
spring-boot-starter-validation | 用于结合Hibernate Validator使用Java Bean Validation的Starter |
spring-boot-starter-web | 用于使用Spring MVC构建Web(包括RESTful)应用程序的Starter。默认使用Tomcat作为嵌入式容器 |
spring-boot-starter-web-services | 用于使用Spring Web Services的Starter |
spring-boot-starter-webflux | 用于使用Spring框架的响应式Web支持构建WebFlux应用程序的Starter |
spring-boot-starter-websocket | 用于使用Spring框架的MVC WebSocket支持构建WebSocket应用程序的Starter |
# 5.2、生产环境Starters
名称 | 描述 |
---|---|
spring-boot-starter-actuator | 生产监控神器,提供健康检查、指标收集、应用信息等功能 |
# 5.3、技术组件替换Starters
当你需要替换默认组件时,这些Starters会很有用:
名称 | 用途 |
---|---|
spring-boot-starter-jetty | 替换默认的Tomcat,使用Jetty作为Web容器 |
spring-boot-starter-undertow | 替换默认的Tomcat,使用Undertow作为Web容器 |
spring-boot-starter-log4j2 | 替换默认的Logback,使用Log4j2作为日志框架 |
spring-boot-starter-reactor-netty | 用于响应式Web应用的Netty服务器 |
# 6、如何选择合适的Starters?
选择Starters时,建议遵循以下原则:
- 按需选择:只引入真正需要的功能,避免项目臃肿
- 官方优先:优先使用官方Starters,稳定性和维护性更好
- 版本兼容:第三方Starters要确认与当前Spring Boot版本兼容
💡 延伸阅读:想了解更多社区贡献的Starters吗?可以查看GitHub上的官方Starters列表 (opens new window)。
# 四、代码结构设计的最佳实践
虽然Spring Boot对代码结构相当灵活,但良好的项目组织能让开发更高效,维护更轻松。
# 1、为什么要重视代码结构?
想象一下,几个月后你回来维护这个项目,或者新同事要接手你的代码。清晰的代码结构就像是一张好的地图,能让人快速找到想要的功能,理解项目的整体架构。
🔧 进阶技巧:对于大型项目,可以考虑使用Spring Modulith (opens new window)来实现基于领域的模块化结构,让系统边界更加清晰。
# 2、包管理的黄金法则
# 远离"默认包"的陷阱
如果你的Java类没有package
声明,那它就处于所谓的"默认包"中。对于Spring Boot应用来说,这绝对是个不好的选择!
为什么要避免默认包?
Spring Boot的组件扫描机制(@ComponentScan
、@SpringBootApplication
等)在遇到默认包时会变得"贪婪",它会扫描classpath中所有的类,包括第三方JAR包里的类。这不仅会大幅降低启动性能,还可能导致意想不到的组件冲突。
# 正确的包命名方式
遵循Java的包命名约定,使用反向域名格式:
- ✅
com.company.project
- ✅
org.example.demo
- ❌ 无包名(默认包)
- ❌
project
(太简单,容易冲突)
# 3、主应用类的黄金位置
# 为什么要放在根包?
主应用类(带有@SpringBootApplication
注解的类)应该放在项目的根包中,这个位置选择不是随意的,而是有深层次的技术考量:
- 组件扫描边界:
@SpringBootApplication
会以当前类所在的包作为扫描的起点,向下扫描所有子包 - JPA实体发现:如果使用JPA,系统会从这个包开始寻找
@Entity
类 - 配置类加载:各种配置类的自动发现也是从这里开始
# 实际的好处
把主应用类放在根包,就像是给项目设定了一个"总指挥部",所有的组件发现和配置加载都有了明确的起点,既保证了功能的完整性,又避免了意外扫描到项目外的类。
🔄 替代方案:如果你不喜欢
@SpringBootApplication
这个"万能注解",也可以分别使用@SpringBootConfiguration
、@EnableAutoConfiguration
和@ComponentScan
来实现相同的效果,这样能更精细地控制每个功能。
# 4、推荐的项目结构
下面是一个典型的Spring Boot项目布局,这种结构清晰地体现了领域驱动设计的思想:
com.example.myapplication
├── MyApplication.java # 主应用类,项目的入口
├── customer/ # 客户业务模块
│ ├── Customer.java # 实体类
│ ├── CustomerController.java # Web层
│ ├── CustomerService.java # 业务逻辑层
│ └── CustomerRepository.java # 数据访问层
└── order/ # 订单业务模块
├── Order.java
├── OrderController.java
├── OrderService.java
└── OrderRepository.java
# 主应用类的标准写法
package com.example.myapplication;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
# 这种结构的优势
- 领域清晰:每个业务模块独立管理自己的组件
- 职责分离:Controller、Service、Repository各司其职
- 扩展友好:新增业务模块时结构保持一致
- 团队协作:不同开发者可以专注于不同的模块
# 五、配置类的现代化管理
# 1、Java配置 vs XML配置
在Spring Boot的世界里,Java配置已经成为主流选择。相比传统的XML配置,Java配置有着显著的优势:
- 类型安全:编译期就能发现配置错误
- IDE支持:更好的代码提示和重构支持
- 可读性强:配置逻辑更加直观明了
虽然Spring Boot仍支持XML配置,但我强烈建议拥抱Java配置。如果你在网上看到XML配置的例子,不妨搜索对应的@Enable*
注解,通常都能找到等效的Java配置方式。
# 2、配置类的组织策略
# 主配置类的定位
通常情况下,包含main
方法的主应用类就是一个天然的主配置类。由于它已经带有@SpringBootApplication
注解(包含了@Configuration
),所以你可以直接在其中添加Bean定义。
# 模块化配置管理
当配置变得复杂时,不要把所有配置都塞到主类中。更好的做法是:
- 按功能分离:数据库配置、安全配置、缓存配置等分别创建独立的配置类
- 使用
@Import
引入:在主配置类中通过@Import
注解引入其他配置类 - 自动扫描发现:利用
@ComponentScan
自动发现和注册配置类
# 兼容XML配置的平滑迁移
如果你的项目中还有历史遗留的XML配置,可以通过@ImportResource
注解来引入,这样就能实现Java配置和XML配置的混合使用,方便渐进式迁移。
# 六、自动配置的魔法
# 1、什么是自动配置?
这是Spring Boot最引人注目的特性之一。简单来说,它能够"智能地猜测"你想要什么配置,然后自动帮你完成。
比如,当你在项目中加入spring-boot-starter-data-jpa
时,Spring Boot会自动:
- 配置数据源
- 设置JPA实体管理器
- 创建事务管理器
- 如果classpath中有HSQLDB,还会自动配置一个内存数据库
这种"约定优于配置"的理念让开发者能专注于业务逻辑,而不是繁琐的配置工作。
# 2、启用自动配置
自动配置通过@EnableAutoConfiguration
注解启用,而@SpringBootApplication
注解已经包含了它,所以通常你什么都不用做,自动配置就已经生效了。
⚠️ 注意:一个应用中只能有一个
@SpringBootApplication
或@EnableAutoConfiguration
注解,通常放在主配置类上即可。
# 1、逐步替换自动配置
自动配置是非侵入式的。任何时候,你都可以开始定义自己的配置来替换自动配置的特定部分。例如,如果你添加了自己的DataSource
Bean,默认的嵌入式数据库支持就会失效。
如果你想了解当前正在应用哪些自动配置以及原因,可以使用--debug
开关启动应用程序。这样做会为一些核心日志记录器启用调试日志,并将条件报告记录到控制台。
# 2、禁用特定的自动配置类
如果你发现某些不想要的自动配置类正在被应用,可以使用@SpringBootApplication
的exclude
属性来禁用它们,如下例所示:
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class MyApplication {
}
如果该类不在类路径中,你可以使用该注解的excludeName
属性,并指定全限定名。如果你更喜欢使用@EnableAutoConfiguration
而不是@SpringBootApplication
,exclude
和excludeName
属性同样可用。最后,你还可以使用spring.autoconfigure.exclude
属性来控制要排除的自动配置类列表。
提示:你可以在注解级别和使用属性时都定义排除项。
注意:即使自动配置类是public
的,但该类中被视为公共API的唯一部分是可用于禁用自动配置的类名。这些类的实际内容,如嵌套配置类或Bean方法,仅用于内部使用,我们不建议直接使用它们。
# 3、自动配置包
自动配置包是各种自动配置功能在扫描实体和Spring Data仓库等内容时默认查找的包。@EnableAutoConfiguration
注解(直接使用或通过@SpringBootApplication
存在)决定了默认的自动配置包。可以使用@AutoConfigurationPackage
注解配置额外的包。
# 七、Bean与依赖注入的最佳实践
# 1、组件注册与发现
Spring Boot继承了Spring框架强大的依赖注入能力,并且让它变得更加简单易用。
当你按照前面建议的结构组织代码时,@SpringBootApplication
注解会自动启用组件扫描功能,所有标注了以下注解的类都会被自动注册为Spring Bean:
@Component
- 通用组件@Service
- 业务服务层@Repository
- 数据访问层@Controller
- Web控制层
# 2、依赖注入的推荐方式
虽然Spring支持多种依赖注入方式,但构造函数注入是目前公认的最佳实践:
为什么推荐构造函数注入?
- 不可变性:依赖一旦注入就无法更改,提高了线程安全性
- 强制依赖:确保对象创建时所有必需依赖都已提供
- 便于测试:单元测试时可以直接通过构造函数传入模拟对象
以下示例展示了一个使用构造函数注入来获取所需 RiskAssessor
Bean 的 @Service
Bean:
import org.springframework.stereotype.Service;
@Service
public class MyAccountService implements AccountService {
private final RiskAssessor riskAssessor;
public MyAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
}
// ...
}
如果一个 Bean 有多个构造函数,你需要使用 @Autowired
标记你希望 Spring 使用的构造函数:
import java.io.PrintStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MyAccountService implements AccountService {
private final RiskAssessor riskAssessor;
private final PrintStream out;
@Autowired
public MyAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
this.out = System.out;
}
public MyAccountService(RiskAssessor riskAssessor, PrintStream out) {
this.riskAssessor = riskAssessor;
this.out = out;
}
// ...
}
提示:注意使用构造函数注入是如何让 riskAssessor
字段被标记为 final
的,这表明它之后不能被更改。
# 八、使用 @SpringBootApplication
注解
许多 Spring Boot 开发者希望他们的应用程序能够使用自动配置、组件扫描功能,并能够在他们的 “应用程序类” 中定义额外的配置。单个 @SpringBootApplication
注解可以用来启用这三项功能,即:
@EnableAutoConfiguration
:启用 Spring Boot 的自动配置机制@ComponentScan
:启用对应用程序所在包的@Component
扫描@SpringBootConfiguration
:启用在上下文中注册额外的 bean 或导入其他配置类。这是 Spring 标准@Configuration
的替代方案,有助于在集成测试中 检测配置。
以下是 Java 示例:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 等同于 @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
# 九、注意事项
@SpringBootApplication
还提供了别名,用于自定义@EnableAutoConfiguration
和@ComponentScan
的属性。- 这些功能都不是强制要求的,你可以选择用它所启用的任何功能来替换这个单一注解。例如,你可能不想在应用程序中使用组件扫描或配置属性扫描:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Import;
@SpringBootConfiguration(proxyBeanMethods = false)
@EnableAutoConfiguration
@Import({ SomeConfiguration.class, AnotherConfiguration.class })
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
在这个示例中,MyApplication
与其他 Spring Boot 应用程序类似,只是不会自动检测带有 @Component
注解的类和带有 @ConfigurationProperties
注解的类,并且会显式导入用户定义的 bean。
# 十、运行您的应用程序
将应用程序打包为 JAR 并使用嵌入式 HTTP 服务器的最大优势之一是,您可以像运行其他普通 Java 应用程序一样运行它。这同样适用于调试 Spring Boot 应用程序,您不需要任何特殊的 IDE 插件或扩展。
注意: 以下选项最适合在本地开发环境中运行应用程序。
注意: 本节仅涵盖基于 JAR 的打包方式。
# 1、从 IDE 中运行
您可以在 IDE 中将 Spring Boot 应用程序作为 Java 应用程序来运行。不过,首先您需要导入项目。导入步骤会因 IDE 和构建系统的不同而有所差异。大多数 IDE 都可以直接导入 Maven 项目。例如,Eclipse 用户可以从“文件”菜单中选择“导入…”→“现有 Maven 项目”。
如果您无法直接将项目导入到 IDE 中,您可以尝试使用构建插件生成 IDE 元数据。Maven 提供了适用于 Eclipse (opens new window) 和 IDEA (opens new window) 的插件。Gradle 则为 各种 IDE (opens new window) 提供了插件。
提示: 如果您不小心多次运行同一个 Web 应用程序,您会看到“端口已被占用”的错误信息。Spring Tools 用户可以使用“重新启动”按钮而不是“运行”按钮,以确保关闭任何现有的实例。
# 2、作为打包应用程序运行
如果您使用 Spring Boot Maven 或 Gradle 插件创建了可执行 JAR 文件,您可以使用 java -jar
命令来运行应用程序,如下例所示:
$ java -jar target/myapplication-0.0.1-SNAPSHOT.jar
您还可以在启用远程调试支持的情况下运行打包好的应用程序。这样做可以让您将调试器连接到打包后的应用程序,如下例所示:
$ java -agentlib:jdwp=server=y,transport=dt_socket,address=8000,suspend=n \
-jar target/myapplication-0.0.1-SNAPSHOT.jar
# 3、使用 Maven 插件
Spring Boot Maven 插件包含一个 run
目标,可用于快速编译和运行您的应用程序。应用程序将以扩展形式运行,就像在 IDE 中一样。以下是运行 Spring Boot 应用程序的典型 Maven 命令示例:
$ mvn spring-boot:run
您可能还想使用 MAVEN_OPTS
操作系统环境变量,如下例所示:
$ export MAVEN_OPTS=-Xmx1024m
# 4、使用 Gradle 插件
Spring Boot Gradle 插件同样包含一个 bootRun
任务,可用于以扩展形式运行您的应用程序。只要您应用了 org.springframework.boot
和 java
插件,bootRun
任务就会被添加,如下例所示:
$ gradle bootRun
您可能还想使用 JAVA_OPTS
操作系统环境变量,如下例所示:
$ export JAVA_OPTS=-Xmx1024m
# 5、热交换
由于 Spring Boot 应用程序是普通的 Java 应用程序,JVM 热交换应该可以直接使用。不过,JVM 热交换在可以替换的字节码方面存在一定限制。若要获得更完整的解决方案,可以使用 JRebel (opens new window)。
spring-boot-devtools
模块还提供了快速应用重启的支持。
# 十一、开发者工具
Spring Boot 包含一组额外的工具,可让应用程序开发体验更加愉悦。spring-boot-devtools
模块可包含在任何项目中,以提供额外的开发时特性。要包含开发工具支持,可将该模块依赖添加到你的构建中,以下是 Maven 和 Gradle 的示例:
# 1、Maven
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
# 2、Gradle
dependencies {
developmentOnly("org.springframework.boot:spring-boot-devtools")
}
注意:开发工具可能会导致类加载问题,特别是在多模块项目中。诊断类加载问题 一节解释了如何诊断和解决这些问题。
注意:运行完全打包的应用程序时,开发工具会自动禁用。如果你的应用程序是通过 java -jar
启动的,或者是从特殊的类加载器启动的,那么它会被视为“生产应用程序”。你可以使用 spring.devtools.restart.enabled
系统属性来控制此行为。要启用开发工具,无论用于启动应用程序的类加载器如何,都可设置 -Dspring.devtools.restart.enabled=true
系统属性。但在生产环境中不应这样做,因为运行开发工具存在安全风险。要禁用开发工具,可排除依赖项或设置 -Dspring.devtools.restart.enabled=false
系统属性。
提示:在 Maven 中将依赖标记为可选,或在 Gradle 中使用 developmentOnly
配置(如上所示),可防止开发工具被传递应用到使用你的项目的其他模块。
提示:重新打包的归档文件默认不包含开发工具。如果你想使用 某些远程开发工具特性,则需要包含它。使用 Maven 插件时,将 excludeDevtools
属性设置为 false
。使用 Gradle 插件时,配置任务的类路径以包含 developmentOnly
配置 (opens new window)。
# 十二、诊断类加载问题
如 重启与重载对比 一节所述,重启功能是通过使用两个类加载器来实现的。对于大多数应用程序来说,这种方法效果很好。但是,它有时可能会导致类加载问题,特别是在多模块项目中。
要诊断类加载问题是否确实由开发工具及其两个类加载器引起,可 尝试禁用重启。如果这样可以解决问题,则 自定义重启类加载器 以包含整个项目。
# 十三、属性默认值
Spring Boot 支持的几个库使用缓存来提高性能。例如,模板引擎 (opens new window) 会缓存编译后的模板,以避免重复解析模板文件。此外,Spring MVC 在提供静态资源时可以向响应添加 HTTP 缓存头。
虽然缓存在生产环境中非常有益,但在开发过程中可能会适得其反,导致你无法看到对应用程序所做的更改。因此,spring-boot-devtools 默认会禁用缓存选项。
缓存选项通常通过 application.properties
文件中的设置进行配置。例如,Thymeleaf 提供了 spring.thymeleaf.cache
属性。spring-boot-devtools
模块会自动应用合理的开发时配置,而不需要你手动设置这些属性。
以下是所有应用的属性列表:
名称 | 默认值 |
---|---|
server.error.include-binding-errors | always |
server.error.include-message | always |
server.error.include-stacktrace | always |
server.servlet.jsp.init-parameters.development | true |
server.servlet.session.persistent | true |
spring.docker.compose.readiness.wait | only-if-started |
spring.freemarker.cache | false |
spring.graphql.graphiql.enabled | true |
spring.groovy.template.cache | false |
spring.h2.console.enabled | true |
spring.mustache.servlet.cache | false |
spring.mvc.log-resolved-exception | true |
spring.reactor.netty.shutdown-quiet-period | 0s |
spring.template.provider.cache | false |
spring.thymeleaf.cache | false |
spring.web.resources.cache.period | 0 |
spring.web.resources.chain.cache | false |
注意:如果你不希望应用属性默认值,可以在 application.properties
中将 spring.devtools.add-properties
设置为 false
。
在开发 Spring MVC 和 Spring WebFlux 应用程序时,你需要有关 web 请求的更多信息,开发工具建议你为 web
日志组启用 DEBUG
日志记录。这将为你提供有关传入请求、处理请求的处理程序、响应结果和其他详细信息。如果你希望记录所有请求细节(包括潜在的敏感信息),可以打开 spring.mvc.log-request-details
或 spring.codec.log-request-details
配置属性。
# 十四、自动重启
使用 spring-boot-devtools
的应用程序在类路径上的文件发生更改时会自动重启。在 IDE 中工作时,这是一个非常有用的特性,因为它为代码更改提供了非常快速的反馈循环。默认情况下,类路径上指向目录的任何条目都会被监视是否有更改。请注意,某些资源(如静态资产和视图模板)不需要重启应用程序。
# 1、触发重启
由于开发工具监视类路径资源,触发重启的唯一方法是更新类路径。无论你是使用 IDE 还是构建插件之一,修改后的文件都必须重新编译才能触发重启。更新类路径的方式取决于你使用的工具:
- 在 Eclipse 中,保存修改后的文件会导致类路径更新并触发重启。
- 在 IntelliJ IDEA 中,构建项目(
Build -> Build Project
)具有相同的效果。 - 如果使用构建插件,运行 Maven 的
mvn compile
或 Gradle 的gradle build
会触发重启。
注意:如果你使用构建插件通过 Maven 或 Gradle 重启应用程序,必须将 forking
设置为 enabled
。如果禁用分叉,开发工具使用的隔离应用程序类加载器将不会创建,重启将无法正常工作。
注意:开发工具依赖于应用程序上下文的关闭挂钩在重启期间关闭应用程序。如果你禁用了关闭挂钩(SpringApplication.setRegisterShutdownHook(false)
),则它将无法正常工作。
注意:开发工具需要自定义应用程序上下文使用的 ResourceLoader
(请参阅 ResourceLoader (opens new window)),如果你的应用程序已经提供了一个,则会对其进行包装。不支持在应用程序上下文上直接重写 getResource
方法。
注意:使用 AspectJ 织入时不支持自动重启。
# 2、重启与重载对比
Spring Boot 提供的重启技术通过使用两个类加载器来工作。不更改的类(例如,来自第三方 jar 的类)被加载到一个“基础”类加载器中。你正在积极开发的类被加载到一个“重启”类加载器中。当应用程序重启时,“重启”类加载器会被丢弃,并创建一个新的。这种方法意味着应用程序重启通常比“冷启动”快得多,因为“基础”类加载器已经可用并已填充。
如果你发现重启对于你的应用程序来说不够快,或者遇到类加载问题,你可以考虑使用 ZeroTurnaround 提供的 JRebel (opens new window) 等重新加载技术。这些技术通过在类加载时重写类来使它们更易于重新加载。
# 3、记录条件评估的变化
默认情况下,每次应用程序重启时,都会记录一份显示条件评估差异的报告。该报告显示了你对应用程序自动配置所做的更改,例如添加或删除 bean 以及设置配置属性。
要禁用报告的日志记录,请设置以下属性: Properties
spring.devtools.restart.log-condition-evaluation-delta=false
YAML
spring:
devtools:
restart:
log-condition-evaluation-delta: false
# 4、排除资源
某些资源在更改时不一定需要触发重启。例如,可以直接编辑 Thymeleaf 模板。默认情况下,更改 /META-INF/maven
、/META-INF/resources
、/resources
、/static
、/public
或 /templates
中的资源不会触发重启,但会触发 实时重新加载。如果你想自定义这些排除项,可以使用 spring.devtools.restart.exclude
属性。例如,要仅排除 /static
和 /public
,可以设置以下属性:
Properties
spring.devtools.restart.exclude=static/**,public/**
YAML
spring:
devtools:
restart:
exclude: "static/**,public/**"
提示:如果你想保留默认设置并 添加 额外的排除项,可以使用 spring.devtools.restart.additional-exclude
属性。
# 5、监视额外的路径
你可能希望在对不在类路径上的文件进行更改时,应用程序能够重启或重新加载。为此,可以使用 spring.devtools.restart.additional-paths
属性来配置额外的监视路径。你可以使用前面 所述 的 spring.devtools.restart.exclude
属性来控制额外路径下的更改是触发完全重启还是 实时重新加载。
# 6、禁用重启
如果你不想使用重启功能,可以使用 spring.devtools.restart.enabled
属性禁用它。在大多数情况下,你可以在 application.properties
中设置此属性(这样做仍然会初始化重启类加载器,但不会监视文件更改)。
如果你需要 完全 禁用重启支持(例如,因为它与特定库不兼容),则需要在调用 SpringApplication.run(…)
之前将 spring.devtools.restart.enabled
系统属性 (opens new window) 设置为 false
,如下例所示:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(MyApplication.class, args);
}
}
# 7、使用触发文件
如果你使用的 IDE 会持续编译更改的文件,你可能希望仅在特定时间触发重启。为此,你可以使用“触发文件”,这是一个特殊文件,只有在你希望实际触发重启检查时才需要修改它。
注意:对文件的任何更新都会触发检查,但只有当开发工具检测到有需要处理的内容时才会实际重启。
要使用触发文件,请将 spring.devtools.restart.trigger-file
属性设置为触发文件的名称(不包括任何路径)。触发文件必须出现在类路径的某个位置。
例如,如果你的项目具有以下结构:
src
+- main
+- resources
+- .reloadtrigger
那么你的 trigger-file
属性应该是:
Properties
spring.devtools.restart.trigger-file=.reloadtrigger
YAML
spring:
devtools:
restart:
trigger-file: ".reloadtrigger"
现在,只有当 src/main/resources/.reloadtrigger
文件更新时才会发生重启。
提示:你可能希望将 spring.devtools.restart.trigger-file
设置为 全局设置,以便所有项目的行为一致。
某些 IDE 具有一些功能,可以让你无需手动更新触发文件。Eclipse 的 Spring Tools (opens new window) 和 IntelliJ IDEA(旗舰版) (opens new window) 都有这样的支持。使用 Spring Tools 时,你可以从控制台视图使用“重新加载”按钮(前提是你的 trigger-file
名为 .reloadtrigger
)。对于 IntelliJ IDEA,你可以遵循 其文档中的说明 (opens new window)。
# 8、自定义重启类加载器
如前面 重启与重载对比 一节所述,重启功能是通过使用两个类加载器来实现的。如果这导致了问题,你可以使用 spring.devtools.restart.enabled
系统属性诊断问题,如果关闭重启后应用程序可以正常工作,你可能需要自定义哪些内容由哪个类加载器加载。
默认情况下,IDE 中的任何打开项目都会使用“重启”类加载器加载,任何常规 .jar
文件都会使用“基础”类加载器加载。如果你使用 mvn spring-boot:run
或 gradle bootRun
也是如此:包含 @SpringBootApplication
的项目会使用“重启”类加载器加载,其他所有内容都会使用“基础”类加载器加载。应用程序启动时,类路径会打印在控制台上,这有助于识别任何有问题的条目。反射使用的类,特别是注解,可能会在应用程序类使用它们之前在启动时加载到父(固定)类加载器中,这可能导致 Spring 在应用程序中无法检测到它们。
你可以通过创建 META-INF/spring-devtools.properties
文件来指示 Spring Boot 使用不同的类加载器加载项目的部分内容。spring-devtools.properties
文件可以包含以 restart.exclude
和 restart.include
为前缀的属性。include
元素是应该提升到“重启”类加载器的项,exclude
元素是应该降级到“基础”类加载器的项。属性的值是一个正则表达式模式,应用于启动时传递给 JVM 的类路径。以下是一个示例,其中排除了一些本地类文件,并在重启类加载器中包含了一些额外的库:
restart:
exclude:
companycommonlibs: "/mycorp-common-[\\w\\d-\\.]/(build|bin|out|target)/"
include:
projectcommon: "/mycorp-myproj-[\\w\\d-\\.]+\\.jar"
注意:所有属性键必须唯一。只要属性以 restart.include.
或 restart.exclude.
开头,就会被考虑。
提示:会加载类路径中所有的 META-INF/spring-devtools.properties
文件。你可以将文件打包在项目中,或项目使用的库中。不能使用系统属性,只能使用属性文件。
# 9、已知限制
使用标准 ObjectInputStream (opens new window) 反序列化的对象,重启功能可能无法正常工作。如果你需要反序列化数据,可能需要结合使用 Spring 的 ConfigurableObjectInputStream (opens new window) 和 Thread.currentThread().getContextClassLoader()
。
不幸的是,一些第三方库在反序列化时不考虑上下文类加载器。如果你遇到这样的问题,需要向原作者请求修复。
# 十五、LiveReload
spring-boot-devtools
模块包含一个嵌入式 LiveReload 服务器,当资源发生更改时,可用于触发浏览器刷新。LiveReload 浏览器扩展可免费用于 Chrome、Firefox 和 Safari。你可以在所选浏览器的应用商店或扩展市场中搜索“LiveReload”来找到这些扩展。
如果你不想在应用程序运行时启动 LiveReload 服务器,可以将 spring.devtools.livereload.enabled
属性设置为 false
。
注意:一次只能运行一个 LiveReload 服务器。在启动应用程序之前,请确保没有其他 LiveReload 服务器正在运行。如果你从 IDE 启动多个应用程序,只有第一个应用程序支持 LiveReload。
警告:要在文件更改时触发 LiveReload,必须启用 自动重启。
# 十六、全局设置
你可以通过在 $HOME/.config/spring-boot
目录中添加以下任何文件来配置全局开发工具设置:
spring-boot-devtools.properties
spring-boot-devtools.yaml
spring-boot-devtools.yml
添加到这些文件中的任何属性都将应用于你机器上使用开发工具的 所有 Spring Boot 应用程序。例如,要配置重启始终使用 触发文件,可以在 spring-boot-devtools
文件中添加以下属性:
Properties
spring.devtools.restart.trigger-file=.reloadtrigger
YAML
spring:
devtools:
restart:
trigger-file: ".reloadtrigger"
默认情况下,$HOME
是用户的主目录。要自定义此位置,请设置 SPRING_DEVTOOLS_HOME
环境变量或 spring.devtools.home
系统属性。
注意:如果在 $HOME/.config/spring-boot
中未找到开发工具配置文件,会在 $HOME
目录的根目录中搜索是否存在 .spring-boot-devtools.properties
文件。这允许你与使用较旧版本 Spring Boot 的应用程序共享开发工具全局配置,这些旧版本的 Spring Boot 不支持 $HOME/.config/spring-boot
位置。
注意:开发工具属性/ YAML 文件不支持配置文件。在 .spring-boot-devtools.properties
中激活的任何配置文件都不会影响 特定配置文件的加载 (opens new window)。不支持特定配置文件的文件名(格式为 spring-boot-devtools-<profile>.properties
)以及 YAML 和属性文件中的 spring.config.activate.on-profile
文档。
# 1、配置文件系统监视器
FileSystemWatcher
(请参阅 FileSystemWatcher (opens new window))通过以一定的时间间隔轮询类更改来工作,然后等待预定义的安静期,以确保没有更多的更改。由于 Spring Boot 完全依赖于 IDE 来编译和复制文件到 Spring Boot 可以读取它们的位置,你可能会发现在某些时候,开发工具重启应用程序时,某些更改没有得到反映。如果你经常遇到此类问题,可尝试将 spring.devtools.restart.poll-interval
和 spring.devtools.restart.quiet-period
参数增加到适合你开发环境的值:
Properties
spring.devtools.restart.poll-interval=2s
spring.devtools.restart.quiet-period=1s
YAML
spring:
devtools:
restart:
poll-interval: "2s"
quiet-period: "1s"
现在,将每 2 秒轮询一次受监视的类路径目录以检查更改,并保持 1 秒的安静期,以确保没有其他类更改。
# 十七、远程应用程序
Spring Boot 开发工具不限于本地开发。在远程运行应用程序时,你还可以使用一些特性。远程支持是可选的,因为启用它可能存在安全风险。只有在受信任的网络上运行或通过 SSL 进行保护时,才应启用它。如果这些选项都不可用,你不应使用开发工具的远程支持。永远不要在生产部署中启用此支持。
要启用它,你需要确保 devtools
包含在重新打包的归档文件中,如下所示:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludeDevtools>false</excludeDevtools>
</configuration>
</plugin>
</plugins>
</build>
然后,你需要设置 spring.devtools.remote.secret
属性。和任何重要的密码或机密一样,该值应该是唯一且强壮的,以防止被猜测或暴力破解。
远程开发工具支持分为两部分:一个接受连接的服务器端端点和一个在 IDE 中运行的客户端应用程序。当设置 spring.devtools.remote.secret
属性时,服务器组件会自动启用。客户端组件必须手动启动。
注意:Spring WebFlux 应用程序不支持远程开发工具。
# 1、运行远程客户端应用程序
远程客户端应用程序旨在在你的 IDE 中运行。你需要使用与要连接的远程项目相同的类路径来运行 RemoteSpringApplication
(请参阅 RemoteSpringApplication (opens new window))。该应用程序的唯一必需参数是它要连接的远程 URL。
例如,如果你使用的是 Eclipse 或 Spring Tools,并且有一个名为 my-app
的项目部署到了 Cloud Foundry,你可以按以下步骤操作:
- 从
Run
菜单中选择Run Configurations…
。 - 创建一个新的
Java Application
“启动配置”。 - 浏览选择
my-app
项目。 - 使用
RemoteSpringApplication
作为主类。 - 将
https://myapp.cfapps.io
(或你的远程 URL)添加到Program arguments
中。
运行中的远程客户端可能如下所示:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ ___ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | | _ \___ _ __ ___| |_ ___ \ \ \ \
\\/ ___)| |_)| | | | | || (_| []::::::[] / -_) ' \/ _ \ _/ -_) ) ) ) )
' |____| .__|_| |_|_| |_\__, | |_|_\___|_|_|_\___/\__\___|/ / / /
=========|_|==============|___/===================================/_/_/_/
:: Spring Boot Remote :: (v3.4.4)
2025-03-20T13:29:24.223Z INFO 89132 --- [ main] o.s.b.devtools.RemoteSpringApplication : Starting RemoteSpringApplication v3.4.4 using Java 17.0.14 with PID 89132 (/Users/myuser/.m2/repository/org/springframework/boot/spring-boot-devtools/3.4.4/spring-boot-devtools-3.4.4.jar started by myuser in /opt/apps/)
2025-03-20T13:29:24.230Z INFO 89132 --- [ main] o.s.b.devtools.RemoteSpringApplication : No active profile set, falling back to 1 default profile: "default"
2025-03-20T13:29:24.855Z INFO 89132 --- [ main] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2025-03-20T13:29:24.926Z INFO 89132 --- [ main] o.s.b.devtools.RemoteSpringApplication : Started RemoteSpringApplication in 1.763 seconds (process running for 2.509)
注意:由于远程客户端使用的类路径与实际应用程序相同,因此它可以直接读取应用程序属性。这就是 spring.devtools.remote.secret
属性被读取并传递到服务器进行身份验证的方式。
提示:始终建议使用 https://
作为连接协议,以便对流量进行加密,防止密码被拦截。
提示:如果你需要使用代理来访问远程应用程序,请配置 spring.devtools.remote.proxy.host
和 spring.devtools.remote.proxy.port
属性。
# 2、远程更新
远程客户端以与 本地重启 相同的方式监视应用程序类路径的更改。任何更新的资源都会推送到远程应用程序,并(如果需要)触发重启。如果你正在迭代一个使用本地没有的云服务的功能,这会很有帮助。通常,远程更新和重启比完整的重建和部署周期要快得多。
在较慢的开发环境中,安静期可能不够,类的更改可能会分成批次。在上传第一批类更改后,服务器会重启。由于服务器正在重启,下一批更改无法发送到应用程序。
这通常会在 RemoteSpringApplication
(请参阅 RemoteSpringApplication (opens new window))日志中显示为一个警告,提示某些类上传失败并随后进行重试。但这也可能导致应用程序代码不一致,并在上传第一批更改后无法重启。如果你经常遇到此类问题,可尝试将 spring.devtools.restart.poll-interval
和 spring.devtools.restart.quiet-period
参数增加到适合你开发环境的值。
注意:只有在远程客户端运行时才会监视文件。如果在启动远程客户端之前更改了文件,则不会将其推送到远程服务器。
# 十八、为生产环境打包应用程序
当你的 Spring Boot 应用程序准备好进行生产部署时,有多种打包和优化应用程序的方法。请参阅“打包 Spring Boot 应用程序 (opens new window)”部分,以了解这些功能。
如果你还需要诸如健康检查、审计以及指标 REST 或 JMX 端点等“生产就绪”功能,可以考虑添加 spring-boot-actuator
。
祝你变得更强!