SpringBoot 官网翻译

自己看着 google 翻译手打的 SpringBoot 的官方文档,希望对初学者有帮助。

快速开始

使用 Spring Boot 可以很容易地构建出能直接运行的、独立的、生产级别的、基于 Spring 的 Java 应用程序。

开发第一个 Spring Boot 应用

让我们使用 Java 开发一个简单的 Hello World! web 应用程序,以便体现 Spring Boot 的一些关键特性。我们将使用 Maven 构建该项目,因为大多数 IDE 都支持它。

创建 POM


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>

    <groupId>com.examplegroupId>
    <artifactId>myprojectartifactId>
    <version>0.0.1-SNAPSHOTversion>

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.6.7version>
    parent>

    

project>

添加 Classpath 依赖

Spring Boot 提供了多个 “Starter”,可以让你方便地将 jar 添加到 classpath 下。我们的示例应用已经在 POM 的 parent 标签使用了 spring-boot-starter-parentspring-boot-starter-parent 是一个特殊 Starter,它提供了有用的 Maven 默认配置。此外它还提供了依赖管理功能,你可以忽略这些依赖的版本标签 version

其他 Starter 只提供在开发特定应用时可能需要用到的依赖。由于我们正在开发一个 web 应用,因此我们将添加一个 spring-boot-starter-web Starter,但在此之前,让我们来看看目前拥有的。

mvn dependency:tree

[INFO] com.example:myproject:jar:0.0.1-SNAPSHOT

mvn dependency:tree 命令以树的形式打印项目的依赖。你可以看到 spring-boot-starter-parent 本身不提供依赖。我们可以在 parent 下方编辑 pom.xml 并添加 spring-boot-starter-web 依赖:

<dependencies>
	<dependency>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-webartifactId>
	dependency>
dependencies>

如果你再次运行 mvn dependency:tree,将会看到现在有许多附加的依赖,包括了 Tomcat web 服务器和 Spring Boot 本身。

主启动类

要完成我们的应用,我们需要创建一个 Java 文件。默认情况下,Maven 将从 src/main/java 目录下编译源代码,因此你需要创建该文件夹结构,之后添加一个名为 src/main/java/MyApplication.java 的文件:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@EnableAutoConfiguration
public class MyApplication {

    @RequestMapping("/")
    String home() {
        return "Hello World!";
    }

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

}
  • 类中的第一个注解是 @RestController,该注解被称为 stereotype 注解。它能为代码阅读者提供一些提示,对于 Spring 而言,这个类具有特殊作用。在本示例中,我们的类是一个 web @Controller,因此 Spring 在处理传入的 web 请求时会使用它。

  • @RequestMapping 注解提供了 routing(路由)信息。它告诉 Spring,任何具有路径为 / 的 HTTP 请求都应映射到 home 方法。@RestController 注解告知 Spring 渲染结果字符串直接返回给调用者。

  • 第二个类级别注解是 @EnableAutoConfiguration。此注解告知 Spring Boot 根据你添加的 jar 依赖来 “猜测” 你想如何配置 Spring 并进行自动配置,由于 spring-boot-starter-web 添加了 Tomcat 和 Spring MVC,auto-configuration(自动配置)将假定你要开发 web 应用并相应设置了 Spring。

  • 应用的最后一部分是 main 方法。这只是一个标准方法,其遵循 Java 规范中定义的应用程序入口点。我们的 main 方法通过调用 run 来委托 Spring Boot 的 SpringApplication 类,SpringApplication 类将引导我们的应用,启动 Spring,然后启动自动配置的 Tomcat web 服务器。我们需要将 MyApplication.class 作为一个参数传递给 run 方法来告知 SpringApplication,它是 Spring 主组件。同时还传递 args 数组以暴露所有命令行参数。

很多 Spring Boot 开发者总是使用 @Configuration@EnableAutoConfiguration@ComponentScan 注解标记在主类上。由于这些注解经常一起使用。Spring Boot 提供了一个更方便的 @SpringBootApplication 注解可用来替代这个组合。

@SpringBootApplication 注解相当于使用 @Configuration@EnableAutoConfiguration@ComponentScan 及他们的默认属性:

package com.example.myapplication;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication // 相当于使用 @Configuration @EnableAutoConfiguration @ComponentScan
public class Application {

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

运行示例

此时,我们的应用应该是可以工作了。由于你使用了 spring-boot-starter-parent POM,因此你可以使用 run 来启动应用程序。在根目录下输入 mvn spring-boot:run 以启动应用:

$ mvn spring-boot:run

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::  (v2.6.7)
....... . . .
....... . . . (log output here)
....... . . .
........ Started MyApplication in 2.222 seconds (JVM running for 6.514)

如果你用浏览器打开了 localhost:8080,你应该会看到以下输出:

Hello World!

使用 Spring Boot

本章节将详细介绍如何使用 Spring Boot。它覆盖了诸如构建系统、自动配置和如何运行应用等主题。我们还介绍一些 Spring Boot 最佳实践。

构建系统

强烈推荐你选择一个支持依赖管理的构建系统,你可以使用它将 artifact 发布到 Maven Central 仓库。我们建议你选择 Maven 或者 Gradle。虽然可以让 Spring Boot 与其他构建系统(如 Ant)配合工作,但它们不会得到特别好的支持。

依赖管理

每一次 Spring Boot 发行都提供了一个它所支持的依赖清单。实际上,你不需要为构建配置提供任何依赖版本,因为 Spring Boot 已经帮你管理这些了。当你升级 Spring Boot 时,这些依赖也将以一致的方式进行升级。

Maven

Maven 用户可以继承 spring-boot-starter-parent 项目以获得合适的默认值,父项目提供了以下功能:

  • Java 1.8 作为默认编译器级别
  • 源代码使用 UTF-8 编码
  • 依赖管理部分继承自 spring-boot-dependencies 的 POM,允许你省略常见依赖的 标签
  • 合理的资源过滤
  • 合适的插件配置
继承 Starter Parent

要将项目配置继承 spring-boot-starter-parent,只需要按以下方式设置 parent


<parent>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starter-parentartifactId>
	<version>2.0.0.RELEASEversion>
parent>

通过该设置,你还可以重写自己项目中的配置属性来覆盖个别依赖。例如,要升级到另一个 Spring Data 发行版本,你需要将以下内容添加到 pom.xml 文件中。

<properties>
	<spring-data-releasetrain.version>Fowler-SR2spring-data-releasetrain.version>
properties>
不使用父 POM

不是每个人都喜欢从 spring-boot-starter-parent 继承 POM。你可能需要使用自己公司标准的父 POM,或者你可能只是希望明确地声明所有 Maven 配置。

如果你不想使用 spring-boot-starter-parent,则仍然可以通过使用 scope=import 依赖来获得依赖管理的好处:

<dependencyManagement>
	<dependencies>
		<dependency>
			
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-dependenciesartifactId>
			<version>2.0.0.RELEASEversion>
			<type>pomtype>
			<scope>importscope>
		dependency>
	dependencies>
dependencyManagement>

如上所述,上述示例设置不会让你使用属性来覆盖个别依赖。要达到相同的目的,需要在 spring-boot-dependencies 项之前在项目的 dependencyManagement 中添加一项。例如,要升级到另一个 Spring Data 发行版,你可以将以下元素添加到 pom.xml 中:

<dependencyManagement>
	<dependencies>
		<!-- 覆盖 Spring Boot 提供的 Spring Data -->
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-releasetrain</artifactId>
			<version>Fowler-SR2</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-dependencies</artifactId>
			<version>2.0.0.RELEASE</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

组织代码

Spring Boot 不需要任何特定的代码布局,但是有一些最佳实践是很有用的。

当一个类没有 package 声明时,它就被认为是在 default 包中。通常不鼓励使用 default 包,应该避免使用。对于使用 @ComponentScan@EntityScan 或者 @SpringBootApplication 注解的 Spring Boot 应用,这样可能会导致特殊问题发生,因为每一个 jar 中的每一个类将会被读取到。

我们通常建议你将主应用类放在其他类之上的根包中。@EnableAutoConfiguration 注解通常放在主类上,它隐式定义了某些项目的包搜索的基准起点。例如,如果你在编写一个 JPA 应用程序,则被 @EnableAutoConfiguration 注解的类所属的包将被用于搜索标记有 @Entity 注解的类。

使用根包还可以允许使用没有指定 basePackage 属性的 @ComponentScan 注解。如果你的主类在根包中,也可以使用 @SpringBootApplication 注解。

以下是一个经典的包结构:

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

Applicatin.java 文件声明了 main 方法,附带了 @Configuration 注解。

package com.example.myapplication;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {

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

}

配置类

Spring Boot 支持基于 Java 的配置。虽然可以在 SpringApplication 中使用 XML 配置源,但我们通常建议主配置源为 @Configuration 类。通常,一个很好的选择是将定义了 main 方法的类作为 @Configuration

导入额外的配置类

你不需要把所有的 @Configuration 放在一个类中。@Import 注解可用于导入其他配置类。或者,你可以使用 @ComponentScan 自动扫描所有 Spring 组件,包括 @Configuration 类。

导入 XML 配置

如果你一定要使用基于 XML 的配置,你可以使用 @ImportResource 注解来加载 XML 配置文件。

自动配置

Spring Boot 自动配置尝试根据你提供的 jar 依赖自动配置 Spring 应用。例如,如果 classpath 下存在 HSQLDB,并且你没有手动配置任何数据库连接 bean,那么 Spring Boot 将自动配置一个内存数据库。

你需要通过将 @EnableAutoConfiguration 或者 @SpringBootApplication 注解添加到其中一个 @Configuration 类之上以启动自动配置。

注意:你应该仅添加一个 @EnableAutoConfiguration 注解。我们通常建议你将其添加到主 @Configuration 类中。

自定义自动配置

自动配置是非侵入的,你可以随时定义自己的配置来代替自动配置的特定部分。例如,如果你添加了自己的 DataSource bean,默认的嵌入式数据库支持将不会自动配置。

如果你需要了解当前正在应用的自动配置,以及为什么使用,请使用 --debug 开关启动应用。这样做可以为核心 logger 启用调试日志,并记录到控制台。

禁用指定的自动配置类

如果你发现正在使用不需要的自动配置类,可以通过使用 @EnableAutoConfigurationexclude 属性来禁用它们。

import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.jdbc.*;
import org.springframework.context.annotation.*;

@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
}

如果类不在 classpath 下,你可以使用注解 excludeName 属性并指定完全类名。此外,你还可以通过 spring.autoconfigure.exclude 属性控制要排除的自动配置类列表。

Spring Bean 与依赖注入

你可以自由使用任何标准的 Spring Framework 技术来定义你的 bean 以及它们注入的依赖。我们发现使用 @ComponentScan 来寻找 bean 和结合 @Autowired 构造器注入可以很好地工作。

如果你按照上述的建议来组织代码,则可以添加无参的 @ComponentScan。所有应用组件(@Component@Service@Repository@Controller等)将自动注册为 Spring Bean。

以下是一个 @Service Bean,其使用构造注入方式获取一个必需的 RiskAssessor bean。

