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

1. 缓存

Spring Framework 支持以透明的方式向应用程序添加缓存. 从本质上讲,将缓存应用于方法上,根据缓存数据减少方法的执行次数. 缓存逻辑是透明的,不会对调用者造成任何干扰. 通过 @EnableCaching 注解启用缓存支持,Spring Boot 就会自动配置缓存设置.

有关更多详细信息,请查看 Spring Framework 参考文档的 相关部分 .

简而言之,为服务添加缓存的操作就像在其方法中添加注解一样简单,如下所示:

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 参数匹配的项. 如果找到,则缓存中的内容会立即返回给调用者,并不会调用该方法. 否则,将调用该方法,并在返回值之前更新缓存.

您还可以使用标准 JSR-107 (JCache) 注解 (例如 @CacheResult) . 但是,我们强烈建议您不要将 Spring Cache 和 JCache 注解混合使用.

如果您不添加任何指定的缓存库,Spring Boot 会自动配置一个使用 concurrent map 的 simple provider . 当需要缓存时 (例如前面示例中的 piDecimals) ,该 simple provider 会为您创建缓存. 不推荐将 simple provider 用于生产环境,但它非常适合入门并帮助您了解这些功能. 当您决定使用缓存提供者时,请务必阅读其文档以了解如何配置应用程序. 几乎所有提供者都要求您显式配置应用程序中使用的每个缓存. 有些提供了自定义 spring.cache.cache-names 属性以定义默认缓存.

还可以透明地从缓存中 更新删除 数据.

1.1. 支持的缓存提供者

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

如果您未定义 CacheManager 类型的 bean 或名为 cacheResolverCacheResolver (请参阅 CachingConfigurer) ,则 Spring Boot 会尝试检测以下提供者 (按序号顺序) :

  1. Generic

  2. JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)

  3. EhCache 2.x

  4. Hazelcast

  5. Infinispan

  6. Couchbase

  7. Redis

  8. Caffeine

  9. Simple

此外,https://github.com/spring-projects/spring-boot-data-geode[Spring Boot for Apache Geode] 提供了 使用 Apache Geode 作为自动配置缓存提供者

也可以通过设置 spring.cache.type 属性来强制指定缓存提供者. 如果您需要在某些环境 (比如测试) 中 完全禁用缓存,请使用此属性.
使用 spring-boot-starter-cache “Starter” 快速添加基本的缓存依赖. starter 引入了 spring-context-support. 如果手动添加依赖,则必须包含 spring-context-support 才能使用 JCache、EhCache 2.x 或 Caffeine 支持.

如果通过 Spring Boot 自动配置 CacheManager,则可以通过暴露一个实现了 CacheManagerCustomizer 接口的 bean,在完全初始化之前进一步调整其配置. 以下示例设置了一个 flag,表示应将 null 值传递给底层 map:

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

}
在前面示例中,需要一个自动配置的 ConcurrentMapCacheManager. 如果不是这种情况 (您提供了自己的配置或自动配置了不同的缓存提供者) ,则根本不会调用 customizer. 您可以拥有多个 customizer,也可以使用 @OrderOrdered 来排序它们.

1.1.1. Generic

如果上下文定义了至少一个 org.springframework.cache.Cache bean,则使用 Generic 缓存. 将创建一个包装所有该类型 bean 的 CacheManager.

1.1.2. JCache (JSR-107)

JCache 通过 classpath 上的 javax.cache.spi.CachingProvider (即 classpath 上存在符合 JSR-107 的缓存库) 来引导,jCacheCacheManagerspring-boot-starter-cache starter 提供. 您可以使用各种兼容库,Spring Boot 为 Ehcache 3、Hazelcast 和 Infinispan 提供依赖管理. 您还可以添加任何其他兼容库.

可能存在多个提供者,在这种情况下必须明确指定提供者. 即使 JSR-107 标准没有强制规定一个定义配置文件位置的标准化方法,Spring Boot 也会尽其所能设置一个包含实现细节的缓存,如下所示:

