使用 Spring Boot 开发

本文为官方文档直译版本。原文链接

引言

本节将详细介绍如何使用 Spring Boot。它涵盖了构建系统、自动配置以及如何运行应用程序等主题。我们还将介绍一些 Spring Boot 最佳实践。尽管 Spring Boot 并没有什么特别之处(它只是另一个可以使用的库),但我们还是提出了一些建议,只要遵循这些建议,开发过程就会变得更加轻松。
如果您刚开始使用 Spring Boot,在阅读本节内容之前,最好先阅读入门指南。

构建系统

强烈建议您选择一个支持依赖关系管理的构建系统,该系统可以使用发布到 “Maven Central” 资源库的工件。我们建议您选择 Maven 或 Gradle。Spring Boot 也可以与其他构建系统(例如 Ant)配合使用,但它们的支持并不完善。

依赖管理

Spring Boot 的每个版本都会提供其支持的依赖项的精选列表。实际上,您无需在构建配置中提供这些依赖项的版本,因为 Spring Boot 会为您管理这些依赖项。当您升级 Spring Boot 本身时,这些依赖项也会以一致的方式升级。

如果需要,您仍然可以指定一个版本并覆盖 Spring Boot 的建议。

精选列表包含了可与 Spring Boot 配合使用的所有 Spring 模块,以及第三方库的细化列表。该列表以标准材料清单 (spring-boot-dependencies) 的形式提供,可与 Maven 和 Gradle 一起使用。

Spring Boot 的每个版本都与 Spring Framework 的基础版本相关联。我们强烈建议您不要指定其版本。

Maven

要了解如何将 Spring Boot 与 Maven 结合使用,请参阅 Spring Boot 的 Maven 插件文档:

Gradle

要了解如何将 Spring Boot 与 Gradle 结合使用,请参阅 Spring Boot 的 Gradle 插件文档:

Ant

使用 Apache Ant+Ivy 可以构建 Spring Boot 项目。spring-boot-antlib "AntLib "模块也可帮助 Ant 创建可执行的 jar。
要声明依赖关系,典型的 ivy.xml 文件看起来与下面的示例类似:

<ivy-module version="2.0">
    <info organisation="org.springframework.boot" module="spring-boot-sample-ant" />
    <configurations>
        <conf name="compile" description="everything needed to compile this module" />
        <conf name="runtime" extends="compile" description="everything needed to run this module" />
    </configurations>
    <dependencies>
        <dependency org="org.springframework.boot" name="spring-boot-starter"
            rev="${spring-boot.version}" conf="compile" />
    </dependencies>
</ivy-module>

典型的 build.xml 文件与下面的示例相似:

<project
    xmlns:ivy="antlib:org.apache.ivy.ant"
    xmlns:spring-boot="antlib:org.springframework.boot.ant"
    name="myapp" default="build">

    <property name="spring-boot.version" value="3.2.0" />

    <target name="resolve" description="--> retrieve dependencies with ivy">
        <ivy:retrieve pattern="lib/[conf]/[artifact]-[type]-[revision].[ext]" />
    </target>

    <target name="classpaths" depends="resolve">
        <path id="compile.classpath">
            <fileset dir="lib/compile" includes="*.jar" />
        </path>
    </target>

    <target name="init" depends="classpaths">
        <mkdir dir="build/classes" />
    </target>

    <target name="compile" depends="init" description="compile">
        <javac srcdir="src/main/java" destdir="build/classes" classpathref="compile.classpath" />
    </target>

    <target name="build" depends="compile">
        <spring-boot:exejar destfile="build/myapp.jar" classes="build/classes">
            <spring-boot:lib>
                <fileset dir="lib/runtime" />
            </spring-boot:lib>
        </spring-boot:exejar>
    </target>
</project>

如果不想使用 spring-boot-antlib 模块,请参见 howto.html “How-to”。

Starters

Starters是一组方便的依赖关系描述符,您可以将其包含在应用程序中。您可以一站式获得所需的所有 Spring 及相关技术,而无需翻阅示例代码和复制粘贴大量依赖关系描述符。例如,如果您想开始使用 Spring 和 JPA 访问数据库,请在项目中包含 Spring-boot-starter-data-jpa 依赖关系。
Starters包含了大量的依赖项,这些依赖项可以让项目快速启动和运行,并提供一套一致的、受支持的受管传递依赖项。

名称的含义
所有官方启动程序都遵循类似的命名模式:spring-boot-starter-*,其中 * 表示特定类型的应用程序。这种命名结构的目的是在需要查找启动程序时提供帮助。许多集成开发环境中的 Maven 集成可让您按名称搜索依赖项。例如,如果安装了相应的 Eclipse 或 Spring Tools 插件,就可以在 POM 编辑器中按下 ctrl-space,然后键入 “spring-boot-starter”,查看完整列表。

