20220523 IO

前言

文档地址

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

1. Caching

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-107JCache )注解(例如@CacheResult)。但是,我们强烈建议您不要混合使用 Spring Cache 和 JCache 注解。

如果您不添加任何特定的缓存库,Spring Boot 会自动配置一个使用内存中的并发 map 的 简单供应商 。当需要缓存时(例如前面的示例 piDecimals ),此供应商将为您创建它。实际上,不建议将简单供应商用于生产用途,但是它对于入门并确保您了解功能非常有用。确定要使用的缓存供应商后,请确保阅读其文档,以了解如何配置应用程序使用的缓存。几乎所有供应商都要求您显式配置在应用程序中使用的每个缓存。有些提供了通过 spring.cache.cache-names 属性自定义默认缓存。

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

1.1. 支持的缓存供应商

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

如果尚未定义类型为 CacheManagerCacheResolver 且名称为 cacheResolver 的 bean (请参阅 CachingConfigurer ),Spring Boot 会尝试检测以下供应商(按指示的顺序,参考 org.springframework.boot.autoconfigure.cache.CacheType ):

  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

此外, Spring Boot for Apache Geode 提供了 使用 Apache Geode 作为缓存供应商的自动配置

可以通过设置 spring.cache.type 属性来强制使用特定的缓存供应商。如果您需要在某些环境(例如测试)中完全 禁用缓存 ,请使用此属性。

使用 spring-boot-starter-cache 启动器快速添加基本的缓存依赖项。启动器带进来 spring-context-support 。如果手动添加依赖项,则必须包括 spring-context-support 才能使用 JCacheEhCache 2.xCaffeine 支持。

如果 Spring Boot 自动配置了 CacheManager ,您可以通过公开实现 CacheManagerCustomizer 接口的 bean ,在完全初始化之前进一步调整其配置。下面的示例设置一个标志,表示 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 。如果不是这种情况(您提供了自己的配置,或者自动配置了其他缓存供应商),则根本不会调用定制器。您可以根据需要拥有任意数量的定制器,也可以使用 @OrderOrdered 对其进行排序。

1.1.1. Generic

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

1.1.2. JCache(JSR-107)

JCache 通过类路径上的 javax.cache.spi.CachingProvider 进行引导(即,类路径上存在符合 JSR-107 的缓存库),并且 JCacheCacheManagerspring-boot-starter-cache 启动器提供。有各种兼容的库可用,Spring Boot 为 Ehcache 3HazelcastInfinispan 提供了依赖管理。也可以添加任何其他兼容的库。

可能会出现多个供应商,在这种情况下,必须明确指定供应商。即使 JSR-107 标准没有强制采用标准化的方式来定义配置文件的位置,Spring Boot 也会尽最大努力适应使用实现细节设置缓存,如以下示例所示:

# Only necessary if more than one provider is present
spring.cache.jcache.provider=com.example.MyCachingProvider
spring.cache.jcache.config=classpath:example.xml

当缓存库同时提供原生实现和 JSR-107 支持时,Spring Boot 会首选 JSR-107 支持,因此,如果您切换到其他 JSR-107 实现,则可以使用相同的功能。

Spring Boot 对 Hazelcast 具有常规支持 。如果单个 HazelcastInstance 可用,则除非指定了 spring.cache.jcache.config 属性,否则它也会自动重用于 CacheManager

有两种方法可以自定义底层 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

如果可以在类路径的根目录找到名为 ehcache.xml 的文件,则使用 EhCache2.x 。如果找到 EhCache 2.x ,则使用 spring-boot-starter-cache 启动器提供的 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,那么它将用于自定义缓存。

Spring Boot 对 Infinispan 的支持仅限于嵌入式模式,并且非常基础。如果您需要更多选择,则应该使用官方的 Infinispan Spring Boot启动器。有关更多详细信息,请参见 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
spring.cache.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
spring.cache.redis.time-to-live=10m

默认情况下,会添加 key 前缀,这样,如果两个单独的缓存使用相同的 key ,则 Redis 不会有重叠的密钥,也不会返回无效值。如果您创建自己的 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 是对 Guava 缓存的 Java 8 重写,它取代了对 Guava 的支持。如果存在 Caffeine ,则 CaffeineCacheManager 将自动配置(由 spring-boot-starter-cache 启动器提供)。缓存可以在启动时通过设置 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
spring.cache.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 缓存类型,使用无操作实现,如以下示例所示:

spring.cache.type=none

2. Hazelcast

如果 Hazelcast 位于类路径上,并且找到了合适的配置,则 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 配置:在工作目录中或类路径的根目录中的 hazelcast.xml ,或在相同位置的对应目录中的 .yaml 。我们还检查是否设置了 hazelcast.config 系统属性。有关更多详细信息,请参见 Hazelcast文档

Spring Boot 还具有 对 Hazelcast 的显式缓存支持 。如果启用了缓存,则会自动将 HazelcastInstance 包装在 CacheManager 实现中。

3. Quartz Scheduler

Spring Boot 为使用 Quartz Scheduler 提供了许多便利,包括 spring-boot-starter-quartz 启动器。如果 Quartz 可用,则自动配置 Scheduler(通过 SchedulerFactoryBean 抽象)。