# Only necessary if more than one provider is present
spring:
  cache:
    jcache:
      provider: "com.example.MyCachingProvider"
      config: "classpath:example.xml"
当缓存库同时提供原生实现和 JSR-107 支持时,Spring Boot 更倾向 JSR-107 支持,因此当您切换到不同的 JSR-107 实现时,还可以使用相同的功能.
Spring Boot 对 Hazelcast 的支持一般. 如果有一个 HazelcastInstance 可用,它也会自动复用 CacheManager ,除非指定了 spring.cache.jcache.config 属性.

有两种方法可以自定义底层的 javax.cache.cacheManager:

  • 可以通过设置 spring.cache.cache-names 属性在启动时创建缓存. 如果定义了自定义 javax.cache.configuration.Configuration bean,则会使用它来自定义.

  • 使用 CacheManager 的引用调用 org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer bean 以进行完全自定义.

如果定义了一个标准的 javax.cache.CacheManager bean,它将自动包装进一个抽象所需的 org.springframework.cache.CacheManager 实现中,而不会应用自定义配置.

1.1.3. EhCache 2.x

如果可以在 classpath 的根目录中找到名为 ehcache.xml 的文件,则使用 EhCache 2.x. 如果找到 EhCache 2.x,则使用 spring-boot-starter-cache starter 提供的 EhCacheCacheManager 来启动缓存管理器. 还可以提供其他配置文件,如下所示:

spring:
  cache:
    ehcache:
      config: "classpath:config/another-config.xml"

1.1.4. Hazelcast

Spring Boot 对 Hazelcast 的支持一般. 如果自动配置了一个 HazelcastInstance,它将自动包装进 CacheManager 中.

1.1.5. Infinispan

Infinispan 没有默认的配置文件位置,因此必须明确指定. 否则将使用默认配置加载.

spring:
  cache:
    infinispan:
      config: "infinispan.xml"

可以通过设置 spring.cache.cache-names 属性在启动时创建缓存. 如果定义了自定义 ConfigurationBuilder bean,则它将用于自定义缓存.

Infinispan 在 Spring Boot 中的支持仅限于内嵌模式,非常简单. 如果你想要更多选项,你应该使用官方的 Infinispan Spring Boot starter. 有关更多详细信息,请参阅 Infinispan 文档 .

1.1.6. Couchbase

如果 Spring Data Couchbase 可用并且已 配置 Couchbase,则会自动配置 CouchbaseCacheManager. 通过设置 spring.cache.cache-names 属性可以在启动时创建其他缓存,并且可以使用 spring.cache.couchbase.* 属性配置缓存默认值. 以下配置创建 cache1cache2 缓存,他们的有效时间为 10 分钟:

spring:
  cache:
    cache-names: "cache1,cache2"
    couchbase:
      expiration: "10m"

如果需要对配置进行更多控制,请考虑注册 CouchbaseCacheManagerBuilderCustomizer bean.以下示例显示了一个定制器,该定制器为 cache1cache2 配置到期:

import java.time.Duration;

import org.springframework.boot.autoconfigure.cache.CouchbaseCacheManagerBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.cache.CouchbaseCacheConfiguration;

@Configuration(proxyBeanMethods = false)
public class MyCouchbaseCacheManagerConfiguration {

    @Bean
    public CouchbaseCacheManagerBuilderCustomizer myCouchbaseCacheManagerBuilderCustomizer() {
        return (builder) -> builder
                .withCacheConfiguration("cache1", CouchbaseCacheConfiguration
                        .defaultCacheConfig().entryExpiry(Duration.ofSeconds(10)))
                .withCacheConfiguration("cache2", CouchbaseCacheConfiguration
                        .defaultCacheConfig().entryExpiry(Duration.ofMinutes(1)));

    }

}

1.1.7. Redis