package com.example.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class DatabaseAccountService implements AccountService {

	private final RiskAssessor riskAssessor;

	@Autowired
	public DatabaseAccountService(RiskAssessor riskAssessor) {
		this.riskAssessor = riskAssessor;
	}

	// ...

}

如果 bean 中只有一个构造方法,你可以忽略掉 @Autowired 注解。

@Service
public class DatabaseAccountService implements AccountService {

	private final RiskAssessor riskAssessor;

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

	// ...

}

开发者工具

Spring Boot 包含了一套工具,可以使应用开发体验更加愉快。spring-boot-devtools 模块可包含在任何项目中,以提供额外的开发时功能。要启用 devtools 支持,只需要将模块依赖添加到你的构建配置中即可。

<dependencies>
	<dependency>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-devtoolsartifactId>
		<optional>trueoptional>
	dependency>
dependencies>

使用 spring-boot-devtools 的应用在 classpath 下的文件发生更改时还会自动重启。这对于使用 IDE 工作而言可能是一个非常棒的功能。默认情况下,将监视 classpath 指向的所有文件夹。请注意,某些资源如静态资源和视图模板更改不会重启。

主动触发重启

当 DevTools 监视 classpath 资源时,触发重启的唯一方式是更新 classpath。使 classpath 更新的方式取决于你使用的 IDE。在 Eclipse 中,保存修改的文件将更新 classpath,从而触发重启。在 IntelliJ IDEA 中,构建项目(Build -> Make Project)将产生相同的效果。

热启动

自动重启功能与 LiveReload(实时重载)一起使用效果更棒。如果你使用 JRebel,自动重启将会被禁用,以支持动态类重载,但其他 devtools 功能仍然可以使用。

Spring Boot 通过使用两个类加载器来提供了重启技术。不改变的类(例如,第三方 jar)被加载到 base 类加载器中。经常处于开发状态的类被加载到 restart 类加载器中。

当应用重启时,restart 类加载器将被丢弃,并重启创建一个新的。这种方式意味着应用重启比冷启动要快得多,因为省去了 base 类加载器的处理过程,并且可以直接使用。

条件评估变更日志

默认情况下,每次应用重启时,都会记录显示条件评估增量的报告。该报告展示了在你进行更改时对应用自动配置所作出的更改。

要禁用报告的日志记录,请设置以下属性:

spring.devtools.restart.log-condition-evaluation-delta=false

排除触发文件

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