以下应用程序starters由 Spring Boot 在 org.springframework.boot 组下提供:

名称描述
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 Framework 缓存支持的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将 Spring Data JPA 与 Hibernate 结合使用的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利用 Spring Data Redis 和 Lettuce 客户端使用 Redis 键值数据存储的Starter
spring-boot-starter-data-redis-reactive利用 Spring Data Redis reactive 和 Lettuce 客户端使用 Redis 键值数据存储的Starter
spring-boot-starter-data-rest使用 Spring Data REST 和 Spring MVC 通过 REST 公开 Spring 数据存储库的Starter
spring-boot-starter-freemarker使用 FreeMarker 视图构建 MVC 网络应用程序的Starter
spring-boot-starter-graphql使用 Spring GraphQL 构建 GraphQL 应用程序的Starter
spring-boot-starter-groovy-templates使用 Groovy 模板视图构建 MVC 网络应用程序的Starter
spring-boot-starter-hateoas使用 Spring MVC 和 Spring HATEOAS 构建基于超媒体的 RESTful 网络应用程序的Starter
spring-boot-starter-integration使用 Spring Integration 的Starter
spring-boot-starter-jdbc与 HikariCP 连接池一起使用 JDBC 的Starter
spring-boot-starter-jersey使用 JAX-RS 和 Jersey 构建 RESTful 网络应用程序的Starter。spring-boot-starter-web 的替代方案
spring-boot-starter-jooq使用 jOOQ 通过 JDBC 访问 SQL 数据库的Starter。spring-boot-starter-data-jpaspring-boot-starter-jdbc 的替代方案
spring-boot-starter-json读写 json 的Starter
spring-boot-starter-mail使用 Java Mail 和 Spring Framework 电子邮件发送支持的Starter
spring-boot-starter-mustache使用 Mustache 视图构建网络应用程序的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 用于 Apache Pulsar 的Starter
spring-boot-starter-pulsar-reactive将 Spring 用于 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 网络应用程序的Starter
spring-boot-starter-validation使用 Hibernate 验证器进行 Java Bean 验证的Starter
spring-boot-starter-web使用 Spring MVC 构建网络(包括 RESTful)应用程序的Starter。使用 Tomcat 作为默认嵌入式容器
spring-boot-starter-web-services使用 Spring Web Services 的Starter
spring-boot-starter-webflux使用 Spring Framework 的反应式 Web 支持构建 WebFlux 应用程序的Starter
spring-boot-starter-websocket使用 Spring Framework 的 MVC WebSocket 支持构建 WebSocket 应用程序的Starter

除应用程序启动器外,还可使用以下启动器来添加生产就绪功能:

名称描述
spring-boot-starter-actuator使用 Spring Boot Actuator 的Starter,该Starter提供生产就绪功能,可帮助您监控和管理应用程序

最后,Spring Boot 还包括以下Starter,如果你想排除或交换特定的技术层面,可以使用这些启动器:

名称描述
spring-boot-starter-jetty将 Jetty 用作嵌入式 servlet 容器的Starter。spring-boot-starter-tomcat 的替代方案
spring-boot-starter-log4j2使用 Log4j2 记录日志的Starter。spring-boot-starter-logging 的替代方案
spring-boot-starter-logging使用 Logback 记录日志的启动器。默认日志记录Starter
spring-boot-starter-reactor-netty将 Reactor Netty 用作嵌入式反应式 HTTP 服务器的Starter。
spring-boot-starter-tomcat将 Tomcat 用作嵌入式 servlet 容器的Starter。spring-boot-starter-web 使用的默认服务器容器Starter
spring-boot-starter-undertow将 Undertow 用作嵌入式 servlet 容器的Starter。spring-boot-starter-tomcat 的替代方案

要了解如何交换技术层面,请参阅如何交换网络服务器日志系统的文档。

有关其他社区贡献的启动器列表,请参阅 GitHub 上 spring-boot-starters 模块中的 README 文件

结构化你的代码

Spring Boot 的运行不需要任何特定的代码布局。不过,有一些最佳实践会有所帮助。

如果你希望基于领域来执行结构,不妨看看 Spring Modulith

使用 “默认” 包

当一个类没有包含package声明时,它被认为是在 “default package” 中。一般不鼓励使用 “default package”,应避免使用。它会给使用 @ComponentScan@ConfigurationPropertiesScan@EntityScan@SpringBootApplication 注解的 Spring Boot 应用程序带来特别的问题,因为每个 jar 中的每个类都会被读取。

