# Spring Boot 3 使用

# 构建系统

Spring Boot 建议我们选择 Maven 或 Gradle 等支持依赖管理并且可以发布 artifacts 到 Maven Central 中央仓库的构建系统 。我们也可以让 Spring Boot 与其他构建系统(例如 Ant)一起工作,但它们并没有得到 Spring Boot 很好的支持。

# 依赖管理

Spring Boot 的每个版本都提供了它所支持依赖项的精选列表,我们不需要在构建配置中为这些依赖项提供版本,因为 Spring Boot 会为我们管理。当我们升级 Spring Boot 版本时,这些依赖项也会以一致的方式升级。

如果需要,我们可以通过覆盖自己项目中的属性来覆盖各个依赖项。例如,我们如果要使用不同版本的 SLF4J 和 Spring Data,则需要在项目 POM 文件中添加以下内容:

<properties>
    <slf4j.version>1.7.30</slf4j.version>
    <spring-data-releasetrain.version>Moore-SR6</spring-data-releasetrain.version>
</properties>

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

精选列表包含可以与 Spring Boot 一起使用的所有 Spring 模块以及第三方库,该列表以 spring-boot-dependencies POM 的形式提供。

# Starters

Starters(启动器) 是可以加入到我们应用程序里面的一系列实用的依赖项,无需搜索示例代码和复制粘贴大量的依赖描述就可以获得所需的所有 Spring 和相关技术的一站式商店。比如,我们想使用 Spring 和 JPA 进行数据库访问,就可以将 spring-boot-starter-data-jpa 依赖项加入到我们的项目中。

启动器包含使项目快速启动并运行的一组受管理的、支持传递的依赖。

所有的官方启动器都遵循类似的命名模式:spring-boot-starter-*,其中 * 是特定类型的应用程序,此命名结构旨在帮助我们在需要查找启动器时提供帮助。

第三方的 starter 不应该以 spring-boot 开头,因为它是为官方 Spring Boot 工作保留的,相反,第三方启动器通常以项目名称开头。比如,一个名为 thirdpartyproject 的第三方 starter 项目通常被命名为 thirdpartyproject-spring-boot-starter。

下面列出 Spring Boot 提供在 org.springframework.boot group 下比较常用的一些官方启动器:

Name Description
spring-boot-starter 核心启动器, 包括自动配置支持、日志和 YAML
spring-boot-starter-amqp 使用 Spring AMQP 和 Rabbit MQ 的启动器
spring-boot-starter-aop 使用 Spring AOP 和 AspectJ 的面向切面编程启动器
spring-boot-starter-batch 使用 Spring Batch 批处理的启动器
spring-boot-starter-cache 使用 Spring Framework 缓存支持的启动器
spring-boot-starter-data-elasticsearch 使用 Elasticsearch 搜索引擎 和 Spring Data Elasticsearch 的启动器
spring-boot-starter-data-jdbc 使用 Spring Data JDBC 的启动器
spring-boot-starter-data-jpa 使用 Spring Data JPA 的启动器
spring-boot-starter-data-mongodb 使用面向文档的数据库 MongoDB 和 Spring Data MongoDB 的启动器
spring-boot-starter-data-mongodb-reactive 使用面向文档的数据库 MongoDB 和 Spring Data MongoDB Reactive 的启动器
spring-boot-starter-data-redis 使用 Spring Data Redis 和 Lettuce 客户端 将 Redis key-value 数据存储的启动器
spring-boot-starter-data-redis-reactive 使用 Spring Data Redis reactive 和 Lettuce 客户端 将 Redis key-value 数据存储的启动器
spring-boot-starter-data-rest 使用 Spring Data REST 通过 REST 暴露 Spring Data 存储库的启动器
spring-boot-starter-freemarker 使用 FreeMarker 视图构建 MVC Web 应用程序的启动器
spring-boot-starter-jdbc 使用 JDBC 和 HikariCP 连接池的启动器
spring-boot-starter-json 读写 json 的启动器
spring-boot-starter-mail 支持使用 Java Mail 和 Spring Framework 的电子邮件发送启动器
spring-boot-starter-oauth2-client 使用 Spring Security 的 OAuth2/OpenID Connect 客户端功能的启动器
spring-boot-starter-oauth2-resource-server 使用 Spring Security 的 OAuth2 资源服务器特性的启动器
spring-boot-starter-quartz 使用 Quartz 调度器的启动器
spring-boot-starter-security 使用 Spring Security 的 启动器
spring-boot-starter-test 使用包括 JUnit Jupiter、Hamcrest 和 Mockito 在内的库测试 Spring Boot 应用程序的启动器
spring-boot-starter-thymeleaf 使用 Thymeleaf 视图构建 MVC Web 应用程序的启动器
spring-boot-starter-validation 使用带有 Hibernate Validator 的 Java Bean Validation 的 启动器
spring-boot-starter-web 使用 Spring MVC 构建 Web 应用程序的启动器,包括 RESTful 应用程序,使用 Tomcat 作为默认的嵌入式容器
spring-boot-starter-web-services 使用 Spring Web Services 的启动器
spring-boot-starter-webflux 使用 Spring Framework 的响应式 Web 支持构建 WebFlux 应用程序的启动器
spring-boot-starter-websocket 使用 Spring Framework 的 WebSocket 支持构建 WebSocket 应用程序的启动器
spring-boot-starter-actuator 使用 Spring Boot 的 Actuator 的启动器,它提供了生产就绪功能来帮助监控和管理应用程序
spring-boot-starter-tomcat 使用 Tomcat 作为嵌入式 servlet 容器,是 spring-boot-starter-web 使用的默认 servlet 容器启动器
spring-boot-starter-jetty 使用 Jetty 作为嵌入式 servlet 容器的启动器,一个 spring-boot-starter-tomcat 替代方案
spring-boot-starter-undertow 使用 Undertow 作为嵌入式 servlet 容器的启动器,一个 spring-boot-starter-tomcat 替代方案
spring-boot-starter-reactor-netty 使用 Reactor Netty 作为嵌入式响应式 HTTP 服务器的启动器
spring-boot-starter-logging 使用 Logback 进行日志记录的启动器,默认的日志记录启动器
spring-boot-starter-log4j2 使用 Log4j2 进行日志记录的启动器,一个 spring-boot-starter-logging 的替代方案