如果 Redis 可用并已经配置,则应用程序会自动配置一个 RedisCacheManager. 通过设置 spring.cache.cache-names 属性可以在启动时创建其他缓存,并且可以使用 spring.cache.redis.* 属性配置缓存默认值. 例如,以下配置创建 cache1cache2 缓存,他们的有效时间为 10 分钟:

spring:
  cache:
    cache-names: "cache1,cache2"
    redis:
      time-to-live: "10m"
默认情况下,会添加一个 key 前缀,这样做是因为如果两个单独的缓存使用了相同的键,Redis 不支持重叠 key,而缓存也不能返回无效值. 如果您创建自己的 RedisCacheManager,我们强烈建议您启用此设置.
您可以通过添加自己的 RedisCacheConfiguration @Bean 来完全控制配置. 如果您想自定义序列化策略,这种方式可能很有用.

如果您需要控制更多的配置,请考虑注册 RedisCacheManagerBuilderCustomizer bean. 以下示例显示了一个自定义的配置,配置了 cache1cache2 的失效时间:

import java.time.Duration;

import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;

@Configuration(proxyBeanMethods = false)
public class MyRedisCacheManagerConfiguration {

    @Bean
    public RedisCacheManagerBuilderCustomizer myRedisCacheManagerBuilderCustomizer() {
        return (builder) -> builder
                .withCacheConfiguration("cache1", RedisCacheConfiguration
                        .defaultCacheConfig().entryTtl(Duration.ofSeconds(10)))
                .withCacheConfiguration("cache2", RedisCacheConfiguration
                        .defaultCacheConfig().entryTtl(Duration.ofMinutes(1)));

    }

}

1.1.8. Caffeine

Caffeine 是一个使用了 Java 8 重写 Guava 缓存,用于取代 Guava 支持的缓存库. 如果 Caffeine 存在,则应用程序会自动配置一个 CaffeineCacheManager (由 spring-boot-starter-cache starter 提供) . 可以通过设置 spring.cache.cache-names 属性在启动时创建缓存,并且可以通过以下方式之一 (按序号顺序) 自定义缓存:

  1. 一个由 spring.cache.caffeine.spec 定义的缓存规范

  2. 一个已定义的 com.github.benmanes.caffeine.cache.CaffeineSpec bean

  3. 一个已定义的 com.github.benmanes.caffeine.cache.Caffeine bean

例如,以下配置创建 cache1cache2 缓存,最大大小为 500,有效时间 为 10 分钟:

spring:
  cache:
    cache-names: "cache1,cache2"
    caffeine:
      spec: "maximumSize=500,expireAfterAccess=600s"

如果定义了 com.github.benmanes.caffeine.cache.CacheLoader bean,它将自动与 CaffeineCacheManager 关联. 由于 CacheLoader 将与缓存管理器管理的所有缓存相关联,因此必须将其定义为 CacheLoader<Object, Object>. 自动配置会忽略所有其他泛型类型.

1.1.9. Simple

如果找不到其他提供者,则配置使用一个 ConcurrentHashMap 作为缓存存储的简单实现. 如果您的应用程序中没有缓存库,则该项为默认值. 默认情况下,会根据需要创建缓存,但您可以通过设置 cache-names 属性来限制可用缓存的列表. 例如,如果只需要 cache1cache2 缓存,请按如下设置 cache-names 属性:

spring:
  cache:
    cache-names: "cache1,cache2"

如果这样做了,并且您的应用程序使用了未列出的缓存,则运行时在它需要缓存时会触发失败,但在启动时则不会. 这类似于真实缓存提供者在使用未声明的缓存时触发的行为方式.

1.1.10. None

当配置中存在 @EnableCaching 时,也需要合适的缓存配置. 如果需要在某些环境中完全禁用缓存,请将缓存类型强制设置为 none 以使用 no-op 实现,如下所示:

spring:
  cache:
    type: "none"

2. Hazelcast

如果 Hazelcast 在 classpath 上并有合适的配置,则 Spring Boot 会自动配置一个可以在应用程序中注入的 HazelcastInstance.