我们建议您遵循 Java 推荐的软件包命名约定,并使用相反的域名(例如 com.example.project)。

查找主应用程序类

一般来说,我们建议将主程序类放在根包中,高于其他类。@SpringBootApplication注解通常会放在主类中,它隐含地为某些项目定义了一个基础 “搜索包”。例如,如果你正在编写一个 JPA 应用程序,那么 @SpringBootApplication 注解类的包就会被用来搜索 @Entity 项目。使用根包还能让组件扫描只适用于你的项目。

如果不想使用 @SpringBootApplication,它导入的 @EnableAutoConfiguration@ComponentScan 注解也定义了该行为,因此也可以用它们来代替。

下面列出了一个典型的布局:

com
 +- example
     +- myapplication
         +- MyApplication.java
         |
         +- customer
         |   +- Customer.java
         |   +- CustomerController.java
         |   +- CustomerService.java
         |   +- CustomerRepository.java
         |
         +- order
             +- Order.java
             +- OrderController.java
             +- OrderService.java
             +- OrderRepository.java

MyApplication.java 文件将声明 main 方法以及基本的 @SpringBootApplication 方法,如下所示:

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);
    }

}

配置类

Spring Boot 偏好基于 Java 的配置。虽然 SpringApplication 可以与 XML 源一起使用,但我们通常建议您将单个 @Configuration 类作为主要源。通常,定义 main 方法的类适合作为主要 @Configuration 类。

互联网上发布了许多使用 XML 配置的 Spring 配置示例。如果可能,请尽量使用基于 Java 的等效配置。搜索 Enable* 注解是一个很好的起点。

导入更多配置类

您不必将所有 @Configuration 都放在一个类中。@Import 注解可用于导入其他配置类。或者,也可以使用 @ComponentScan 自动获取所有 Spring 组件,包括 @Configuration 类。

导入 XML 配置

如果您绝对必须使用基于 XML 的配置,我们建议您仍从 @Configuration 类开始。然后,您可以使用 @ImportResource 注解来加载 XML 配置文件。

自动配置

Spring Boot 自动配置会尝试根据您添加的 jar 依赖关系自动配置您的 Spring 应用程序。例如,如果 HSQLDB 位于类路径上,而您没有手动配置任何数据库连接 Bean,那么 Spring Boot 就会自动配置内存数据库。
您需要在 @Configuration 类中添加 @EnableAutoConfiguration@SpringBootApplication 注解,选择加入自动配置。

你只能添加一个 @SpringBootApplication@EnableAutoConfiguration 注解。我们通常建议只在主 @Configuration 类中添加其中一个注解。

逐步取代自动配置

自动配置是非侵入式的。在任何时候,您都可以开始定义自己的配置,以取代自动配置的特定部分。例如,如果您添加了自己的 DataSource Bean,默认的嵌入式数据库支持就会退出。
如果需要了解当前应用的自动配置及其原因,请使用 --debug 开关启动应用程序。这样做可以为选定的核心日志记录器启用调试日志,并将条件报告记录到控制台。

禁用特定的自动配置类

如果发现不需要的特定自动配置类被应用,可以使用 @SpringBootApplication 的排除属性来禁用它们,如下例所示:

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class MyApplication {

}

如果类不在类路径上,可以使用注解的 excludeName 属性来指定全限定名。如果你更喜欢使用 @EnableAutoConfiguration 而不是 @SpringBootApplication,也可以使用 excludeexcludeName。最后,你还可以使用 spring.autoconfigure.exclude 属性来控制要排除的自动配置类列表。

您既可以在注释层定义排除项,也可以使用属性定义排除项。

尽管自动配置类是public的,但该类中唯一被视为公共 API 的方面是该类的名称,该名称可用于禁用自动配置。这些类的实际内容(如嵌套配置类或 bean 方法)仅供内部使用,我们不建议直接使用这些内容。

自动配置包

自动配置包是各种自动配置功能在扫描实体和 Spring Data 资源库时默认查找的包。@EnableAutoConfiguration 注解(可直接使用,也可在 @SpringBootApplication 中使用)决定了默认的自动配置包。可使用 @AutoConfigurationPackage 注解配置其他软件包。

Spring Beans 和依赖注入

您可以自由使用任何标准的 Spring Framework 技术来定义您的 Bean 及其注入的依赖关系。我们通常建议使用构造器注入来连接依赖关系,并使用 @ComponentScan 来查找 Bean。
如果你按照上面的建议构建代码(将应用程序类放在顶级包中),你就可以添加 @ComponentScan 而不需要任何参数,或者使用 @SpringBootApplication 注解来隐式地包含它。所有应用程序组件(@Component@Service@Repository@Controller 及其他)都会自动注册为 Spring Bean。
下面的示例展示了一个 @Service Bean,它使用构造器注入来获取所需的 RiskAssessor 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 字段标记为最终字段,表示以后不能更改。