# 代码结构

# 包命名

当一个类不包含 package 声明时,它被认为在默认包中。我们通常不鼓励使用默认包,应避免使用,它可能会导致使用了 @ComponentScan、@ConfigurationPropertiesScan、@EntityScan 或 @SpringBootApplication 注解的 Spring Boot 应用程序出现特殊问题,因为每个 jar 中的每个类都会被扫描读取。Spring Boot 建议遵循 Java 推荐的包命名约定并使用反向域名(例如,io.github.xxyopen)。

# 启动类

Spring Boot 建议将应用程序启动类放在其他类之上的根包中。@SpringBootApplication 注解通常放置在启动类上,它隐式地定义了一个基础的搜索包(根包)。使用根包的好处还包括只扫描本项目中的组件。

如果不想使用 @SpringBootApplication 注解,也可以用 @EnableAutoConfiguration 和 @ComponentScan 这两个注解来代替。

以下是一个典型的示例:

io
 +- github
     +- xxyopen   
        +- novel
            +- NovelApplication.java -- 项目启动类
            |
            +- dao
            |   +- BookDao.java 
            |   +- UserDao.java
            | 
            +- service 
            |   +- BookService.java
            |   +- UserService.java
            |  
            +- controller
            |   +- UserController.java
            |   +- UserController.java     
                    

NovelApplication.java 被定义成启动类, 带有 @SpringBootApplication 注解, 如下所示:

@SpringBootApplication
public class NovelApplication {

    public static void main(String[] args) {
        SpringApplication.run(NovelApplication.class, args);
    }

}

# 配置类

Spring Boot 支持基于 Java 的配置,虽然可以使用 XML 来配置 Spring 应用程序,但 SpringBoot 建议我们使用 @Configuration 类来配置我们的应用程序。定义 main 方法的 Spring Boot 启动类通常是一个 @Configuration 主配置类。

我们不需要把所有的配置都放到一个 @Configuration 配置类中,@Import 注解可用于导入其他的配置类。我们还可以使用 @ComponentScan 自动扫描获取所有的 Spring 组件,包括 @Configuration 配置类。

如果我们需要使用基于 XML 的配置,Spring Boot 建议我们仍然从 @Configuration 类开始,然后使用 @ImportResource 注解来加载 XML 配置文件。