Spring Boot 首先尝试通过检查以下配置选项来创建一个客户端:

  • 存在 com.hazelcast.client.config.ClientConfig bean.

  • spring.hazelcast.config 属性定义的配置文件.

  • 存在 hazelcast.client.config 系统属性.

  • 工作目录中或 classpath 根目录下的 hazelcast-client.xml.

  • 工作目录中或 classpath 根目录下的 hazelcast-client.yaml.

Spring Boot 支持 Hazelcast 4 和 Hazelcast 3. 如果降级到 Hazelcast 3, 应该将 hazelcast-client 添加到类路径中以配置客户端.

如果无法创建客户端,则 Spring Boot 尝试配置嵌入式服务器.

如果定义了 com.hazelcast.config.Config bean,则 Spring Boot 会使用它. 如果您的配置定义了实例名称,Spring Boot 会尝试查找现有的实例,而不是创建新实例.

您还可以指定通过配置使用的 Hazelcast 配置文件,如以下示例所示:

spring:
  hazelcast:
    config: "classpath:config/my-hazelcast.xml"

否则,Spring Boot 会尝试从默认位置查找 Hazelcast 配置: 工作目录或 classpath 根目录中的 hazelcast.xml,或相同位置中的 .yaml 文件. 我们还检查是否设置了 hazelcast.config 系统属性. 有关更多详细信息,请参见 Hazelcast documentation.

Spring Boot 还为 Hazelcast 提供了缓存支持. 如果启用了缓存,HazelcastInstance 将自动包装在 CacheManager 实现中.

3. Quartz Scheduler

Spring Boot 提供了几种使用 Quartz 调度器的便捷方式,它们来自 spring-boot-starter-quartz “Starter”. 如果 Quartz 可用,则 Spring Boot 将自动配置 Scheduler (通过 SchedulerFactoryBean 抽象) .

自动选取以下类型的 Bean 并将其与 Scheduler 关联起来:

  • JobDetail: 定义一个特定的 job. 可以使用 JobBuilder API 构建 JobDetail 实例.

  • Calendar.

  • Trigger: 定义何时触发 job.

默认使用内存存储方式的 JobStore. 但如果应用程序中有 DataSource bean,并且配置了 spring.quartz.job-store-type 属性,则可以配置基于 JDBC 的存储,如下所示:

spring:
  quartz:
    job-store-type: "jdbc"

使用 JDBC 存储时,可以在启动时初始化 schema (表结构) ,如下所示:

spring:
  quartz:
    jdbc:
      initialize-schema: "always"
默认将使用 Quartz 库提供的标准脚本检测并初始化数据库. 这些脚本会删除现有表,在每次重启时删除所有触发器. 可以通过设置 spring.quartz.jdbc.schema 属性来提供自定义脚本.

要让 Quartz 使用除应用程序主 DataSource 之外的 DataSource,请声明一个 DataSource bean,使用 @QuartzDataSource 注解其 @Bean 方法. 这样做可确保 SchedulerFactoryBean 和 schema 初始化都使用 Quartz 指定的 DataSource.类似地,要让 Quartz 使用应用程序的主 TransactionManager 之外的 TransactionManager 来声明 TransactionManager bean,并用 @QuartzTransactionManager 注解其 @Bean 方法.

默认情况下,配置创建的 job 不会覆盖已从持久 job 存储读取的已注册的 job. 要启用覆盖现有的 job 定义,请设置 spring.quartz.overwrite-existing-jobs 属性.

Quartz 调取器配置可以使用 spring.quartz 属性和 SchedulerFactoryBeanCustomizer bean 进行自定义,它们允许以编程方式的 SchedulerFactoryBean 自定义. 可以使用 spring.quartz.properties.* 自定义高级 Quartz 配置属性.

需要强调的是,Executor bean 与调度程序没有关联,因为 Quartz 提供了通过 spring.quartz.properties 配置调度器的方法. 如果需要自定义 Actuator ,请考虑实现 SchedulerFactoryBeanCustomizer.