使用 @SpringBootApplication 注解

许多 Spring Boot 开发人员都希望自己的应用程序能使用自动配置、组件扫描,并能在 “application class” 上定义额外的配置。一个 @SpringBootApplication 注解就可以启用这三个功能,即:“自动配置”、"组件扫描 "和 “应用程序类”:

  • @EnableAutoConfiguration:启用 Spring Boot 的自动配置机制
  • @ComponentScan:在应用程序所在的包上启用 @Component 扫描(请参阅最佳实践)
  • @SpringBootConfiguration:启用在上下文中注册额外的 Bean 或导入额外的配置类。它是 Spring 标准 @Configuration 的替代方案,有助于在集成测试中进行配置检测
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// Same as @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-annotated 类和 @ConfigurationProperties-annotated 类不会被自动检测到,而用户定义的 Bean 会被显式导入(请参阅 @Import)。

运行你的应用程序

将应用程序打包为 jar 并使用嵌入式 HTTP 服务器的最大优势之一是,您可以像运行其他应用程序一样运行应用程序。该示例适用于调试 Spring Boot 应用程序。您不需要任何特殊的集成开发环境插件或扩展。

本节仅涉及基于 jar 的打包。如果您选择将应用程序打包为 war 文件,请参阅服务器和 IDE 文档。

从集成开发环境中运行

您可以将 Spring Boot 应用程序作为 Java 应用程序从集成开发环境中运行。不过,您首先需要导入您的项目。导入步骤因集成开发环境和构建系统而异。大多数 IDE 可以直接导入 Maven 项目。例如,Eclipse 用户可以从 “File” 菜单中选择 “Import...Existing Maven Projects”。
如果无法直接将项目导入集成开发环境,可以使用构建插件生成集成开发环境元数据。Maven 包括用于 EclipseIDEA 的插件。Gradle 为各种集成开发环境提供了插件。

如果不小心运行网络应用程序两次,就会出现 “Port already in use” 的错误。Spring Tools 用户可以使用 “重新启动”(Relaunch)按钮而不是 “运行”(Run)按钮来确保关闭任何现有实例。

以包的形式运行

如果使用 Spring Boot Maven 或 Gradle 插件创建可执行 jar,则可以使用 java -jar 运行应用程序,如下例所示:

$ java -jar target/myapplication-0.0.1-SNAPSHOT.jar

也可以在启用远程调试支持的情况下运行打包应用程序。这样做可以将调试器附加到打包应用程序上,如下例所示:

$ java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n \
       -jar target/myapplication-0.0.1-SNAPSHOT.jar

使用 Maven 插件

Spring Boot Maven 插件包含一个run目标(goal),可用于快速编译和运行应用程序。应用程序会以展开(exploded,按博主理解即为非打包、文件夹)形式运行,就像在集成开发环境中一样。下面的示例展示了运行 Spring Boot 应用程序的典型 Maven 命令:

$ mvn spring-boot:run

您可能还想使用 MAVEN_OPTS 操作系统环境变量,如下例所示:

$ export MAVEN_OPTS=-Xmx1024m

使用 Gradle 插件

Spring Boot Gradle 插件还包含一个 bootRun 任务,可用于以展开(exploded,按博主理解即为非打包、文件夹)形式运行应用程序。只要应用 org.springframework.bootjava 插件,就会添加 bootRun 任务,如下示例所示:

$ gradle bootRun

您可能还想使用 JAVA_OPTS 操作系统环境变量,如下例所示:

$ export JAVA_OPTS=-Xmx1024m

热部署

由于 Spring Boot 应用程序是纯 Java 应用程序,因此 JVM 热部署开箱即用。JVM 热部署所能替换的字节码有限。要获得更完整的解决方案,可以使用 JRebel
spring-boot-devtools 模块也支持快速重启应用程序。详情请参阅热部署 “如何操作”

开发工具

Spring Boot 包含一套额外的工具,可让应用程序开发体验更加愉快。spring-boot-devtools 模块可包含在任何项目中,以提供额外的开发时间功能。要包含 devtools 支持,请将模块依赖关系添加到您的构建中,如以下针对 Maven 和 Gradle 的列表所示:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
dependencies {
    developmentOnly("org.springframework.boot:spring-boot-devtools")
}

Devtools 可能会导致类加载问题,尤其是在多模块项目中。诊断类加载问题介绍了如何诊断和解决这些问题。