spring.devtools.restart.exclude=static/**,public/**

Spring Boot 特性

SpringApplication

SpringApplication 类提供了一种可通过运行 main() 方法来启动 Spring 应用的简单方式。多数情况下,你只需要委托给静态的 SpringApplication.run 方法:

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

默认情况下,INFO 会显示日志信息,包括一些相关的启动详细信息,例如启动应用程序的用户。如果你需要 INFO 之外的日志,你可以设置它。spring.main.log-startup-info 设置为 false 可以关闭应用程序日志。

延迟初始化

SpringApplication 允许延迟初始化应用程序。当启用延迟初始化时,bean 会在需要时创建,而不是在应用程序启动期间创建。因此,启用延迟初始化可以减少应用程序启动所需的时间。在 Web 应用程序中,启用延迟初始化将导致许多与 Web 相关的 bean 在收到 HTTP 请求之前不会被初始化。

延迟初始化的一个缺点是它会延迟应用程序问题的发现。如果配置错误的 bean 被延迟初始化,那么在启动过程中将不会发生故障,并且只有在 bean 初始化时问题才会变得明显。还必须注意确保 JVM 有足够的内存来容纳所有应用程序的 bean,而不仅仅是那些在启动期间初始化的 bean。

由于这些原因,默认情况下不启用延迟初始化,建议在启用延迟初始化之前对 JVM 的堆大小进行微调。

SpringApplicationBuilder#lazyInitializationSpringApplication#setLazyInitialization 可以以编程方式开启延迟初始化,或者在配置文件中设置 spring.main.lazy-initialization=true

如果你想禁用某些 bean 的延迟初始化,同时堆应用程序的其余部分使用延迟初始化,你可以使用 @Lazy(false) 注解将其延迟属性显示设置为 false。

如果 SpringApplication 的默认设置不符合你的想法,你可以创建本地实例进行定制化。

public static void main(String[] args) {
	SpringApplication app = new SpringApplication(MySpringConfiguration.class);
	app.setBannerMode(Banner.Mode.OFF);
	app.run(args);
}

多层 ApplicationContext

如果你需要构建一个有层级关系的 ApplicationContext(具有父子关系的多上下文),或者偏向使用 fluent(流式)构建起 API,可以使用 SpringApplicationBuilder

SpringApplicationBuilder 运行你链式调用多个方法,包括能构建出具有层次结构的 parentchild 方法。

可用性

当部署在平台上时,应用程序可以使用 Kubernates Probes 等基础设施向平台提供有关其可用性的信息。Spring Boot 包括对常用 ”liveness“ 和 ”readiness“ 可用性状态的开箱即用支持。如果你使用 Spring Boot 的执行器支持,那么这些状态将作为健康端点组公开。

此外,你还可以通过将 ApplicationAvailability 接口注入到自己的 bean 中来获取可用性状态。

应用程序组件可以随时检索当前的可用性状态,方法是注入 ApplicationAvailability 接口并在其上调用方法。更多时候,应用程序会想要监听状态更新或更新应用程序的状态。

例如,我们可以将应用程序的 ”Readiness“ 状态导出到一个文件中,以便 Kubernetes ”exec Probo“ 可以查看这个文件:

@Component
public class MyReadinessStateExporter {

    @EventListener
    public void onStateChange(AvailabilityChangeEvent<ReadinessState> event) {
        switch (event.getState()) {
        case ACCEPTING_TRAFFIC:
            // create file /tmp/healthy
            break;
        case REFUSING_TRAFFIC:
            // remove file /tmp/healthy
            break;
        }
    }

}

我们还可以在应用中断且无法恢复时更新应用的状态:

@Component
public class MyLocalCacheVerifier {

    private final ApplicationEventPublisher eventPublisher;

    public MyLocalCacheVerifier(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void checkLocalCache() {
        try {
            // ...
        }
        catch (CacheCompletelyBrokenException ex) {
            AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN);
        }
    }

}

事件与监听器

除了常见的 Spring Framework 事件,比如 ContextRefreshedEventSpringApplication 还会发送其他应用程序事件。

注意:在 ApplicationContext 创建之前,实际上触发了一些事件,因此你不能像 @Bean 一样注册监听器。你可以通过 SpringApplication.addListeners(...) 或者 SpringApplicationBuilder.listeners(...) 方法注册它们。

如果你希望无论应用使用何种创建方式都能自动注册这些监听器,你可以将 META-INF/spring.factories文件添加到项目中,并使用 org.springframework.context.ApplicationListener 属性键指向你的监听器。比如:org.springframework.context.ApplicationListener=com.example.project.MyListener

当你运行应用时,应用程序事件将按照如下顺序发送:

  1. 在应用开始运行但还没有进行任何处理时,将发送 ApplicationStartingEvent
  2. Environment 被上下文使用,但是在上下文创建之前,将发送 ApplicationEnvironmentPreparedEvent
  3. ApplicationContext 准备好并调用 ApplicationContextInitializers,当是在加载任何 bean 定义之前,发送 ApplicationContextInitializedEvent
  4. 在开始刷新之前,bean 定义被加载之后发送 ApplicationPreparedEvent
  5. 在上下文刷新之后且所有的应用的命令行运行器(command-line runner)被调用之前发送ApplicationStartedEvent
  6. ReadinessState.CORRECT 被声明后发送 AvailabilityChangeEvent,此时应用程序被认为是存活状态
  7. 在所有 application 和命令行运行器(command-line runner)被调用之后,将发出 ApplicationReadyEvent
  8. ReadinessState.ACCEPTION_TRAFFIC 被声明后发送 AvailabilityChanageEvent 事件,此时应用程序准备处理请求
  9. 如果启用时发生异常,将发送 ApplicationFailedEvent

上面列表只包含了与 SpringApplication 绑定的 SpringApplicationEvent 类型的事件,除此之外,下面两个事件将会被发布,它们在 ApplicationPreparedEvent 之前,ApplicationStartedEvent 之后被触发:

  • WebServer 准备好后触发 WebServerInitializedEvent 事件
  • ApplicationContext 被刷新之后触发 ContextRefreshedEvent 事件

应用程序事件的发送使用了 Spring Framework 的事件发布机制。该部分机制确保在子上下文中发布给监听器的事件也会发布给所有祖先上下文中的监听器。因此,如果你的应用程序使用有层级结构的 SpringApplication 实例,则监听器可能会收到同种类型应用程序事件的多个实例。

为了让监听器能够区分其上下文事件和后代上下文事件,你应该注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。可以通过实现 ApplicationContextAware 来注入上下文,如果监听器是 bean,则使用 @Autowired 注入上下文。

ApplicationContext 类型

SpringApplication 试图为你创建正确类型的 ApplicationContext。确定 WebApplicationType 的算法非常简单:

  • 如果存在 Spring MVC,则使用 AnnotationConfigServletWebServerApplicationContext
  • 如果 Spring MVC 不存在且存在 Spring WebFlux,则使用 AnnotationConfigReactiveWebServerApplicationContext
  • 否则,使用 AnnotatioinConfigApplicationContext

这意味着如果你在同一个应用程序中使用了 Spring MVC 和 Spring WebFlux,默认情况下将使用 Spring MVC。你可以通过调用 setWebApplicationType(WebApplicationType) 修改默认行为。

也可以调用 setApplicationContextClass(...) 来完全控制 ApplicationContext 类型。

访问应用程序参数

如果你需要访问从 SpringAppplication.run(...)传入的应用程序参数,可以注入一个 org.springframework.boot.ApplicationArguments

ApplicationArguments 接口提供了访问原始 String[] 参数以及解析后的 optionno-option 参数的方法:

import org.springframework.boot.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;

@Component
public class MyBean {

	@Autowired
	public MyBean(ApplicationArguments args) {
		boolean debug = args.containsOption("debug");
		List<String> files = args.getNonOptionArgs();
		// if run with "--debug logfile.txt" debug=true, files=["logfile.txt"]
	}

}

Spring Boot 还向 Spring Enviroment 注册了一个 CommandLinePropertySource。在运行你可以使用 @Value 注解注入单个应用参数。

程序启动之前执行

如果你需要在 SpringApplication 启动时运行一些代码,可以实现 ApplicationRunner 或者 CommandLineRunner 接口。这两个接口的工作方式是一样的,都提供了一个单独的 run 方法,它将在 SpringApplication.run(...) 完成之前调用。

CommandLineRunner 接口提供了访问应用程序字符串数组形式参数的方法,而 ApplicationRunner 则使用了上述的 ApplicationArguments 接口。以下示例展示 CommandLineRunnerrun 方法的使用:

import org.springframework.boot.*;
import org.springframework.stereotype.*;

@Component
public class MyBean implements CommandLineRunner {

	public void run(String... args) {
		// Do something...
	}

}

如果你定义了多个 CommandLineRunner 或者 ApplicationRunner bean,则必需指定调用顺序,你可以实现 org.springframework.core.Ordered 接口,也可以使用 org.springframework.core.annotation.Order 注解解决顺序问题。

外部化配置

Spring Boot 可以让你的配置外部化,以便可以在不同环境中使用相同的应用程序代码。你可以使用 properties 文件、YAML 文件、环境变量或者命令行参数来外部化配置。

可以使用 @Value 注解将属性值注解注入到 bean 中,可通过 Spring 的 Environment 方法,或者通过 @ConfigurationProperties 绑定到结构化对象。

Spring Boot 使用了一个非常特别的 PropertySource 指令,用于智能覆盖默认值。属性将按照如下顺序处理:

  1. 默认属性(由 SpringApplication#setDefualtProperties 设置)
  2. @Configuration 类上的 @PropertySource 注解配置的属性源,请注意,这些属性源不会被添加到 Enviroment 直到 application context 被 refresh。
  3. 配置数据(比如 application.properties 文件)
  4. RandomValuePropertySource 中的属性
  5. 操作系统环境变量
  6. Java 系统属性(System.getProperties()
  7. JNDI 属性来自 java:comp/env
  8. ServletContext 的初始化参数
  9. ServletConfig 的初始化参数
  10. SPRING_APPLICATION_JSON 属性
  11. 命令行参数
  12. 测试方法中的 properties 属性
  13. 测试方法中的 TestPropertySource 注解
  14. Devtools 全局配置属性

配置数据文件的加载顺序:

  1. jar 包里面的 application.properties 文件的属性
  2. jar 包里面的指定 profile 的属性文件(application-{profile}.properties
  3. jar 包外部的 application.properties 属性文件
  4. jar 包外部的指定 profile 的属性文件(application-{profile}.properties

访问命令行属性

默认情况下,SpringApplication 将任何命令行选项参数转换为一个 property 并将它们添加到 Spring 的 Environment 中。如前所述,命令行属性始终优先于基于文件的属性源。

如果你不想将命令行属性添加到 Environment 中,可以使用 SpringApplication.setAddCommandLineProperties(false) 禁用它们。

加载外部属性

当你的应用程序启动时,Spring Boot 将自动从以下位置查找并加载 application.properties 或者 application.yaml 文件

  1. 从类路径
    • 根路径 classpath/
    • classpath/config
  2. 从当前目录
    • 当前目录
    • 当前目录中的 /config 子目录
    • 子目录的 /config 直接子目录

附加数据

应用程序属性可以使用 spring.config.import 属性从其他位置导入更多配置数据。

例如,你的类路径 application.properties 文件中可能包含以下内容:

spring.application.name=myapp
spring.config.import=optional:file:./dev.properties

这将触发 dev.properties 当前目录中文件的导入(如果存在这样的文件)。导入的值将优先于触发导入的文件。

类型安全的属性注入

使用 @Value("${property}") 注解注入配置属性有时会很麻烦,特别是当你使用多个属性或你的数据本质上是分层时。Spring Boot 提供了一种使用属性的替代方法,可以让强类型 bean 管理和验证应用程序的配置。

JavaBean 属性绑定

可以绑定声明标准 JavaBean 属性的 bean,如下例所示:

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("my.service")
public class MyProperties {

    private boolean enabled;

    private InetAddress remoteAddress;

    private final Security security = new Security();

    public boolean isEnabled() {
        return this.enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public InetAddress getRemoteAddress() {
        return this.remoteAddress;
    }

    public void setRemoteAddress(InetAddress remoteAddress) {
        this.remoteAddress = remoteAddress;
    }

    public Security getSecurity() {
        return this.security;
    }

    public static class Security {

        private String username;

        private String password;

        private List<String> roles = new ArrayList<>(Collections.singleton("USER"));

        public String getUsername() {
            return this.username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return this.password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public List<String> getRoles() {
            return this.roles;
        }

        public void setRoles(List<String> roles) {
            this.roles = roles;
        }

    }

}

这种方式依赖于默认的空构造函数,并且 getter 和 setter 通常是强制性的。

构造函数绑定

要使用构造函数绑定,必须使用 @EnableConfigurationProperties 或配置属性扫描启动类。你不能对由常规 Spring 机制创建的 bean 使用构造函数绑定。

import java.net.InetAddress;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;

@ConstructorBinding
@ConfigurationProperties("my.service")
public class MyProperties {

    private final boolean enabled;

    private final InetAddress remoteAddress;

    private final Security security;

    public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) {
        this.enabled = enabled;
        this.remoteAddress = remoteAddress;
        this.security = security;
    }

    public boolean isEnabled() {
        return this.enabled;
    }

    public InetAddress getRemoteAddress() {
        return this.remoteAddress;
    }

    public Security getSecurity() {
        return this.security;
    }

    public static class Security {

        private final String username;

        private final String password;

        private final List<String> roles;

        public Security(String username, String password, @DefaultValue("USER") List<String> roles) {
            this.username = username;
            this.password = password;
            this.roles = roles;
        }

        public String getUsername() {
            return this.username;
        }

        public String getPassword() {
            return this.password;
        }

        public List<String> getRoles() {
            return this.roles;
        }

    }

}

在此示例中,@ConstructorBinding 注释用于指示应使用构造函数绑定。这意味着绑定器将期望找到你希望绑定的参数的构造函数。如果你的类有多个构造函数,你可以直接在应该绑定的构造函数上使用 @ConstructorBinding

可以使用 @DefaultValue 在构造函数中给参数指定默认值。

启用类型安全的属性注入

Spring Boot 提供了绑定 @ConfigurationProperties 类型并将它们注册为 bean 的基础设施。你可以逐个类启用配置属性,也可以启用于组件扫描类似的配置属性扫描。

有时,带有 @ConfigurationProperties 的类可能不适合扫描,例如,如果你正在开发自己的自动配置或者你想要有条件地启用它们。在这些情况下,可以使用 @EnableConfigurationProperties 注解指定要处理的类型列表。这可以在任何 @Configuration 类上完成,如下例所示:

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties.class)
public class MyConfiguration {

}

要使用配置属性扫描,请将 @ConfigurationPropertiesScan 注解添加到你的应用程序。通常,它被添加到带有注@SpringBootApplication 注解的主应用程序类中,它也可以添加到任何 @Configuration 类中。默认情况下,扫描将从声明注解的类的包中进行。如果要定义要扫描的特定包,可以按照以下示例进行操作:

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {

}
第三方类配置参数

@ConfigurationProperties 除了用于注释类之外,你还可以在 @Bean 方法上使用它。当你想要将属性绑定到你无法控制的第三方组件时,这样做会特别有用,如为 DataSource 配置参数。

如以下示例所示:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class ThirdPartyConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "another")
    public AnotherComponent anotherComponent() {
        return new AnotherComponent();
    }

}
绑定规则

Spring Boot 使用一些宽松的规则将 Environment 属性绑定到 bean,因此属性名称和 bean 属性名称 @ConfigurationProperties 之间不需要完全匹配。Environment 这很有用的常见示例包括用破折号分隔的环境属性和大小的环境属性。

例如,考虑以下 @ConfigurationProperties 类:

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "my.main-project.person")
public class MyPersonProperties {

    private String firstName;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

}

使用上述代码,可以使用以下属性名称:

  • my.main-project.person.first-name
  • my.main-project.person.firstName:驼峰式语法
  • my.main-project.person.first_name:下划线表示法
  • MY_MAINPROJECT_PERSON_FIRSTNAME:大写格式
绑定 Map

绑定 Map 属性时,你可能需要使用特殊的括号表示法,以便 key 保留原始值。

例如,考虑将以下属性绑定到一个 Map

my.map.[/key1]=value1
my.map.[/key2]=value2
my.map./key3=value3
合并复杂类型

以下示例为MyProperties 中的 List 配置属性:

import java.util.ArrayList;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("my")
public class MyProperties {

    private final List<MyPojo> list = new ArrayList<>();

    public List<MyPojo> getList() {
        return this.list;
    }

}

考虑以下配置:

my.list[0].name=my name
my.list[0].description=my description
#---
spring.config.activate.on-profile=dev
my.list[0].name=my another name

对于 Map 属性,你可以绑定从多个来源提取的属性值。但是,对于多个源中的相同属性,将使用具有最高优先级的属性。以下示例公开了 Map

import java.util.LinkedHashMap;
import java.util.Map;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("my")
public class MyProperties {

    private final Map<String, MyPojo> map = new LinkedHashMap<>();

    public Map<String, MyPojo> getMap() {
        return this.map;
    }

}

考虑以下配置:

my.map.key1.name=my name 1
my.map.key1.description=my description 1
#---
spring.config.activate.on-profile=dev
my.map.key1.name=dev name 1
my.map.key2.name=dev name 2
my.map.key2.description=dev description 2
属性转换

Spring Boot 在绑定到 bean 时尝试将外部应用程序属性强制转换为正确的类型。如果你需要自定义类型转换,你可以提供一个 ConversionService bean(一个名为 conversionService 的 bean) 或自定义属性编辑器(通过一个 CustomEditorConfigurer bean)或自定义 Converters(带有 @ConfigurationPropertiesBinding 的 bean)。

转换持续时间

Spring Boot 专门支持表示持续时间,如果公开 java.time.Duration 属性,则应用程序属性中的以下格式可用:

  • 常规 long 表示
  • 使用的标准 ISO-8601 格式 java.time.Duration
  • 一种更易读的格式,其中值和单位耦合(10s 表示 10 秒)

考虑以下示例:

import java.time.Duration;
import java.time.temporal.ChronoUnit;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;

@ConfigurationProperties("my")
public class MyProperties {

    @DurationUnit(ChronoUnit.SECONDS)
    private Duration sessionTimeout = Duration.ofSeconds(30);

    private Duration readTimeout = Duration.ofMillis(1000);

    public Duration getSessionTimeout() {
        return this.sessionTimeout;
    }

    public void setSessionTimeout(Duration sessionTimeout) {
        this.sessionTimeout = sessionTimeout;
    }

    public Duration getReadTimeout() {
        return this.readTimeout;
    }

    public void setReadTimeout(Duration readTimeout) {
        this.readTimeout = readTimeout;
    }

}

如果你更喜欢使用构造函数绑定,则可以公开相同的属性,如下例所示:

import java.time.Duration;
import java.time.temporal.ChronoUnit;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DurationUnit;

@ConfigurationProperties("my")
@ConstructorBinding
public class MyProperties {

    private final Duration sessionTimeout;

    private final Duration readTimeout;

    public MyProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout,
            @DefaultValue("1000ms") Duration readTimeout) {
        this.sessionTimeout = sessionTimeout;
        this.readTimeout = readTimeout;
    }

    public Duration getSessionTimeout() {
        return this.sessionTimeout;
    }

    public Duration getReadTimeout() {
        return this.readTimeout;
    }

}
转换期间

除了持续时间,Spring Boot 还可以使用 java.time.Period 类型,以下格式可用于应用程序属性:

  • 常规 int 表示
  • 使用的标准 ISO-8601 格式 java.time.Period
  • 一种更简单的格式,其中值和单位耦合(1y3d 表示 1年和3天)
转换数据大小

Spring Framework 有一个 DataSize 值类型,它以字节为单位表示大小。如果公开 DataSize 属性,则应用程序属性中的以下格式可用:

  • 常规 long 表示
  • 一种更易读的格式,其中值和单位是耦合的(10MB 意味着 10 兆字节)

考虑以下示例:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;

@ConfigurationProperties("my")
public class MyProperties {

    @DataSizeUnit(DataUnit.MEGABYTES)
    private DataSize bufferSize = DataSize.ofMegabytes(2);

    private DataSize sizeThreshold = DataSize.ofBytes(512);

    public DataSize getBufferSize() {
        return this.bufferSize;
    }

    public void setBufferSize(DataSize bufferSize) {
        this.bufferSize = bufferSize;
    }

    public DataSize getSizeThreshold() {
        return this.sizeThreshold;
    }

    public void setSizeThreshold(DataSize sizeThreshold) {
        this.sizeThreshold = sizeThreshold;
    }

}

如果你更喜欢使用构造函数绑定,则可以公开相同的属性,如下列所示:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;

@ConfigurationProperties("my")
@ConstructorBinding
public class MyProperties {

    private final DataSize bufferSize;

    private final DataSize sizeThreshold;

    public MyProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize,
            @DefaultValue("512B") DataSize sizeThreshold) {
        this.bufferSize = bufferSize;
        this.sizeThreshold = sizeThreshold;
    }

    public DataSize getBufferSize() {
        return this.bufferSize;
    }

    public DataSize getSizeThreshold() {
        return this.sizeThreshold;
    }

}
属性验证

每当使用 Spring 的 @Validated 注解时,Spring Boot 都会尝试验证类 。你可以直接在配置类上使用 JSR-303 约束注释。为此,请确保你的类路径上存在兼容的 JSR-303 实现,然后将约束注释添加到你的实现,如以下示例所示:

import java.net.InetAddress;

import javax.validation.constraints.NotNull;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@ConfigurationProperties("my.service")
@Validated
public class MyProperties {

    @NotNull
    private InetAddress remoteAddress;

    public InetAddress getRemoteAddress() {
        return this.remoteAddress;
    }

    public void setRemoteAddress(InetAddress remoteAddress) {
        this.remoteAddress = remoteAddress;
    }

}

为确保始终为嵌套属性触发验证,即使未找到任何属性,关联字段也必须使用 @Valid 注解,以下示例基于前面的示例构建 MyProperties

import java.net.InetAddress;

import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@ConfigurationProperties("my.service")
@Validated
public class MyProperties {

    @NotNull
    private InetAddress remoteAddress;

    @Valid
    private final Security security = new Security();

    public InetAddress getRemoteAddress() {
        return this.remoteAddress;
    }

    public void setRemoteAddress(InetAddress remoteAddress) {
        this.remoteAddress = remoteAddress;
    }

    public Security getSecurity() {
        return this.security;
    }

    public static class Security {

        @NotEmpty
        private String username;

        public String getUsername() {
            return this.username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

    }

}

Profiles

Spring Profiles 提供了一个方法分离你应用程序配置的各个部分,并在确定的环境下可用。任何 @Component@Configuration 或者 @ConfigurationProperties 能使用 @Profile 做标记来限制什么时候被加载,如下所示:

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {

    // ...

}

你可以使用 spring.profiles.active Environment 属性来指定哪个 profiles 处于活跃状态。

spring.profiles.active=dev,hsqldb

你也能够通过命令行来指定:--spring.profiles.active=dev.hsqldb

如果没有 profile 处于活跃状态,默认的 profile 将会被开启。默认的 profile 名为 default,并且可以使用 spring.profiles.default Environment 属性进行调整,如以下示例所示:

spring.profiles.default=none

Profile 组

有时候,你在应用程序中定义和使用的配置文件过于细化,使用起来很麻烦。例如,你可能拥有用于独立启用数据库和消息传递功能的配置文件 proddbprodmq

为了解决这个问题,Spring Boot 允许你定义配置文件组。配置文件组允许你为相关的配置文件组定义逻辑名称。

例如,我们可以创建一个 production 由我们的 proddbprodmq 配置文件组成的组。

spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq

现在,我们启动应用程序时,可以指定 --spring.profiles.active=production 来激活 productionproddbprodmq 配置文件。

编程方式设置配置文件

你可以通过在应用程序运行之前调用 SpringApplicatioin.setAdditionalProfiles(...) 来以编程方式设置活动配置文件。也可以使用 Spring 的 ConfigurableEnvironment 激活配置文件。

国际化

Spring Boot 支持本地化消息,以便你的应用程序可以满足不同语言偏好的用户。默认情况下,Spring Boot 在类路径的根目录中查找 message 资源包的存在。

可以使用命名空间配置资源包的基本名称以及其他几个属性 spring.messages,如下例所示:

spring.messages.basename=messages,config.i18n.messages
spring.messages.fallback-to-system-locale=false

任务执行与调度

ThreadPoolTaskExecutor

在上下文中没有 Executor bean 的情况下,Spring Boot 自动配置一个 ThreadPoolTaskExecutor 合理的默认值,这些默认值可以自动关联到异步任务执行(@EnableAsync)和 Spring MVC 异步请求处理。

线程池使用 8 个核心线程,可以根据负载增长和收缩。这些默认设置可以使用 spring.task.execution 进行微调,如以下示例所示:

spring.task.execution.pool.max-size=16
spring.task.execution.pool.queue-capacity=100
spring.task.execution.pool.keep-alive=10s

这将线程池更改为使用有界队列,以便当队列已满(100个任务)时,线程池增加到最多16个线程。由于线程在空闲 10 秒时被回收,因此池的收缩具有侵略性。

ThreadPoolTaskScheduler

ThreadPoolTaskScheduler 能够被自动配置如果需要被 scheduled task execution 关联。线程池默认使用一个线程,可以使用 spring.task.scheduling 进行微调,如下例所示:

spring.task.scheduling.thread-name-prefix=scheduling-
spring.task.scheduling.pool.size=2

如果需要创建自定义执行程序和调度程序,则 TaskExecutorBuilder bean 和 TaskSchedulerBuilder bean 在上下文都可用。

自动配置

如果你在一家开发共享库的公司工作,或者如果你在开源或商业库中工作,你可能想要开发自己的自动配置。自动配置类可以捆绑在外部 jar 中,并且仍然可以被 Spring Boot 拾取。

自动配置可以与提供自动配置代码以及你将使用的典型库的 ”Starter“ 相关联。我们首先介绍了构建你自己的自动配置所需了解的内容,然后我们继续介绍创建自定义 Starter 所需的经典步骤。

自动配置的条件

在底层,自动配置是通过标准 @Configuration 类实现的。附加 @Conditional 注解用于限制何时应用自动配置。通常,自动配置类使用 @ConditionalOnClass@ConditionalOnMissingBean 注解。这确保了自动配置仅在找到相关类并且你没有声明自己的@Configuration类的时候使用。

加载自定配置

Spring Boot 会检查你发布的 jar 中是否存在 META-INF/spring.factories 文件。这个文件应该列出你的配置类,使用 EnableAutoConfiguration 键,如下所示:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mycorp.libx.autoconfigure.LibXAutoConfiguration,\
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration

注意:自动配置只能以这种方式加载,确保它们是在特定的包空间中定义的,并且它们永远不是组件扫描的目标。此外,自动配置类不应启用组件扫描以查找其他组件,使用 @Import 注解来代替查找其他组件。

如果你的配置需要以特定的顺序调用,你可以使用 @AutoConfigureAfter@AutoConfigureBefore 注解。比如,如果你提供了特定于 web 的配置,你的类可能需要在 WebMvcAutoConfiguration 之后被调用。

如果你想排序一些没有任何关联的自动配置,你可以使用 @AutoConfigureOrder 注解,该注解与常规的 @Order 注解有相同的语义,但是为自动配置提供了专用顺序。

与标准 @Configuration 一样,自动配置类被调用的顺序只会影响它们的 beans 被定义的顺序,它们的 beans 随后被创建的顺序没有影响,beans 的创建顺序和 bean 的依赖和一些 @DependsOn 关系。

条件注解

你总是想要包含一个或多个 @Conditional 注解在你的自动配置类中,@ConditionalOnMissingBean 是一个公共例子被用于允许开发者覆盖自动配置如果它们对你的默认配置不满意。

Spring Boot 包含许多 @Conditional 注解,你可以在你的代码中复用它们通过在 @Configuration 类或单独的 @Bean 方法上标注,这些注解包括:

  • Class Conditions
  • Bean Conditions
  • Property Conditions
  • Resource Conditions
  • Web Application Conditions
  • SqEL Expression Conditions
Class Conditions

@ConditionalOnClass@ConditionalOnMissingClass 注解让 @Configuration 类被包含基于指定的类是否存在。由于注解元数据是使用 ASM 解析的,因此你可以使用 value 属性来引用真实的类,即使类可能实际上不会出现在应用程序的类路径中。你也可以使用 name属性来指定一个字符串形式的类名。

这种机制对 @Bean 方法的应用方式不同,在 @Bean 方法中,返回类型通常是条件的目标:在方法上的条件应用之前,JVM 将加载类和可能已处理的方法引用,如果类不存在,这些方法引用将失败。

要处理此场景,可以使用单独的 @Configuration 类来隔离条件,如下例所示:

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
// Some conditions ...
public class MyAutoConfiguration {

    // Auto-configured beans ...

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(SomeService.class)
    public static class SomeServiceConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public SomeService someService() {
            return new SomeService();
        }

    }

}
Bean Conditions

@ConditionalOnBean@ConditionalOnMissingBean 注解通过判断指定的 bean 是否存在决定包含一个 bean。你可以使用 value 属性指定 bean 的类型或者 name 属性指定 bean 的名字。search 属性允许你限制在搜索 bean 时应考虑的 ApplicationContext 层级结构。

当位于 @Bean 方法时,目标类型默认时方法的返回类型,如下例所示:

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public SomeService someService() {
        return new SomeService();
    }

}

在上面的例子中,someService bean 将会被创建,如果在 ApplicationContext 中不存在 SomeService 类型的 bean。

Property Conditions

@ConditionalOnProperty 注解让配置被包含基于一个 Spring Environment property。使用 prefixname 属性来指定应检查的属性。默认情况下,属性存在却不等于 false 将会被匹配。你也能创建更多的高级检查通过使用 havingVlauematchIfMissing 属性。

Resource Conditions

@ConditionalOnResource 注解让配置被包含基于一个指定的资源是否存在。资源能够被指定使用通常的 Spring 约定。如下例所示:

file:/home/user/test.dat

Web Application Conditions

@ConditionalOnWebApplication@ConditionalOnNotWebApplication 注解让配置被包含依赖于应用程序是否是一个 ”web application“,基于 Servlet 的 web 应用程序使用 Spring WebApplicationContext,定义一个 Session socpe,或者有一个 ConfigurableWebEnvironment。reactive web 应用程序使用 ReactiveWebApplication,或者有一个 ConfigurableReactiveWebEnvironment

@ConditionalOnWarDeployment 注解让配置被包含基于应用程序是否是一个传统的 WAR 应用程序部署在一个容器中,如如果应用程序是运行在内嵌服务中则条件不会匹配。

SqEL Expression Conditions

@ConditionalOnExpression 注解让配置被包含基于一个 SpEL expression 的结果。

测试自动配置

自动配置能够被很多因素影响:用户的配置(@Bean 定义和 Environment 定制化),条件评估(一个特殊的 library 存在)等等。

具体来说,每个测试应该创建一个良好的 ApplicationContext 定义代表所有定制化的组合。ApplicationContextRunner 提供一个很好的方法来完成它。

ApplicationContextRunner 经常被定义为测试类的一个字段来收集基础公共的配置。下面例子确保了 MyServiceAutoConfiguration 一定被调用。

private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
        .withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration.class));

每一个测试能使用 runner 来代表一个特殊的用例。例如,下面的示例调用用户配置(UserConfiguration)并检查自动配置是否正确退出。

@Test
void defaultServiceBacksOff() {
    this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> {
        assertThat(context).hasSingleBean(MyService.class);
        assertThat(context).getBean("myCustomService").isSameAs(context.getBean(MyService.class));
    });
}

@Configuration(proxyBeanMethods = false)
static class UserConfiguration {

    @Bean
    MyService myCustomService() {
        return new MyService("mine");
    }

}

这是更容易定制化 Environment,如下所示:

@Test
void serviceNameCanBeConfigured() {
    this.contextRunner.withPropertyValues("user.name=test123").run((context) -> {
        assertThat(context).hasSingleBean(MyService.class);
        assertThat(context.getBean(MyService.class).getName()).isEqualTo("test123");
    });
}

这个 runner 也能被用于展示 ConditionEvaluationReport,报告能够被打印在 INFO 或者 DEBUG 水平。下面例子展示了如何使用 ConditionEvaluationReportLoggingListener 来打印自动配置测试的报告。

import org.junit.jupiter.api.Test;

import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;

class MyConditionEvaluationReportingTests {

    @Test
    void autoConfigTest() {
        new ApplicationContextRunner()
            .withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.INFO))
            .run((context) -> {
                    // Test something...
            });
    }

}

还可以测试在运行时不存在特定类和包时会发生什么。Spring Boot 附带一个 FilteredClassLoader 能够很容易通过 runner 使用。如下所示,我们断言如果 MyService 不存在,自动配置可能会失效:

@Test
void serviceIsIgnoredIfLibraryIsNotPresent() {
    this.contextRunner.withClassLoader(new FilteredClassLoader(MyService.class))
            .run((context) -> assertThat(context).doesNotHaveBean("myService"));
}

创建 Starter

一个经典的 Spring Boot starter 包含自动配置和自定义给定技术的基础设置的代码,我们称之为 “acme”。为了使其易于扩展,可以将装用命名空间中的许多配置键暴露给环境。最后,提供了一个 “starter” 依赖项来帮助用户尽可能轻松的开始。

具体来说,自定义启动器可以包含以下内容:

  • autoconfigure 模块,包含自动配置中 ”acme“ 的代码
  • starter 模块,提供对 ”autoconfigure“ 模块的依赖关系,以及 ”acme“ 和任何其他通常有用的依赖关系。简而言之,添加 starter 应该提供开始使用该库所需的一切

两个模块的这种分离是没有必要的。如果 ”acme“ 有几种风格、选项或可选功能,那么最好将自动配置分开,因为你可以清除的表达某种功能是可选的。此外,你还可以设计一个 starter,提供关于这些可选依赖项的意见。同时,其他人只能依靠自动配置模块,用不同的观点设计自己的启动器。

如果自动配置相对简单,并且没有可选功能,那么在启动器中合并两个模块肯定是一个选项。

命名

你应该确保提供一个合适的命名空间为你的 starter。不要开始你的模块名用 spring-boot,即使你使用不同的 Maven groupId。我们可能提供官方支持为你自动配置的东西在将来。

作为首条规则,你应该命名一个组合模块在 starter 之后。比如,假如你为 ”acme“ 创建了一个 starter,你命名 auto-configure 模块为 acme-spring-boot,然后 starter 为 acme-spring-boot-starter。如果你只有一个模块组合这两个,则命名为 acme-spring-boot-starter

配置键

如果你的 starter 提供配置键,为它们提供一个唯一的命名空间。特别的,不要包含你的键在 Spring Boot 使用的命名空间(比如 servermanagementspring 等等)。如果你使用相同的命名空间,我们可能修改这些命名空间在将来在不同的方式,这将破坏你的模块。作为顶层的规则,你所有的键前缀使用你自己的命名空间。

确保每一个配置键被记录通过附加字段 javadoc,如下所示:

import java.time.Duration;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("acme")
public class AcmeProperties {

    /**
     * Whether to check the location of acme resources.
     */
    private boolean checkLocation = true;

    /**
     * Timeout for establishing a connection to the acme server.
     */
    private Duration loginTimeout = Duration.ofSeconds(3);

    public boolean isCheckLocation() {
        return this.checkLocation;
    }

    public void setCheckLocation(boolean checkLocation) {
        this.checkLocation = checkLocation;
    }

    public Duration getLoginTimeout() {
        return this.loginTimeout;
    }

    public void setLoginTimeout(Duration loginTimeout) {
        this.loginTimeout = loginTimeout;
    }

}