job 可以定义 setter 以注入数据映射属性. 也可以以类似的方式注入常规的 bean,如下所示:

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

import org.springframework.scheduling.quartz.QuartzJobBean;

public class MySampleJob extends QuartzJobBean {

    // fields ...

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

}

4. 发送邮件

Spring Framework 提供了一个使用 JavaMailSender 接口发送电子邮件的简单抽象,Spring Boot 为其提供了自动配置以及一个 starter 模块.

有关如何使用 JavaMailSender 的详细说明,请参阅 参考文档.

如果 spring.mail.host 和相关库 (由 spring-boot-starter-mail 定义) 可用,则创建默认的 JavaMailSender (如果不存在) . 可以通过 spring.mail 命名空间中的配置项进一步自定义发件人. 有关更多详细信息,请参阅 MailProperties.

特别是,某些默认超时时间的值是无限的,您可能想更改它以避免线程被无响应的邮件服务器阻塞,如下示例所示:

spring:
  mail:
    properties:
      "[mail.smtp.connectiontimeout]": 5000
      "[mail.smtp.timeout]": 3000
      "[mail.smtp.writetimeout]": 5000

也可以使用 JNDI 中的现有 Session 配置一个 JavaMailSender:

spring:
  mail:
    jndi-name: "mail/Session"

设置 jndi-name 时,它优先于所有其他与 Session 相关的设置.

5. Validation

只要 classpath 上存在 JSR-303 实现 (例如 Hibernate 验证器) ,就会自动启用 Bean Validation 1.1 支持的方法验证功能. 这允许 bean 方法在其参数和/或返回值上使用 javax.validation 约束进行注解. 带有此类注解方法的目标类需要在类级别上使用 @Validated 进行注解,以便搜索其内联约束注解的方法.

例如,以下服务触发第一个参数的验证,确保其大小在 8 到 10 之间:

import javax.validation.constraints.Size;

import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

@Service
@Validated
public class MyBean {

    public Archive findByCodeAndAuthor(@Size(min = 8, max = 10) String code, Author author) {
        return ...
    }

}

解析 constraint messages 中的 {parameters} 时使用应用程序的 MessageSource。 这允许您使用 您的应用程序的 messages.properties 文件 用于 Bean 验证消息。 解析参数后,使用 Bean Validation 的默认插值器完成消息插值。

6. 调用 REST Services

如果您的应用程序需要调用远程 REST 服务,这可以使用 Spring Framework 的 RestTemplate 或 WebClient 类.

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

}
RestTemplateBuilder 包含许多可用于快速配置 RestTemplate 的方法. 例如,要添加 BASIC auth 支持,可以使用 builder.basicAuthentication("user", "password").build().

6.1.1. 自定义 RestTemplate

RestTemplate 自定义有三种主要方法,具体取决于您希望自定义的程度.

要想自定义的作用域尽可能地窄,请注入自动配置的 RestTemplateBuilder,然后根据需要调用其方法. 每个方法调用都返回一个新的 RestTemplateBuilder 实例,因此自定义只会影响当前构建器.

要在应用程序作用域内添加自定义配置,请使用 RestTemplateCustomizer bean. 所有这些 bean 都会自动注册到自动配置的 RestTemplateBuilder,并应用于使用它构建的所有模板.

以下示例展示了一个 customizer,它为除 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。为了防止关闭 RestTemplateBuilder 的自动配置,并防止任何 RestTemplateCustomizer bean 被使用,请确保使用 RestTemplateBuilderConfigurer 配置您的自定义实例。下面的示例公开了一个 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));
    }

}

最极端 (也很少使用) 的选择是创建自己的 RestTemplateBuilder bean. 这样做会关闭 RestTemplateBuilder 的自动配置,并阻止使用任何 RestTemplateCustomizer bean.

6.2. WebClient

如果在 classpath 上存在 Spring WebFlux,则还可以选择使用 WebClient 来调用远程 REST 服务. 与 RestTemplate 相比,该客户端更具函数式风格并且完全响应式. 您可以在 Spring Framework 文档的相关部分中了解有关 WebClient 的更多信息.