运行完全打包的应用程序时,开发工具会自动禁用。如果应用程序是从 java -jar 启动的,或者是从特殊的类加载器启动的,那么它将被视为 “生产应用程序”。您可以使用 spring.devtools.restart.enabled 系统属性来控制这一行为。要启用 devtools(无论使用哪个类加载器启动应用程序),请设置 -Dspring.devtools.restart.enabled=true 系统属性。在运行 devtools 会带来安全风险的生产环境中,切勿这样做。要禁用 devtools,请排除依赖关系或设置 -Dspring.devtools.restart.enabled=false 系统属性。

在 Maven 中将依赖关系标记为可选,或在 Gradle 中使用 developmentOnly 配置(如上图所示),可以防止 devtools 被临时应用到使用项目的其他模块中。

重新打包的归档文件默认不包含 devtools。如果要使用某种远程 devtools 功能,则需要将其包含在内。使用 Maven 插件时,将 excludeDevtools 属性设置为 false。使用 Gradle 插件时,配置任务的类路径以包含 developmentOnly 配置

诊断类加载问题

正如 "重启与重载 "部分所述,重启功能是通过使用两个类加载器来实现的。对于大多数应用程序来说,这种方法效果很好。但有时也会导致类加载问题,尤其是在多模块项目中。
要诊断类加载问题是否确实由 devtools 及其两个类加载器引起,请尝试禁用重启。如果问题得到解决,请自定义重启类加载器,使其包含整个项目。

属性默认值

Spring Boot 支持的一些库使用缓存来提高性能。例如,模板引擎会缓存已编译的模板,以避免重复解析模板文件。此外,Spring MVC 还能在提供静态资源时为响应添加 HTTP 缓存标头。
虽然缓存在生产中非常有益,但在开发过程中却可能适得其反,让你无法看到刚刚在应用程序中做出的更改。因此,spring-boot-devtools 默认禁用缓存选项。
缓存选项通常由application.properties文件中的设置来配置。例如,Thymeleaf 提供了 spring.thymeleaf.cache 属性。spring-boot-devtools 模块无需手动设置这些属性,而是在开发时自动应用合理的配置。
下表列出了应用的所有属性:

名称默认值
server.error.include-binding-errorsalways
server.error.include-messagealways
server.error.include-stacktracealways
server.servlet.jsp.init-parameters.developmenttrue
server.servlet.session.persistenttrue
spring.docker.compose.readiness.waitonly-if-started
spring.freemarker.cachefalse
spring.graphql.graphiql.enabledtrue
spring.groovy.template.cachefalse
spring.h2.console.enabledtrue
spring.mustache.servlet.cachefalse
spring.mvc.log-resolved-exceptiontrue
spring.reactor.netty.shutdown-quiet-period0s
spring.template.provider.cachefalse
spring.thymeleaf.cachefalse
spring.web.resources.cache.period0
spring.web.resources.chain.cachefalse

如果不想应用属性默认值,可以在application.properties中将 spring.devtools.add-properties 设置为 false

由于在开发 Spring MVC 和 Spring WebFlux 应用程序时需要更多有关 Web 请求的信息,开发工具建议您启用 Web 日志组的 DEBUG 日志。这将为你提供有关传入请求、处理程序、响应结果和其他细节的信息。如果希望记录所有请求细节(包括潜在的敏感信息),可以打开 spring.mvc.log-request-detailsspring.codec.log-request-details 配置属性。

自动重启

只要类路径上的文件发生变化,使用 spring-boot-devtools 的应用程序就会自动重新启动。在集成开发环境中工作时,这是一项非常有用的功能,因为它能为代码更改提供非常快速的反馈回路。默认情况下,类路径上指向目录的任何条目都会受到监控,以防发生变化。请注意,某些资源(如静态资产和视图模板)不需要重新启动应用程序。

触发重启
由于 DevTools 会监控类路径资源,因此触发重启的唯一方法就是更新类路径。无论您使用的是集成开发环境还是构建插件,都必须重新编译修改后的文件才能触发重启。更新类路径的方式取决于你使用的工具:

  • 在 Eclipse 中,保存修改后的文件会更新类路径并触发重启。
  • 在 IntelliJ IDEA 中,构建项目(Build +→+ Build Project)也有同样的效果。
  • 如果使用编译插件,运行 Maven 的 mvn 编译或 Gradle 的 gradle 编译将触发重启。

如果使用构建插件通过 Maven 或 Gradle 重新启动,则必须将forking设置为启用。如果禁用forking,devtools 使用的隔离应用程序类加载器将无法创建,重启将无法正常运行。

自动重启与 LiveReload 配合使用时效果非常好。详情请参阅 LiveReload 部分。如果使用 JRebel,自动重启将被禁用,转而使用动态类重载。但仍可使用其他 devtools 功能(如 LiveReload 和属性重载)。