以下是一个 Redis 的配置类示例:

@Configuration
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisConfiguration {

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

# 自动配置

Spring Boot 自动配置会根据我们添加的 jar 依赖项自动配置我们的 Spring 应用程序。例如,如果 HSQLDB 在 classpath 类路径上,并且没有手动配置任何数据库连接 bean,那么 Spring Boot 会自动配置一个内存数据库。

自动配置生效的前提是我们需要向 @Configuration 配置类上添加 @EnableAutoConfiguration 或 @SpringBootApplication 注解来开启自动配置,Spring Boot 建议我们添加到主配置类(通常是定义 main 方法的启动类)上。

自动配置是非侵入性的。在任何时候,我们都可以定义自己的配置来替换自动配置的特定部分。例如,如果添加自己的 DataSource bean,则默认的嵌入式数据库支持会替换掉。

如果我们要了解当前正在应用哪些自动配置以及原因,可以使用 --debug 来启动应用程序。这样可以开启调试日志,并将条件报告打印到控制台。

$  mvn spring-boot:run --debug

我们还可以通过使用 @SpringBootApplication 注解的 exclude 属性来禁用特定的自动配置类:

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

}

如果需要禁用的自动配置类不在 classpath 类路径上,我们还可以用注解的 excludeName 属性指定类的完全限定名。我们也可以使用 @EnableAutoConfiguration 而不是 @SpringBootApplication 注解的 exclude 或 excludeName 属性来禁用自动配置类。最后,我们还可以使用 spring.autoconfigure.exclude 配置属性来控制要禁用的自动配置类列表。

# 依赖注入

我们可以使用任何 Spring Framework 标准技术来定义我们的 beans 以及注入它们的依赖。Spring Boot 建议使用构造函数注入和使用 @ComponentScan 扫描 beans。

如果我们将应用程序启动类放在根包中,我们可以添加不带任何参数的 @ComponentScan 或 @SpringBootApplication(隐式包含 @ComponentScan) 注解。这样我们所有的应用程序组件(@Component、@Service、@Repository、@Controller等)都会自动注册为 Spring Bean。

以下是一个使用构造函数向一个 Spring bean 注入所需依赖的例子:

@Service
public class MyAccountService implements AccountService {

    private final RiskAssessor riskAssessor;

    public MyAccountService(RiskAssessor riskAssessor) {
        this.riskAssessor = riskAssessor;
    }

    // ...

}

如果一个 bean 有多个构造函数,需要使用 @Autowired 注解来标记我们想要 Spring 使用哪一个:

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

    // ...

}

注:依赖注入的字段被声明为 final 是为了表明它以后不能再被修改。

# @SpringBootApplication 注解

打开 SpringBootApplication 源文件可以发现单个 @SpringBootApplication 注解由以下三个注解组成:

  • @EnableAutoConfiguration: 启用 Spring Boot 的自动配置机制

  • @ComponentScan :在当前包下开启 @Component 扫描

  • @SpringBootConfiguration: 允许在 Spring 上下文中注册额外的 bean 或导入额外的配置类。Spring 标准注解 @Configuration 的替代方案,可以帮助我们在集成测试中进行配置检测。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "nameGenerator"
    )
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

# 程序运行

  1. 从 IDE 导入代码运行

使用 IntelliJ IDEA 或 Eclipse 等集成开发工具导入项目源码后运行。

  1. 打包应用程序运行

使用 Spring Boot Maven 插件打包源码成可执行 jar 后使用 java -jar 来运行应用程序。

java -jar target/novel-0.0.1-SNAPSHOT.jar

还可以在启用远程调试支持的情况下运行打包的应用程序,这样可以让调试器连接到打包的应用程序

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

  1. 使用 maven 插件运行

Spring Boot Maven 插件包含一个可用于快速编译和运行应用程序的 run 目标,应用程序以分解的形式运行,就像在 IDE 中一样。

$ mvn spring-boot:run

如果想使用 MAVEN_OPTS 操作系统环境变量,可以使用如下命令:

$ export MAVEN_OPTS = -Xmx1024m

# 开发者工具

Spring Boot 包含一组能够提升开发体验的工具 spring-boot-devtools,要获得该工具的支持,需要将以下的依赖添加到 Maven POM 文件中,以提供额外的开发时功能:

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