Spring Boot 为您创建并预配置了一个 WebClient.Builder. 强烈建议将其注入您的组件中并使用它来创建 WebClient 实例. Spring Boot 配置该构建器以共享 HTTP 资源,以与服务器相同的方式反射编解码器设置 (请参阅 WebFlux 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);
    }

}

6.2.1. WebClient 运行时

Spring Boot 将自动检测用于驱动 WebClientClientHttpConnector,具体取决于应用程序 classpath 上可用的类库. 目前支持 Reactor Netty ,Apache HttpClient 和 Jetty RS 客户端.

默认情况下 spring-boot-starter-webflux starter 依赖于 io.projectreactor.netty:reactor-netty,它包含了服务器和客户端的实现. 如果您选择将 Jetty 用作响应式服务器,则应添加 Jetty Reactive HTTP 客户端库依赖 org.eclipse.jetty:jetty-reactive-httpclient. 服务器和客户端使用相同的技术具有一定优势,因为它会自动在客户端和服务器之间共享 HTTP 资源.

开发人员可以通过提供自定义的 ReactorResourceFactoryJettyResourceFactory bean 来覆盖 Jetty 和 Reactor Netty 的资源配置 —— 这将同时应用于客户端和服务器.

如果您只希望覆盖客户端选项,则可以定义自己的 ClientHttpConnector bean 并完全控制客户端配置.

您可以在 Spring Framework 参考文档中了解有关 WebClient 配置选项的更多信息.

6.2.2. 自定义 WebClient

WebClient 自定义有三种主要方法,具体取决于您希望自定义的程度.

要想自定义的作用域尽可能地窄,请注入自动配置的 WebClient.Builder,然后根据需要调用其方法. WebClient.Builder 实例是有状态的: 构建器上的任何更改都会影响到之后所有使用它创建的客户端. 如果要使用相同的构建器创建多个客户端,可以考虑使用 WebClient.Builder other = builder.clone(); 的方式克隆构建器.

要在应用程序作用域内对所有 WebClient.Builder 实例添加自定义,可以声明 WebClientCustomizer bean 并在注入点局部更改 WebClient.Builder.

最后,您可以回退到原始 API 并使用 WebClient.create(). 在这种情况下,不会应用自动配置或 WebClientCustomizer.

7. Web Services

Spring Boot 提供 Web Service 自动配置,因此您要做的就是定义 Endpoints.

可以使用 spring-boot-starter-webservices 模块轻松访问 Spring Web Services 功能.

可以分别为 WSDL 和 XSD 自动创建 SimpleWsdl11DefinitionSimpleXsdSchema bean. 为此,请配置其位置,如下所示:

spring:
  webservices:
    wsdl-locations: "classpath:/wsdl"

7.1. 使用 WebServiceTemplate 调用 Web Service

如果您需要从应用程序调用远程 Web 服务,则可以使用 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 使用 classpath 上的可用 HTTP 客户端库检测合适的基于 HTTP 的 WebServiceMessageSender. 您还可以按如下方式自定义读取和连接的超时时间:

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

}

8. JTA 分布式事务

Spring Boot 通过使用 Atomikos 嵌入式事务管理器来支持跨多个 XA 资源的分布式 JTA 事务, 并弃用了 Bitronix 嵌入式事务管理器的支持,并在未来版本中删除. 部署在某些 Java EE 应用服务器 (Application Server) 上也支持 JTA 事务.

当检测到 JTA 环境时,Spring 的 JtaTransactionManager 将用于管理事务. 自动配置的 JMS、DataSource 和 JPA bean 已升级为支持 XA 事务. 您可以使用标准的 Spring 方式 (例如 @Transactional) 来使用分布式事务. 如果您处于 JTA 环境中并且仍想使用本地事务,则可以将 spring.jta.enabled 属性设置为 false 以禁用 JTA 自动配置.