DevTools 依靠应用程序上下文的关闭钩子在重启时关闭应用程序。如果禁用了关闭钩子(SpringApplication.setRegisterShutdownHook(false)),它就无法正常工作。

DevTools 需要定制 ApplicationContext 使用的 ResourceLoader。如果您的应用程序已经提供了资源加载器,它将被封装。不支持直接覆盖 ApplicationContext 上的 getResource 方法。

使用 AspectJ 织入时不支持自动重启。

重新启动与重新加载
Spring Boot 提供的重启技术通过使用两个类加载器来工作。不会更改的类(例如,来自第三方 jar 的类)会加载到基础类加载器中。正在开发的类会加载到重启类加载器中。重启应用程序时,重启类加载器会被丢弃,然后创建一个新的类加载器。这种方法意味着应用程序重启通常比 "冷启动 "要快得多,因为基类加载器已经可用并已填充。
如果你发现重启对你的应用程序来说不够快,或者你遇到类加载问题,你可以考虑重载技术,比如 ZeroTurnaround 的 JRebel。这些技术的工作原理是在加载类时对其进行重写,使其更易于重载。

记录条件评估中的变化

默认情况下,每次重新启动应用程序时,都会记录一份显示条件评估 delta 的报告。当您进行添加或删除 Bean 以及设置配置属性等更改时,该报告会显示应用程序自动配置的更改。
要禁用报告记录,请设置以下属性:

spring:
  devtools:
    restart:
      log-condition-evaluation-delta: false
排除资源

某些资源在更改时不一定需要触发重启。例如,Thymeleaf 模板可以就地编辑。默认情况下,更改 /META-INF/maven/META-INF/resources/resources/static/public/templates 中的资源不会触发重启,但会触发实时重新加载。如果要自定义这些排除项,可以使用 spring.devtools.restart.exclude 属性。例如,如果只想排除 /static/public,可以设置以下属性:

spring:
  devtools:
    restart:
      exclude: "static/**,public/**"

如果要保留默认值并添加额外的排除项,请使用 spring.devtools.restart.additional-exclude 属性。

监听更多路径

您可能希望在更改不在类路径上的文件时重启或重新加载应用程序。为此,可使用 spring.devtools.restart.additional-paths 属性配置额外路径,以监控更改。你可以使用前面介绍的 spring.devtools.restart.exclude 属性来控制附加路径下的更改是触发完全重启还是实时重载。

禁用重启

如果不想使用重启功能,可以使用 spring.devtools.restart.enabled 属性将其禁用。在大多数情况下,你可以在 application.properties 中设置该属性(这样做仍会初始化重启类加载器,但不会监控文件更改)。
如果需要完全禁用重启支持(例如,因为它无法与特定库配合使用),则需要在调用 SpringApplication.run(...) 之前将 spring.devtools.restart.enabled System 属性设置为 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);
    }

}
使用触发器文件

如果您使用的集成开发环境会持续编译已更改的文件,那么您可能更希望只在特定时间触发重启。要做到这一点,可以使用 “触发文件”,这是一个特殊的文件,必须在实际触发重启检查时进行修改。

文件的任何更新都会触发检查,但只有在 Devtools 检测到它有事情要做时,才会实际重启。

要使用触发器文件,请将 spring.devtools.restart.trigger-file 属性设置为触发器文件的名称(不包括任何路径)。触发器文件必须出现在类路径的某个位置。
例如,如果你有一个结构如下的项目:

src
+- main
   +- resources
      +- .reloadtrigger

那么,您的trigger-file属性就是:

spring:
  devtools:
    restart:
      trigger-file: ".reloadtrigger"

现在只有在更新 src/main/resources/.reloadtrigger 时才会重启。

您可能希望将 spring.devtools.restart.trigger-file 设置为全局设置,这样所有项目都能以相同方式运行。

有些集成开发环境具有免于手动更新触发器文件的功能。Eclipse Spring ToolsIntelliJ IDEA(Ultimate Edition)都支持这种功能。使用 Spring Tools,你可以使用控制台视图中的 "reload "按钮(只要你的触发器文件命名为 .reloadtrigger)。对于 IntelliJ IDEA,可以按照其文档中的说明进行操作。

自定义重启类加载器