以下类型的 Bean 会被自动拾取并与 Scheduler 关联:

  • JobDetail :定义特定的作业(Job)。 JobDetail 实例可以使用 JobBuilder API 构建
  • Calendar
  • Trigger :定义何时触发特定作业

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

spring.quartz.job-store-type=jdbc

使用 JDBC 存储时,可以在启动时初始化模式,如以下示例所示:

spring.quartz.jdbc.initialize-schema=always

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

要让 Quartz 使用应用程序的 main DataSource 之外的其他 DataSource ,请声明一个 DataSource bean ,并用 @QuartzDataSource 注解其 @Bean 方法。这样做可确保 SchedulerFactoryBean 和模式初始化都使用特定于 Quartz 的 DataSource 。类似地,要让 Quartz 使用 TransactionManager 而非应用程序的 main TransactionManager 声明一个 TransactionManager bean,用 @QuartzTransactionManager 注解它的 @Bean 方法。

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

可以使用 spring.quartz 属性和 SchedulerFactoryBeanCustomizer bean 来定制 Quartz Scheduler 配置,从而允许以编程方式进行 SchedulerFactoryBean 定制。可以使用 spring.quartz.properties.* 来定制高级 Quartz 配置属性。

特别是,Executor bean 没有与调度器相关联,因为 Quartz 通过 spring.quartz.properties 提供了一种配置调度器的方法。如果您需要自定义任务执行器,请考虑实现 SchedulerFactoryBeanCustomizer

作业可以定义 setter 以注入数据映射属性。常规 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);
    }

}

4. Sending Email

Spring Framework 提供了一种使用 JavaMailSender 接口发送电子邮件的简单抽象方法,而 Spring Boot 为其提供了自动配置以及启动器模块。

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

如果 spring.mail.host 和相关库(由 spring-boot-starter-mail 定义)可用,如果 JavaMailSender 不存在则创建默认 JavaMailSender 。可以通过 spring.mail 名称空间中的配置项进一步自定义发送方。请参阅 MailProperties

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

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

还可以使用 JNDI 中现有的 Session 配置 JavaMailSender

spring.mail.jndi-name=mail/Session

当设置了 jndi-name ,它优先于所有其他会话相关的设置。

5. Validation

只要 JSR-303 实现(例如 Hibernate validator)位于类路径上,就会自动启用 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 ...
    }

}

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

6. Calling REST Services

如果您的应用程序调用远程 REST 服务,Spring Boot 使用 RestTemplateWebClient

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 身份验证支持,可以使用 builder.basicAuthentication("user", "password").build()

6.1.1. 自定义 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 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。除了替换自动配置的构建器之外,这还可以防止使用任何 RestTemplateCustomizer bean

6.2. WebClient

如果您的类路径上具有 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 将根据应用程序类路径上可用的库自动检测要使用哪个 ClientHttpConnector 驱动 WebClient 。目前,支持 Reactor NettyJetty RS 客户端。

spring-boot-starter-webflux 启动器默认依赖于 io.projectreactor.netty:reactor-netty ,附带服务器和客户端的实现。如果选择使用 Jetty 作为反应式服务器,则应在 Jetty 反应式 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 Services 自动配置,因此您所要做的就是定义您的 Endpoints

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

可以分别为您的 WSDL 和 XSD 自动创建 SimpleWsdl11DefinitionSimpleXsdSchema bean。为此,请配置它们的位置,如以下示例所示:

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

7.1. 使用 WebServiceTemplate 调用 Web 服务

如果需要从应用程序调用远程 Web 服务,可以使用 WebServiceTemplate 类。由于 WebServiceTemplate 实例通常需要在使用之前进行自定义,因此 Spring Boot 不提供任何单个自动配置的 WebServiceTemplate bean 。但是,它确实会自动配置 WebServiceTemplateBuilder ,可用于在需要时创建 WebServiceTemplate 实例。

下面的代码展示了一个典型的例子:

@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 使用类路径上可用的 HTTP 客户端库检测合适的基于 HTTP 的 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 事务。部署到合适的 Java EE 应用服务器时也支持 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 启动器来引入适当的 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:/XAConnectionFactory)处查找 ConnectionFactory 来自动配置 JMS,您可以使用 spring.datasource.jndi-name 属性 来配置您的 DataSource

8.3. 混合 XA 和非 XA JMS 连接

使用 JTA 时,主 JMS ConnectionFactory bean 是 XA 感知的并参与分布式事务。您可以注入您的 bean 而无需使用任何 @Qualifier

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

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

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

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

为了保持一致性,jmsConnectionFactory bean 也使用 bean 别名 xaJmsConnectionFactory 来提供:

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

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

XAConnectionFactoryWrapperXADataSourceWrapper 接口可用于支持替代的嵌入式事务管理器。这些接口负责包装 XAConnectionFactoryXADataSource bean 并将它们作为常规 ConnectionFactoryDataSource bean 公开,它们透明地注册到分布式事务中。DataSource 和 JMS 自动配置使用 JTA 变体,前提是在 ApplicationContext 中注册了 JtaTransactionManager bean 和适当的 XA 包装器 bean。

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

posted @ 2022-06-08 07:57  流星<。)#)))≦  阅读(35)  评论(0编辑  收藏  举报