这里有一些规则我们需要遵守以确保描述是一致的:

  • 描述不要用 TheA 开始
  • 对于 boolean 类型,描述使用 Whether 或者 Enable 开头
  • 基于 collection 的类型,描述使用 ”Comma-separated list“
  • 使用 java.time.Duration 而不是 long,描述默认的单位如果不是毫秒,比如 ”If a duration suffix is not specified,seconds will be used“
  • 在描述中不要提供默认值,除非它是在运行时被决定的

确保触发元数据生成,以便 IDE 协助也可用于 key。你可能需要查看生成的元数据(META-INF/spring configuration metadata.json),以确保你的密钥得到了正确的记录。在兼容的 IDE 中使用自己的 starter 也是验证元数据质量的一个好主意。

autoconfigure 模块

autoconfigure 模块包含任何东西,哪些需要通过 library 在开始时得到的。它也可能会包含配置键定义(ConfigurationProperties)和任何回调接口能被用于自定义组件如何初始化。

Spring Boot 使用注解处理器来收集元数据文件(META-INF/spring-autoconfigure-metadata.properties)中的自动配置条件。如果文件存在,它将用于急切地过滤不匹配地自动配置,这将缩短启动时间。建议在包含自动配置地模块中添加以下依赖项:

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-autoconfigure-processorartifactId>
    <optional>trueoptional>