如前文 “重启与重载” 部分所述,重启功能是通过使用两个类加载器来实现的。如果这引起了问题,你可能需要自定义哪个类加载器加载哪个项目。
默认情况下,集成开发环境中任何打开的项目都会使用 “重启” 类加载器加载,而任何普通 .jar 文件都会使用 “基本” 类加载器加载。使用 mvn spring-boot:rungradle bootRun 时也是如此:包含 @SpringBootApplication 的项目使用 “重启” 类加载器加载,其他项目使用 “基本” 类加载器加载。
您可以通过创建 META-INF/spring-devtools.properties 文件,指示 Spring Boot 使用不同的类加载器加载项目的部分内容。spring-devtools.properties 文件可包含以 restart.excluderestart.include 为前缀的属性。include 元素指的是应上拉到 “重启” 类加载器中的项目,而 exclude 元素指的是应下推到 “基本” 类加载器中的项目。该属性的值是一个应用于 classpath 的 regex 模式,如下例所示:

restart:
  exclude:
    companycommonlibs: "/mycorp-common-[\\w\\d-\\.]+\\.jar"
  include:
    projectcommon: "/mycorp-myproj-[\\w\\d-\\.]+\\.jar"

所有属性键都必须是唯一的。只要属性以 restart.include.restart.exclude. 开头,就会被认为是唯一的。

类路径中的所有 META-INF/spring-devtools.properties 都会被加载。你可以在项目中打包文件,也可以在项目使用的库中打包文件。

已知的局限性

对于使用标准 ObjectInputStream 反序列化的对象,重启功能无法正常工作。如果需要反序列化数据,可能需要将 Spring 的 ConfigurableObjectInputStreamThread.currentThread().getContextClassLoader() 结合使用。
遗憾的是,一些第三方库在进行反序列化时没有考虑上下文类加载器。如果发现此类问题,需要向原作者申请修复。

LiveReload

spring-boot-devtools 模块包含一个嵌入式 LiveReload 服务器,可用于在资源发生变化时触发浏览器刷新。LiveReload 浏览器扩展可免费用于 Chrome、Firefox 和 Safari。你可以在所选浏览器的市场或商店中搜索 “LiveReload”,找到这些扩展。
如果不想在应用程序运行时启动 LiveReload 服务器,可以将 spring.devtools.livereload.enabled 属性设置为 false

一次只能运行一个 LiveReload 服务器。启动应用程序前,请确保没有其他 LiveReload 服务器在运行。如果从集成开发环境启动多个应用程序,只有第一个支持 LiveReload。

要在文件更改时触发 LiveReload,必须启用自动重启。

全局设置

$HOME/.config/spring-boot 目录中添加以下任意文件,即可配置全局 devtools 设置:

  • spring-boot-devtools.properties
  • spring-boot-devtools.yaml
  • spring-boot-devtools.yml

添加到这些文件中的任何属性都适用于机器上使用 devtools 的所有 Spring Boot 应用程序。例如,要配置重启始终使用触发器文件,可以在 spring-boot-devtools 文件中添加以下属性:

spring:
  devtools:
    restart:
      trigger-file: ".reloadtrigger"

默认情况下,$HOME 是用户的主目录。要自定义该位置,请设置 SPRING_DEVTOOLS_HOME 环境变量或 spring.devtools.home 系统属性。

如果在 $HOME/.config/spring-boot 中找不到 devtools 配置文件,则会搜索 $HOME 目录根目录中是否存在 .spring-boot-devtools.properties 文件。这样,您就可以与使用不支持 $HOME/.config/spring-boot 位置的旧版 Spring Boot 的应用程序共享 devtools 全局配置。

devtools properties/yaml 文件不支持配置文件。
.spring-boot-devtools.properties 中激活的任何配置文件都不会影响特定的配置文件加载。不支持 YAML 文件和属性文件中的配置文件特定文件名(形式为 spring-boot-devtools-<profile>.properties)和 spring.config.activate.on-profile 文件。

配置文件系统监视器

FileSystemWatcher 的工作原理是以一定的时间间隔轮询类的更改,然后等待预定义的静默期,以确保不再有更改。由于 Spring Boot 完全依赖集成开发环境来编译文件并将其复制到 Spring Boot 可以读取的位置,因此您可能会发现,当 devtools 重新启动应用程序时,某些更改有时无法反映出来。如果您经常发现此类问题,请尝试将 spring.devtools.restart.poll-intervalspring.devtools.restart.quiet-period 参数设置为适合您开发环境的值:

spring:
  devtools:
    restart:
      poll-interval: "2s"
      quiet-period: "1s"

现在,每 2 秒钟就会轮询一次受监控的类路径目录是否有变化,并保持 1 秒钟的静默期,以确保没有其他类变化。

远程应用