8.1. 使用 Atomikos 事务管理器

Atomikos 是一个流行的开源事务管理器,可以嵌入到 Spring Boot 应用程序中. 您可以使用 spring-boot-starter-jta-atomikos starter 来获取相应的 Atomikos 库. Spring Boot 自动配置 Atomikos 并确保将合适的 depends-on 设置应用于 Spring bean,以确保启动和关闭顺序正确.

默认情况下,Atomikos 事务日志将写入应用程序主目录 (应用程序 jar 文件所在的目录) 中的 transaction-logs 目录. 您可以通过在 application.properties 文件中设置 spring.jta.log-dir 属性来自定义此目录的位置. 也可用 spring.jta.atomikos.properties 开头的属性来自定义 Atomikos UserTransactionServiceImp. 有关完整的详细信息,请参阅 AtomikosProperties Javadoc.

为确保多个事务管理器可以安全地协调相同的资源管理器,必须为每个 Atomikos 实例配置唯一 ID. 默认情况下,此 ID 是运行 Atomikos 的计算机的 IP 地址. 在生产环境中要确保唯一性,应为应用程序的每个实例配置 spring.jta.transaction-manager-id 属性,并使用不同的值.

8.2. 使用 Java EE 管理的事务管理器

如果将 Spring Boot 应用程序打包为 warear 文件并将其部署到 Java EE 应用程序服务器,则可以使用应用程序服务器的内置事务管理器. Spring Boot 尝试通过查找常见的 JNDI 位置 (java:comp/UserTransactionjava:comp/TransactionManager 等) 来自动配置事务管理器. 如果使用应用程序服务器提供的事务服务, 通常还需要确保所有资源都由服务器管理并通过 JNDI 暴露. Spring Boot 尝试通过在 JNDI 路径 (java:/JmsXAjava:/JmsXA) 中查找 ConnectionFactory 来自动配置 JMS,并且可以使用 spring.datasource.jndi-name 属性 属性来配置 DataSource.

8.3. 混合使用 XA 与非 XA JMS 连接

使用 JTA 时,primary (主) JMS ConnectionFactory bean 可识别 XA 并参与分布式事务.您可以注入到您的 bean 中,而无需使用任何 @Qualifier

public MyBean(ConnectionFactory connectionFactory) {
    // ...
}

在某些情况下,您可能希望使用非 XA ConnectionFactory 处理某些 JMS 消息. 例如,您的 JMS 处理逻辑可能需要比 XA 超时时间更长的时间.

如果要使用非 XA ConnectionFactory,可以使用 nonXaJmsConnectionFactory bean:

public MyBean(@Qualifier("nonXaJmsConnectionFactory") ConnectionFactory connectionFactory) {
    // ...
}

为了保持一致性,提供的 jmsConnectionFactory bean 还需要使用 xaJmsConnectionFactory 别名.

public MyBean(@Qualifier("xaJmsConnectionFactory") ConnectionFactory connectionFactory) {
    // ...
}

8.4. 支持嵌入式事务管理器

XAConnectionFactoryWrapperXADataSourceWrapper 接口可用于支持其他嵌入式事务管理器. 接口负责包装 XAConnectionFactoryXADataSource bean,并将它们暴露为普通的 ConnectionFactoryDataSource bean,它们透明地加入分布式事务. DataSource 和 JMS 自动配置使用 JTA 变体,前提是您需要有一个 JtaTransactionManager bean 和在 ApplicationContext 中注册有的相应 XA 包装器 (wrapper) bean.

AtomikosXAConnectionFactoryWrapperAtomikosXADataSourceWrapper 为如何编写 XA 包装器提供了很好示例.

9. 下一步

您现在应该对 Spring Boot 的 core features 以及 Spring Boot 通过自动配置提供支持的各种技术有了很好的了解。

接下来的几节将详细介绍如何将应用程序部署到云平台。 您可以在下一节中阅读有关 构建容器映像 或跳到 生产就绪功能 部分。