dependency>

如果你在应用程序中直接定义了自动配置,请确保配置 spring-boot-maven-plugin 以防止 repackage glob 将依赖项添加到 jar 中:

<project>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.springframework.bootgroupId>
                            <artifactId>spring-boot-autoconfigure-processorartifactId>
                        exclude>
                    excludes>
                configuration>
            plugin>
        plugins>
    build>
project>
Starter 模块

Starter 实际上是一个空 jar,它的唯一目的是提供必要的依赖项以使用该库。你可以将其视为对入门所需内容的固执已见。

不要对添加 Starter 的项目做出假设。如果你要自动配置的库通常需要其他启动器,请同时提及它们。如果可选依赖项的数量很高,则提供一组适当的默认依赖项可能会很困难,因为你应该避免包含对于库的典型使用而言不必要的依赖。换句话说,你不应该包含可选依赖项。

Web

静态资源

Spring Boot 默认将 / 所有访问映射到以下目录:

classpath:/static
classpath:/public
classpath:/resources
classpath:/META-INF/resources

Servlet Web 应用程序

如果你想要构建基于 servlet 的 web 应用程序,你可以体会到 Spring Boot 自动配置对于 Spring MVC 或者 Jersey 的好处。

Spring Web MVC Framework 是一个丰富的 model view controller web 框架,Spring MVC 让你创建特殊的 @Controller 或者 @RestController beans 来处理进入的 HTTP 请求。通过使用 @RequestMapping 注解,controller 中的方法被映射到 HTTP 请求。

下面的例子展示了一个典型的 @RestController

import java.util.List;

import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/users")
public class MyRestController {

    private final UserRepository userRepository;

    private final CustomerRepository customerRepository;

    public MyRestController(UserRepository userRepository, CustomerRepository customerRepository) {
        this.userRepository = userRepository;
        this.customerRepository = customerRepository;
    }

    @GetMapping("/{userId}")
    public User getUser(@PathVariable Long userId) {
        return this.userRepository.findById(userId).get();
    }

    @GetMapping("/{userId}/customers")
    public List<Customer> getUserCustomers(@PathVariable Long userId) {
        return this.userRepository.findById(userId).map(this.customerRepository::findByUser).get();
    }

    @DeleteMapping("/{userId}")
    public void deleteUser(@PathVariable Long userId) {
        this.userRepository.deleteById(userId);
    }

}

自定义消息转换

Spring MVC 使用 HttpMessageConverter 接口来转换 HTTP 请求和响应,自动使用合理的默认值,比如,对象被自动转换为 JSON 或者 XML。字符串默认使用 UTF-8 编码。

如果你需要添加一个自定义 converter,你可以使用 Spring Boot 提供的 HttpMessageConverters 类,如下所示:

import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;

@Configuration(proxyBeanMethods = false)
public class MyHttpMessageConvertersConfiguration {

    @Bean
    public HttpMessageConverters customConverters() {
        HttpMessageConverter<?> additional = new AdditionalHttpMessageConverter();
        HttpMessageConverter<?> another = new AnotherHttpMessageConverter();
        return new HttpMessageConverters(additional, another);
    }

}

任何存在在 context 中的 HttpMessageConverter 会被添加到一个 converters List 中,你也可以使用此方法覆盖默认的 converters。

自定义静态资源路径

默认情况下,Spring Boot 处理从类路径的 /static/public/resources/META-INF/resources 下处理静态资源,或者从 ServletContext 的根路径。它使用 Spring MVC 的 ResourceHttpRequestHandler,因此你可以通过添加自己的 WebMvcConfigurer 并重写 addResourceHandlers 方法来修改行为。

自定义 Error 页面

默认情况下,Spring Boot 提供 /error 映射来处理所有的处理,它被注册作为 servlet 容器中全局的错误页面,对于机器客户端,它提供一个 JSON 格式的响应来描述 error,HTTP status,exception message。对于浏览器客户端,它提供一个空白 error 页面。

如果你想要自定义 HTML error page 来展示被提供的 status code,你可以在 /error 目录中添加一个文件,error page 可以是一个 HTML 格式的文件,也可以是一个模板引擎。该文件的名字应该是准确的状态码或者序列掩码。

例如,要映射 404 到静态 HTML 文件,你的目录结构如下:

src/
 +- main/
     +- java/
     |   + 
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- 

使用 FreeMarker 模板映射所有的 5xx errors,你的目录结构如下:

src/
 +- main/
     +- java/
     |   + 
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.ftlh
             +- 

对于更复杂的映射,你可以添加实现了 ErrorViewResolver 接口的 beans,如下所示:

public class MyErrorViewResolver implements ErrorViewResolver {

    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        // Use the request or status to optionally return a ModelAndView
        if (status == HttpStatus.INSUFFICIENT_STORAGE) {
            // We could add custom model values here
            new ModelAndView("myview");
        }
        return null;
    }

}