Spring Boot 开发人员工具不仅限于本地开发。您还可以在远程运行应用程序时使用多项功能。远程支持是选择性的,因为启用它可能存在安全风险。只有在受信任的网络上运行或使用 SSL 确保安全时,才能启用远程支持。如果这两个选项都不可用,则不应使用 DevTools 的远程支持。绝对不能在生产部署中启用支持。
要启用远程支持,您需要确保重新打包的存档中包含 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 属性。就像任何重要的密码或秘密一样,该值应是唯一且强大的,以防被猜测或暴力破解。
远程 devtools 支持由两部分组成:接受连接的服务器端端点和在集成开发环境中运行的客户端应用程序。当设置了 spring.devtools.remote.secret 属性后,服务器组件就会自动启用。客户端组件必须手动启动。

Spring WebFlux 应用程序不支持远程 devtools。

运行远程客户端程序

远程客户端应用程序可在集成开发环境中运行。您需要运行 org.springframework.boot.devtools.RemoteSpringApplication,其 classpath 应与您连接的远程项目相同。该应用程序的唯一必要参数是它所连接的远程 URL。
例如,如果您使用的是 Eclipse 或 Spring Tools,并且您有一个名为 my-app 的项目已部署到 Cloud Foundry,您将执行以下操作:

  • 从 “Run” 菜单中选择 “Run Configurations…”。
  • 创建一个新的 Java 应用程序 “启动配置”。
  • 浏览 my-app 项目。
  • 使用 org.springframework.boot.devtools.RemoteSpringApplication 作为主类。
  • 在程序参数(或任何远程 URL)中添加 https://myapp.cfapps.io

运行中的远程客户端可能类似于下面的列表:

  .   ____          _                                              __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _          ___               _      \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` |        | _ \___ _ __  ___| |_ ___ \ \ \ \
 \\/  ___)| |_)| | | | | || (_| []::::::[]   / -_) '  \/ _ \  _/ -_) ) ) ) )
  '  |____| .__|_| |_|_| |_\__, |        |_|_\___|_|_|_\___/\__\___|/ / / /
 =========|_|==============|___/===================================/_/_/_/
 :: Spring Boot Remote ::  (v3.2.0)

2023-11-23T13:40:36.387Z  INFO 39306 --- [           main] o.s.b.devtools.RemoteSpringApplication   : Starting RemoteSpringApplication v3.2.0 using Java 17.0.9 with PID 39306 (/Users/myuser/.m2/repository/org/springframework/boot/spring-boot-devtools/3.2.0/spring-boot-devtools-3.2.0.jar started by myuser in /opt/apps/)
2023-11-23T13:40:36.394Z  INFO 39306 --- [           main] o.s.b.devtools.RemoteSpringApplication   : No active profile set, falling back to 1 default profile: "default"
2023-11-23T13:40:36.794Z  INFO 39306 --- [           main] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2023-11-23T13:40:36.823Z  INFO 39306 --- [           main] o.s.b.devtools.RemoteSpringApplication   : Started RemoteSpringApplication in 0.923 seconds (process running for 1.313)

由于远程客户端使用与真实应用程序相同的类路径,因此它可以直接读取应用程序属性。Spring.devtools.remote.secret 属性就是这样被读取并传递给服务器进行身份验证的。

建议始终使用 https:// 作为连接协议,这样可以对流量进行加密,密码也不会被截获。

如果需要使用代理访问远程应用程序,请配置 spring.devtools.remote.proxy.hostspring.devtools.remote.proxy.port 属性。

远程更新

远程客户端以与本地重启相同的方式监控应用程序类路径的变化。任何更新的资源都会推送到远程应用程序,并(在需要时)触发重启。如果您迭代的功能使用的是本地没有的云服务,这将很有帮助。一般来说,远程更新和重启比完整的重建和部署周期要快得多。
在开发速度较慢的环境中,可能会出现静默期不够的情况,类的更改可能会分成几批进行。第一批类更改上传后,服务器会重新启动。由于服务器正在重启,下一批更改无法发送到应用程序。
这种情况通常会在 RemoteSpringApplication 日志中出现警告,提示无法上传某些类,并随之重试。但它也可能导致应用程序代码不一致,以及在上传第一批变更后无法重启。如果经常出现此类问题,请尝试将 spring.devtools.restart.poll-intervalspring.devtools.restart.quiet-period 参数设置为适合您开发环境的值。有关配置这些属性的信息,请参阅 "配置文件系统监视器 "部分。

只有在远程客户端运行时,文件才会受到监控。如果在启动远程客户端之前更改文件,则不会将其推送到远程服务器。

打包你的应用程序到生产

可执行 jar 可用于生产部署。由于它们是独立的,因此也非常适合基于云的部署。
如需获得更多 "生产就绪 "功能,如健康、审计和度量 REST 或 JMX 端点,可考虑添加 spring-boot-actuator。详情请参见 actuator.html