注:在 Maven 中将依赖项的 optional 标签设置为 true 可防止 devtools 被传递到项目的其它模块中。运行完整打包的应用程序时,开发者工具会自动禁用。

# 诊断类加载问题

重新启动功能是通过使用两个类加载器实现的,对于大多数应用程序来说,这种方法效果很好,但是,它有时会导致类加载问题,尤其是在多模块项目中。

要诊断类加载问题是否确实是由 devtools 及其两个类加载器引起的,可以尝试禁用重新启动功能,看是否能够解决问题,如果是,则需要自定义重启类加载器。

# 属性默认值

Spring Boot 支持的几个库使用缓存来提高性能。例如,缓存已编译的模板引擎以避免重复解析模板文件。Spring MVC 可以在提供静态资源时将 HTTP 缓存标头添加到响应中。

虽然缓存在生产中非常有用,但在开发过程中可能会适得其反,使我们无法实时看到刚刚在应用程序中所做的更改。因此,spring-boot-devtools 默认禁用缓存选项。

缓存选项通常在 application.properties 文件中配置。例如,Thymeleaf 提供了 spring.thymeleaf.cache 属性。spring-boot-devtools 会自动应用合理的开发时配置,无需手动设置这些属性。

下面列出了所有的应用属性:

Name Default Value
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.freemarker.cache false
spring.groovy.template.cache false
spring.h2.console.enabled true
spring.mustache.servlet.cache false
spring.mvc.log-resolved-exception true
spring.reactor.debug true
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 来禁用这些默认属性。

# 自动重启

当 classpath 类路径下的文件改变时,使用 spring-boot-devtools 的应用程序会自动重启,某些资源,例如静态资源和视图模板,不需要重新启动应用程序。

由于 DevTools 监控类路径资源,触发重启的唯一方法是更新类路径。无论我们使用的是 IDE 还是其中的一个构建插件,都必须重新编译修改后的文件以触发重新启动。更新类路径的方式取决于使用的工具:

  • 在 Eclipse 中,保存修改后的文件会导致类路径更新并触发重新启动。

  • 在 IntelliJ IDEA 中,构建项目 ( Build +→+ Build Project) 具有相同的效果。

  • 如果使用构建插件,运行 mvn compile 命令将触发重新启动。

如果不想使用重启功能,可以使用 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 NovelApplication {

    public static void main(String[] args) {
        System.setProperty("spring.devtools.restart.enabled", "false");
        SpringApplication.run(NovelApplication.class, args);
    }

}

# 实时重载

spring-boot-devtools 模块包括一个嵌入式 LiveReload 服务器,可用于在资源更改时触发浏览器刷新。LiveReload 浏览器扩展可从 livereload.com 免费获得,适用于 Chrome、Firefox 和 Safari 。

如果我们不想在应用程序运行时启动 LiveReload 服务器,我们可以将 spring.devtools.livereload.enabled 属性设置为 false.

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

注:一次只能运行一个 LiveReload 服务器,在启动应用程序之前,请确保没有其他 LiveReload 服务器正在运行。如果从 IDE 启动多个应用程序,则只有第一个具有 LiveReload 支持

# 全局设置

我们可以通过将以下任何文件添加到 $HOME/.config/spring-boot 目录来配置全局 devtools 设置:

  1. spring-boot-devtools.properties

  2. spring-boot-devtools.yaml

  3. spring-boot-devtools.yml

添加到这些文件的任何属性都适用于我们机器上使用 devtools 的所有Spring Boot 应用程序。

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

# 远程应用程序

Spring Boot 开发者工具不仅限于本地开发,还可以在运行远程应用程序时使用多种功能,远程支持是可选的,因为启用它可能会带来安全风险。只有在受信任的网络上运行或使用 SSL 保护时才应启用它。

要启用它,需要确保 devtools 包含在重新打包的 jar 中,如以下所示:

<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 支持分两部分提供:接受连接的服务器端端点和在 IDE 中运行的客户端应用程序。设置 spring.devtools.remote.secret 属性时,服务端组件会自动启用,客户端组件必须手动启动。

# 程序部署

可执行 jar 可用于生产部署。由于它们是独立的,因此它们也非常适合于基于云的部署。

对于其它生产就绪功能,例如运行状况检查、审计和 REST 度量 或 JMX 端点等,需要添加 spring-boot-starter-actuator 依赖。

上次更新: a year ago