内嵌 Servlet 容器

对于 Servlet 应用,Spring Boot 包含了内嵌 Tomcat,Jetty 和 Undertow 服务器的支持,大多数开发者使用合适的 “Starter” 来获得一个完整的配置实例,默认情况下,内嵌服务器在 8080 端口监听 HTTP 请求。

注册 Servlet 组件

当使用内嵌 Servlet 容器时,你可以使用 Spring Bean 或者扫描 Servlet 组件来注册 Servlets,Filters 和 List。

任何作为 Spring bean 的 ServletFilter 或者 Listener 实例都会在内嵌容器中注册。

当使用内嵌 Servlet 容器时,使用 @ServletComponentScan 可以开启 Servlet 3.0 注解的扫描。标注了 @WebServletWebFilter@WebListener 注解的类将会被自动注册。

默认情况下,如果上下文只包含一个 Servlet,它会映射到 /.,在多个 Servlet bean 的情况下,bean 名称用作路径前缀,过滤器映射到 /*

如果基于约定的映射不够灵活,你可以使用 ServletRegistrationBeanFilterRegistrationBeanServletListenerRegistrationBean 类进行完全控制。

让 Filter Bean 无序通常是正确的,如果需要特定的顺序,你应该在 Filter Bean 上添加 @Order 或者继承 Ordered,如果你不能在 Filter 类上添加 @Order 注解或者继承 Ordered 类,你可以定义一个 FilterRegistrationBean,使用 setOrder(int) 方法来对注册的 bean 进行排序。

Servlet 上下文初始化

内嵌 Servlet 容器不会直接执行 servlet 3.0 的 javax.servlet.ServletContainerInitializer 接口或者 Spring 的 org.springframework.web.WebApplicationInitializer 接口。这是一个有意的设计决策,旨在降低在 war 包中运行的第三方类库破坏 Spring Boot 应用程序的风险。

如果你需要在 Spring Boot 应用程序中执行 Servlet 上下文初始化,你应该注册一个实现 org.springframework.boot.web.servlet.ServletContextInitializer 接口的 bean。

ServletWebServerApplicationContext

在底层,Spring Boot 使用不同类型的 ApplicationContext 对内嵌 Servlet 容器提供支持,ServletWebServerApplicationContext 是一个特殊类型的 WebApplicationContext,它通过搜索单个的 ServletWebServerFactory bean 来引导自身。

通常情况下,TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactory 已经被自动配置。

在一个内嵌容器安装过程中,ServletContext 被设置为服务启动的一部分,发生在应用程序上下文初始化期间。因为在 ApplicationContext 中的 bean 不能通过 ServletContext 可靠的初始化。解决此问题的一种方法是将注入 ApplicationContext 作为 bean 的依赖并在有需要的时候访问 ServletContext。另一种方法是在服务器启动后使用回调,使用 ApplicationListener 监听 ApplicationStartedEvent 事件:

import javax.servlet.ServletContext;

import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.web.context.WebApplicationContext;

public class MyDemoBean implements ApplicationListener<ApplicationStartedEvent> {

    private ServletContext servletContext;

    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        this.servletContext = ((WebApplicationContext) applicationContext).getServletContext();
    }

}
自定义内嵌 Servlet 容器
配置文件配置

公共 Servlet 容器配置可以通过 Spring Environment 属性配置。通常,你可以在 application.properties 文件上配置。

公共服务配置包括:

  • 网络配置:HTTP 监听端口(server.port),接口地址绑定到 server.address
  • Session 配置:如果 session 是持久的(server.servlet.session.persistent),session 超时时间(server.servlet.session.timeout),session 数据存放位置(server.servlet.session.store-dir),session-cookie 配置(server.servlet.session.cookie.*
  • 错误页面配置:错误页面的位置(server.error.path

Spring Boot 尽可能多的暴露公共配置,但是这不总是合适的。比如,专用的命名空间提供特定服务器自定义(server.tomcatserver.undertow)。

编程方式配置

如果你需要编程配置你的内嵌 Servlet 容器,你可以注册一个实现了 WebServerFactoryCustomizer 接口的 Spring bean。WebServerFactoryCustomizer 提供访问 ConfigurableServletWebServerFactory 的方式,它包含许多设置器方法,以下示例显示以编程方式设置端口:

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;

@Component
public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }

}

TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactoryConfigurableServletWebServerFactory 的专用变体,分别具有用于 Tomcat,Jetty 和 Undertow 的其他自定义 setter 方法。以下示例显示了如何自定义 TomcatServletWebServerFactory,该示例可访问特定于 Tomcat 的配置选项:

import java.time.Duration;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class MyTomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Override
    public void customize(TomcatServletWebServerFactory server) {
        server.addConnectorCustomizers((connector) -> connector.setAsyncTimeout(Duration.ofSeconds(20).toMillis()));
    }

}

Data

SQL 数据库

Spring Framework 为使用 SQL 数据库提供了广泛的支持,从使用 JdbcTemplate 直接 JDBC 访问到完全 object relational mapping 技术比如 Hibernate。

DataSource

Java 的 javax.sql.DataSource 接口提供了一种处理数据库连接的标准方法。传统上,DataSource 使用一个 URL 以及一些凭据来建立数据库连接。

内嵌数据库支持

使用内存嵌入式数据库开发应用程序通常很方便,显然,内存数据库不提供持久存储。你需要在应用程序启动时填充数据库,并准备在应用程序结束时丢弃数据库。

Spring Boot 能够自动配置嵌入式 H2,HSQL 和 Derby 数据库,你不需要提供任何连接 URL,你只需要添加你想要使用的嵌入式数据库的依赖到类路径中。如果类路径中存在多个嵌入式数据库,spring.datasource.embedded-database-connection 配置属性可以控制使用哪个数据库,设置属性为 none 表示禁用嵌入式数据库的自动配置。

比如,典型的 POM 依赖如下:

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
    <groupId>org.hsqldbgroupId>
    <artifactId>hsqldbartifactId>
    <scope>runtimescope>
dependency>
数据源配置

DataSource 配置被前缀为 spring.datasource.* 的外部配置属性控制,如下:

spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass

查看 DataSourceProperties 可以了解更多支持的选项。无论实际实现如何,这些都是标准选项。

通过它们各自的前缀,你也可以微调特定实现的设置,比如 spring.datasource.hikari.*spring.datasource.tomcat.*spring.datasource.dbcp2.*spring.datasource.oracleucp.*

比如,如果你使用 Tomcat connection pool,你可以自定义许多附加的配置,比如:

spring.datasource.tomcat.max-wait=10000
spring.datasource.tomcat.max-active=50
spring.datasource.tomcat.test-on-borrow=true
连接 JNDI 数据源

如果你部署你的 Spring Boot 应用程序到服务器上,你可能想要使用服务器内置功能配置和管理 DataSource,并使用 JNDI 访问它。

spring.datasource.jndi-name 属性能够作为 spring.datasource.url 的替代属性。spring.datasource.usernamespring.datasource.password 属性从指定的 JNDI 中访问 DataSource。如下:

spring.datasource.jndi-name=java:jboss/datasources/customers

JdbcTemplate

Spring 的 JdbcTemplateNamedParameterJdbcTemplate 类被自动配置,你可以使用 @Autowire 将它们注入到你的 bean 中。

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    private final JdbcTemplate jdbcTemplate;

    public MyBean(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void doSomething() {
        this.jdbcTemplate ...
    }

}

你是可以使用 spring.jdbc.template.* 属性自定义 template 的一些属性,如下:

spring.jdbc.template.max-rows=500

JPA 和 Spring Data JPA

Java Persistence API 是一个标准技术,让你映射对象到关系型数据库。spring-boot-starter-data-jpa POM 提供一个快速的方法来启动,它提供了下面关键依赖:

  • Hibernate:最受欢迎的 JPA 实现之一
  • Spring Data JPA:帮助你实现基于 JPA 的仓库
  • Spring ORM:Spring Framework 提供的核心 ORM 支持
Entity

传统上,JAP Entity 被在 persistence.xml 文件中指定。通过 Spring Boot,这个文件不再是必须的并使用实体扫描来替代,默认情况下,在你主启动类类下的所有的包都会被搜索。

任何标注了 @Entity@Embeddable 或者 @MappedSuperclass 的类将会被扫描,如下:

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class City implements Serializable {

    @Id
    @GeneratedValue
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String state;

    // ... additional members, often include @OneToMany mappings

    protected City() {
        // no-args constructor required by JPA spec
        // this one is protected since it should not be used directly
    }

    public City(String name, String state) {
        this.name = name;
        this.state = state;
    }

    public String getName() {
        return this.name;
    }

    public String getState() {
        return this.state;
    }

    // ... etc

}
Repositories

Spring Data JPA Repositories 是一个你能够定义如何访问数据的接口。JPA 查询会根据你的方法名自动创建,比如,CityRepository 接口可以声明一个 findAllByState(String state) 方法来根据被给定的 state 来查询所有的 cities。

Spring Data Repositories 经常从 Repository 或者 CrudRepository 接口中进行扩展,如果你使用自动配置,在主启动类下的包中的 Repositories 会被自动搜索。

一个典型的 Spring Data Repository 接口定义如下:

import org.springframework.boot.docs.data.sql.jpaandspringdata.entityclasses.City;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;

public interface CityRepository extends Repository<City, Long> {

    Page<City> findAll(Pageable pageable);

    City findByNameAndStateAllIgnoringCase(String name, String state);

}

Spring Data JPA Repositories 提供了三种不同的启动模式:defaultdeferredlazy,通过 spring.data.jpa.repositories.bootstrap-mode 属性来配置。

创建和删除 JPA 数据库

默认情况下,JPA 数据库只有在你使用一个嵌入式数据库的时候自动创建,你可以使用 spring.jpa.* 属性显示配置 JPA 设置。比如,创建和删除表你可以添加下面的行到你的配置文件 application.properties 中:

spring.jpa.hibernate.ddl-auto=create-drop

IO

大多数应用程序在某些时候需要处理输入和输出问题。Spring Boot 提供实用程序和与一系列技术的集成,以在你需要 IO 功能时提供帮助。本节涵盖标准 IO 功能(例如缓存和验证)以及更高级的主题(例如调度和分布式事务)。我们还将介绍调用远程 REST 或 SOAP 服务以及发送电子邮件。

缓存

Spring Framework 支持透明的向应用程序添加缓存,在其核心,抽象将缓存应用于方法,从而减少基于缓存中可用信息的执行次数。缓存逻辑对应用是透明的,对调用者没有任何干扰。只要使用 @EnableCaching 注解启用缓存支持,Spring Boot 就会自动配置缓存基础结构。

简而言之,要向服务的操作添加缓存,请将相关注释添加到其方法上,如下所示:

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

@Component
public class MyMathService {

    @Cacheable("piDecimals")
    public int computePiDecimal(int precision) {
        ...
    }

}

此示例演示了在可能代价高昂的操作中使用缓存。在调用 computePiDecimal 之前,抽象会在 piDecimals 缓存中查找与 i 参数匹配的条目,如果找到条目,缓存中的内容会立即返回给调用者,并且不会调用该方法。否则,调用该方法,并在返回值之前更新缓存。

Cache Providers

缓存抽象不提供实际的存储,它依赖于 org.springframework.cache.Cacheorg.springframework.cache.CacheManager 接口实现的抽象。

如果你尚未定义 CacheManager 类型的 bean 或者命名为 cacheResolverCacheResolver,Spring Boot 尝试发现下列的提供者:

  1. Generic
  2. JCache(EhCache 3)
  3. EhCache 2.x
  4. Hazelcast
  5. Infinispan
  6. Couchbase
  7. Redis
  8. Caffeine
  9. Cache2k
  10. Simple

如果 Spring Boot 自动配置了 CacheManager,你可以在完全初始化之前通过公开实现 CacheManagerCustomizer 接口的 bean 进一步调整其配置,以下示例设置了一个标志,表示 null 值不应该被传递到底层映射。

import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizer;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyCacheManagerConfiguration {

    @Bean
    public CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
        return (cacheManager) -> cacheManager.setAllowNullValues(false);
    }

}

Quartz Scheduler

Spring Boot 为应用程序使用 Quartz Scheduler 提供了很多方便,包括 spring-boot-starter-quartz Starter。如果 Quartz 是可用的,一个 Scheduler 会被自动配置。

下面类型的 Beans 会被自动拾取并和 Scheduler 进行关联:

  • JobDetail:定义一个特殊的任务,JobDetail 实例应该使用 JobBuilder API 构建
  • Calendar
  • Trigger:当一个特殊的 job 被触发的时候被创建

默认情况下,使用内存 JobStore,然而,你也可以使用基于 JDBC 的 Store,这需要有 DataSource bean 在你的应用程序中,并且 spring.quartz.job-store-type 属性配置了相应的值,如下:

spring.quartz.job-store-type=jdbc

当 JDBC Store 被使用的时候,schema 能够在启动的时候被初始化,如下:

spring.quartz.jdbc.initialize-schema=always

要让 Quartz 使用应用程序主 DataSource 之外的 DataSource,声明一个 DataSource bean,在方法上标注 @Bean@QuartzDataSource 。这样可以确保指定为 Quartz 的 DataSource 被用于 SchedulerFactoryBean 和 schema 初始化。

类似的,要让 Quartz 使用应用程序主 TransactionManager 之外的 TransactionManager,声明一个 TransactionManager bean,在方法上标注 @Bean@QuartzTransactionManager

默认情况下,通过配置创建的 Job 不会覆盖已经从持久 Job Store 中读取并注册的 Job,设置 spring.quartz.overwrite-existing-jobs 属性可以启用覆盖已经存在的 Job。

使用 spring.quartz 属性和 SchedulerFactoryBeanCustomizer beans 能够自定义 Quartz Scheduler 的配置,Quartz Scheduler 的高级配置能够使用 spring.quartz.properties.* 属性配置。

Job 能够使用 setters 注入数据映射属性,常规 bean 也能通过类似的方法注入,如下:

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import org.springframework.scheduling.quartz.QuartzJobBean;

public class MySampleJob extends QuartzJobBean {

    private MyService myService;

    private String name;

    // Inject "MyService" bean
    public void setMyService(MyService myService) {
        this.myService = myService;
    }

    // Inject the "name" job data property
    public void setName(String name) {
        this.name = name;
    }

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        this.myService.someMethod(context.getFireTime(), this.name);
    }

}

REST Service

如果你的应用调用远程 REST 服务,使用 Spring Boot 提供的 RestTemplate 或者 WebClient 可能会更容易。

RestTemplate

如果你需要从你的应用程序中调用远程 REST 服务,你可以使用 Spring Framework 的 RestTemplate 类,因此 RestTemplate 实例经常需要在使用之前被自定义,Spring Boot 没有提供任何单个的自动配置 RestTemplate bean。

然而,RestTemplateBuilder 可以被用来创建 RestTemplate 实例,RestTemplateBuilder 确保合适的 HttpMessageConverters 被应用到 RestTemplate 实例。

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class MyService {

    private final RestTemplate restTemplate;

    public MyService(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    }

    public Details someRestCall(String name) {
        return this.restTemplate.getForObject("/{name}/details", Details.class, name);
    }

}
自定义 RestTemplate

这里有三种主要的方式来自定义 RestTemplate,使用哪种依赖于你想要自定义多广泛。

确保任何自定义的范围尽可能小,注入 RestTemplateBuilder 然后调用它的方法。每一个方法返回一个新的 RestTemplateBuilder 实例。

要进行应用程序范围的附加定制,请使用 RestTemplateCustomizer bean,所有这些 bean 都会自动注册到 RestTemplateBuilder 中,并应用于使用它构建的任何模板。

以下示例显示了一个自定义程序,它为所有主机配置了代理的使用,除了 192.168.0.5

import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.routing.HttpRoutePlanner;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
import org.apache.http.protocol.HttpContext;

import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

public class MyRestTemplateCustomizer implements RestTemplateCustomizer {

    @Override
    public void customize(RestTemplate restTemplate) {
        HttpRoutePlanner routePlanner = new CustomRoutePlanner(new HttpHost("proxy.example.com"));
        HttpClient httpClient = HttpClientBuilder.create().setRoutePlanner(routePlanner).build();
        restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
    }

    static class CustomRoutePlanner extends DefaultProxyRoutePlanner {

        CustomRoutePlanner(HttpHost proxy) {
            super(proxy);
        }

        @Override
        public HttpHost determineProxy(HttpHost target, HttpRequest request, HttpContext context) throws HttpException {
            if (target.getHostName().equals("192.168.0.5")) {
                return null;
            }
            return super.determineProxy(target, request, context);
        }

    }

}

最后,你可以定义自己的 RestTemplateBuilder bean。这样做将替换自动配置的构建器,如果你希望任何 RestTemplateCustomizer beans 被应用于你自定义构建器,就像自动配置所作的那样,请使用 RestTemplateBuilderConfiguer,以下示例公开了 RestTemplateBuilder 与 Spring Boot 的自动配置所做的匹配,除了自定义连接,还指定了读超时时间:

import java.time.Duration;

import org.springframework.boot.autoconfigure.web.client.RestTemplateBuilderConfigurer;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyRestTemplateBuilderConfiguration {

    @Bean
    public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer configurer) {
        return configurer.configure(new RestTemplateBuilder()).setConnectTimeout(Duration.ofSeconds(5))
                .setReadTimeout(Duration.ofSeconds(2));
    }

}

WebClient

如果你有 Spring WebFlux 在你的类路径中,你也可以选择使用 WebClient 来调用远程 REST 服务。和 RestTemplate 相比较,这个客户端有更多的功能并且是完全反应式的。

Spring Boot 为你创建并预配置了一个 WebClient.Builder。强烈建议将其注入你的组件并使用它来创建 WebClient 实例,Spring Boot 正在配置该构建器以共享 HTTP 资源。

import org.neo4j.cypherdsl.core.Relationship.Details;
import reactor.core.publisher.Mono;

import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

@Service
public class MyService {

    private final WebClient webClient;

    public MyService(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("https://example.org").build();
    }

    public Mono<Details> someRestCall(String name) {
        return this.webClient.get().uri("/{name}/details", name).retrieve().bodyToMono(Details.class);
    }

}

WebService

Spring Boot 提供 WebService 自动配置,因此你所要做的就是定义你的 Endpoints

Spring Web Services Features 通过 spring-boot-starter-webservices 模块能够轻松访问。

通过你的 WSDLs 和 XSDs 能够自动创建 SimpleWsdl11DefinitionSimpleXsdSchema beans,你只需要配置它们的位置,如下:

spring.webservices.wsdl-locations=classpath:/wsdl

如果你的应用程序需要调用远程 WebService,你可以使用 WebServiceTemplate 类。

WebServiceTemplate 实例在使用之前经常需要被自定义配置,Spring Boot 没有提供任何单独自动配置 WebServiceTemplate bean。然而,它会自动配置一个 WebServiceTemplateBuilder,它可以用来创建 WebServiceTemplate,如下:

import org.springframework.boot.webservices.client.WebServiceTemplateBuilder;
import org.springframework.stereotype.Service;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.client.core.SoapActionCallback;

@Service
public class MyService {

    private final WebServiceTemplate webServiceTemplate;

    public MyService(WebServiceTemplateBuilder webServiceTemplateBuilder) {
        this.webServiceTemplate = webServiceTemplateBuilder.build();
    }

    public SomeResponse someWsCall(SomeRequest detailsReq) {
        return (SomeResponse) this.webServiceTemplate.marshalSendAndReceive(detailsReq,
                new SoapActionCallback("https://ws.example.com/action"));
    }

}

默认情况下,WebServiceTemplateBuilder 会检测合适的 WebServiceMessageSenderWebServiceMessageSender,在 classpath 中查找一个可用的 HTTP client,如下:

import java.time.Duration;

import org.springframework.boot.webservices.client.HttpWebServiceMessageSenderBuilder;
import org.springframework.boot.webservices.client.WebServiceTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.transport.WebServiceMessageSender;

@Configuration(proxyBeanMethods = false)
public class MyWebServiceTemplateConfiguration {

    @Bean
    public WebServiceTemplate webServiceTemplate(WebServiceTemplateBuilder builder) {
        WebServiceMessageSender sender = new HttpWebServiceMessageSenderBuilder()
                .setConnectTimeout(Duration.ofSeconds(5))
                .setReadTimeout(Duration.ofSeconds(2))
                .build();
        return builder.messageSenders(sender).build();
    }

}

Messaging

Spring Framework 提供了广泛的支持来集成消息系统,从简单使用 JMS API 如 JmsTemplate 到一个完整的架构来接收异步消息。Spring AMQP 提供了一个类似的功能来配置 Advanced Message Queuing Protocol,Spring Boot 也为 RabbitTemplate 和 RabbitMQ 提供了自动配置选项。Spring WebSocket 原生包括对 STOMP 消息传递的支持,Spring Boot 通过启动器和少量的自动配置来支持它。Spring Boot 还支持 Apache Kafka。

JMS

javax.jms.ConnectionFactory 接口提供一个标准方法来创建一个 javax.jms.Connection,用于与 JMS 代理交互的标准方法。尽管 Spring 需要 ConnectionFactory 与 JMS 一起使用,但你通常不需要自己直接使用它,而是可以依赖更高级别的消息传递抽象。Spring Boot 还自动配置必要的基础设置来发送和接收消息。

ActiveMQ Support

当 ActiveMQ 在类路径中可用时,Spring Boot 也能配置一个 ConnectionFactory,如果 broker 存在,则会自动启动和配置嵌入式代理。

ActiveMQ 配置由 spring.activemq.* 配置。

默认情况下,ActiveMQ 自动配置为使用 VM transport,它启动嵌入在同一 JVM 实例中的代理。

你可以使用 spring.activemq.in-memory 属性来禁用嵌入式代理,比如:

spring.activemq.in-memory=false

如果你配置代理 URL,嵌入式代理也会被禁用,如下:

spring.activemq.broker-url=tcp://192.168.1.210:9876
spring.activemq.user=admin
spring.activemq.password=secret

默认情况下,CachingConnectionFactory 使用合理的配置封装本地 ConnectionFactory,你可以使用外部配置 spring.jms.* 来完全控制它,如下:

spring.jms.cache.session-cache-size=5

ActiveMQ Artemis Support

Spring Boot 能够自动配置 ConnectionFactory,当它在 classpath 中发现 ActiveMQ Artemis 时。如果 broker 存在,一个嵌入式 broker 会被自动启动和配置。支持的模式有 embedded 的和 native。当模式配置为后者时,Spring Boot 配置一个 ConnectionFactory 连接到本地机器上运行的代理的默认配置。

ActiveMQ Artemis 配置由 spring.artemis.* 指定,如下:

spring.artemis.mode=native
spring.artemis.broker-url=tcp://192.168.1.210:9876
spring.artemis.user=admin
spring.artemis.password=secret

Using a JNDI ConnectionFactory

如果你在一个应用服务器上运行你的应用程序,Spring Boot 尝试使用 JNDI 定位一个 JMS ConnectionFactory。默认情况下,java:/JmsXAjava:/XAConnectionFactory 位置会被检查,你应该使用 spring.jms.jndi-name 属性,如果你需要指定一个可选的位置,比如:

spring.jms.jndi-name=java:/MyConnectionFactory

Sending a Message

Spring 的 JmsTemplate 会自动配置,你可以将其自动装配到你自己的 bean 中,如下:

import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    private final JmsTemplate jmsTemplate;

    public MyBean(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public void someMethod() {
        this.jmsTemplate.convertAndSend("hello");
    }

}

Receiving a Message

当 JMS 架构存在时,任何被标注了 @JmsListener 的 bean 会创建一个 listener endpoint。如果没有 JmsListenerContainerFactory 被定义,则会自动配置一个。如果 DestinationResolverMessageConverter 或者 javax.jms.ExceptionListener beans 被定义了,它们自动和默认的工厂相关联。

默认情况下,默认工厂是事务性的,如果存在一个 JtaTransactionManager,它被关联到 listener container,如果没有,sessionTrasacted 标记会被启用。在后一种情况下,你可以通过添加 @Transactional 侦听器方法将本地数据存储事务与传入消息的处理相关联。这可确保在本地事务完成后确认传入消息。这还包括发送已在同一 JMS 会话上执行的响应消息。如下:

import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    @JmsListener(destination = "someQueue")
    public void processMessage(String content) {
        // ...
    }

}

如果你需要创建更多 JmsListenerContainerFactory 实例或者你想覆盖默认值,Spring Boot 提供了一个 DefaultJmsListenerContainerFactoryConfigurer,你可以使用 DefaultJmsListenerContainerFactory 与自动配置相同的设置来初始化一个实例。

import javax.jms.ConnectionFactory;

import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;

@Configuration(proxyBeanMethods = false)
public class MyJmsConfiguration {

    @Bean
    public DefaultJmsListenerContainerFactory myFactory(DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        ConnectionFactory connectionFactory = getCustomConnectionFactory();
        configurer.configure(factory, connectionFactory);
        factory.setMessageConverter(new MyMessageConverter());
        return factory;
    }

    private ConnectionFactory getCustomConnectionFactory() {
        return ...
    }

}

然后你可以在任何带注释的方法中使用工厂,如下:

import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    @JmsListener(destination = "someQueue", containerFactory = "myFactory")
    public void processMessage(String content) {
        // ...
    }

}

AMQP

高级消息队列协议(AMQP)是面向消息中间件的平台中立、线路级协议。Spring AMQP 项目将核心 Spring 概念应用于基于 AMQP 的消息传递解决方案的开发。Spring Boot 为通过 RabbitMQ 使用 AMQP 提供了多种便利,包括 spring-boot-starter-amqp Starter。

RabbitMQ Support

RabbitMQ 是基于 AMQP 协议的轻量级、可靠、可扩展、可移植的消息代理。Spring 使用 RabbitMQ 通过 AMQP 协议进行通信。RabbitMQ 的配置前缀为 spring.rabbitmq.*,如下:

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=secret

或者,你可以使用如下 addresses 属性配置相同的连接:

spring.rabbitmq.addresses=amqp://admin:secret@localhost

Sending a Message

Spring 的 AmqpTemplateAmqpAdmin 是自动配置的,你可以将它们直接自动装配到你自己的 bean 中,如下列所示:

import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    private final AmqpAdmin amqpAdmin;

    private final AmqpTemplate amqpTemplate;

    public MyBean(AmqpAdmin amqpAdmin, AmqpTemplate amqpTemplate) {
        this.amqpAdmin = amqpAdmin;
        this.amqpTemplate = amqpTemplate;
    }

    public void someMethod() {
        this.amqpAdmin.getQueueInfo("someQueue");
    }

    public void someOtherMethod() {
        this.amqpTemplate.convertAndSend("hello");
    }

}

如有必要,任何 org.springframework.amqp.core.Queue 定义为 bean 的内容都会自动用于 RabbitMQ 实例上声明相应的队列。要重试操作,你可以重启重试 AmqpTemplate

spring.rabbitmq.template.retry.enabled=true
spring.rabbitmq.template.retry.initial-interval=2s

Sending a Message To A Stream

发送消息到一个特定的流,指定流的名称,如下:

spring.rabbitmq.stream.name=my-stream

如果 MessageConverterStreamMessageConverter 或者 ProducerCustomizer bean 被定义,它们会被自动关联到 RabbitStreamTemplate

如果你想要创建更多的 RabbitStreamTemplate 实例或者你想要覆盖默认的实例,Spring Boot 提供一个 RabbitStreamTemplateConfigurer bean 来初始化一个 RabbitStreamTemplate

Receiving a Message

当 Rabbit 架构存在的时候,任何标注了 @RabbitListener 注解的类创建一个 listener endpoint,如果 RabbitListenerContainerFactory 没有被定义,将使用 SimpleRabbitListenerContainerFactory,你也可以使用 spring.rabbitmq.listener.type 属性来自定义。如果 MessageConverter 或者 MessageRecoverer bean 已经被定义,会自动和默认工厂关联。

下面的例子会创建一个 listener endpoint 在 someQueue 队列:

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    @RabbitListener(queues = "someQueue")
    public void processMessage(String content) {
        // ...
    }

}

如果你需要创建更多的 RabbitListenerContainerFactory 实例或者你想要覆盖默认的,Spring Boot 提供了 SimpleRabbitListenerContainerFactoryConfigurerDirectRabbitListenerContianerFactoryConfigurer 来初始化一个 SimpleRabbitListenerContainerFactory 和一个 DirectRabbitListenerContainerFactory

下面的配置类暴露另一个使用特定 MessageConverter 的工厂类:

import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyRabbitConfiguration {

    @Bean
    public SimpleRabbitListenerContainerFactory myFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        ConnectionFactory connectionFactory = getCustomConnectionFactory();
        configurer.configure(factory, connectionFactory);
        factory.setMessageConverter(new MyMessageConverter());
        return factory;
    }

    private ConnectionFactory getCustomConnectionFactory() {
        return ...
    }

}

然后,你能在任何标注了 @RabbitListener 注解的方法中使用该工厂,如下:

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    @RabbitListener(queues = "someQueue", containerFactory = "myFactory")
    public void processMessage(String content) {
        // ...
    }

}

Apache Kafka Support

Apache Kafka 被支持自动配置通过一个 spring-kafka 项目。

Kafka 配置的前缀为 spring.kafka.*

spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.group-id=myGroup

Sending a Message

Spring 的 KafkaTemplate 被自动配置,你可以将它注入到你自己的 bean 中,如下:

import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    private final KafkaTemplate<String, String> kafkaTemplate;

    public MyBean(KafkaTemplate<String, String> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }

    public void someMethod() {
        this.kafkaTemplate.send("someTopic", "Hello");
    }

}

Receiving a Message

当 Apache Kafka 架构存在的时候,任何被标注了 @KafkaListener 的 bean 创建一个 listener endpoint。如果没有定义 KafkaListenerContainerFactory,使用默认的工厂。

下面组件创建一个 listener endpoint 在 someTopic topic 中:

import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    @KafkaListener(topics = "someTopic")
    public void processMessage(String content) {
        // ...
    }

}

生成环境特性

Spring Boot 包含很多附加的特性来帮助你监控和管理你的应用程序。你可以选择使用 HTTP 端点或者使用 JMX 来管理和监控你的应用程序。

启用生产环境特性

spring-boot-actuator 模块提供了所有的 Spring Boot 的 Production-Ready Features。开启这个特性推荐的方法是添加 spring-boot-starter-actuator Starter。

<dependencies>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-actuatorartifactId>
    dependency>
dependencies>

Endpoints

Actuator Endpoints 让你监控和与你的应用程序交互。Spring Boot 包括很多内建 Endpoints,你也可以添加你自己的 Endpoints。比如,health Endpoint 提供了基础的应用程序健康信息。

你可以启动或者禁用每一个单独的 Endpoint 以及将它们暴露到 HTTP 或者 JMX 中。当一个 Endpoint 是 Enabled(启用) 和 Exposed(暴露) 时,才是可用的。内建 Endpoint 只有在可用的时候自动配置。

大多数应用程序选择 HTTP 协议暴露 Endpoint,其中 Endpoint 的 ID 和 /actuator 前缀映射到 URL。例如,默认情况下,health 端点映射到 /actuator/health

启用 Endpoint

默认情况下,除了 shutdown 其他端点都被启用。配置 Endpoint 启用,使用 management.endpoint..enabled 属性,下面例子启用了 shutdown 端点:

management.endpoint.shutdown.enabled=true

暴露 Enpoint

因为 Endpoint 可能包含敏感信息,你应该仔细考虑什么时候暴露它们,下表显示了内置 Endpoint 的默认暴露:

ID JMX Web
auditevents Yes No
beans Yes No
caches Yes No
conditions Yes No
configprops Yes No
env Yes No
flyway Yes No
health Yes Yes
heapdump N/A No
httptrace Yes No
info Yes No
integrationgraph Yes No
jolokia N/A No
logfile N/A No
loggers Yes No
liquibase Yes No
metrics Yes No
prometheus N/A No
quartz Yes No
scheduledtasks Yes No
sessions Yes No
shutdown Yes No
startup Yes No
threaddump Yes No

改变哪些端点暴露,使用下面特殊的 includeexclude 属性:

Property Default
management.endpoints.jmx.exposure.exclude
management.endpoints.jmx.exposure.include *
management.endpoints.jmx.exposure.exclude
management.endpoints.jmx.exposure.include health

include 属性列出暴露端点的 ID,exclude 属性列出不被暴露端点的 ID。exclude 属性优先于 include 属性。你可以同时配置 exludeinclude 属性。

比如,停止暴露 JMX 上的所有端点,只暴露 healthinfo 端点,使用下面的属性:

management.endpoints.jmx.exposure.include=health,info

* 表示选择所有端点。比如,暴露所有在 HTTP 上的端点,除了 envbeans 端点,使用下面配置:

management.endpoints.web.exposure.include=*
management.endpoints.web.exposure.exclude=env,beans

你可能感兴趣的:(spring,boot,后端,java)