# IO

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

# 1. 缓存

Spring 框架提供了对向应用程序透明地添加缓存的支持。在其核心部分,抽象将缓存应用于方法,从而减少了基于缓存中可用信息的执行次数。缓存逻辑是透明地应用的,不会对调用程序造成任何干扰。 Spring 只要使用@EnableCaching注释启用缓存支持,启动就会自动配置缓存基础设施。

查看 Spring 框架引用的相关部分 (opens new window)以获得更多详细信息。

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

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 注释(例如@CacheResult)。但是,我们强烈建议你不要混合和匹配 Spring 缓存和 JCache 注释。

如果不添加任何特定的缓存库, Spring 引导自动配置一个简单提供者,它在内存中使用并发映射。当需要缓存时(例如前面示例中的piDecimals),此提供程序将为你创建它。简单的提供者并不是真正推荐用于生产使用的,但是它对于开始使用和确保你了解这些特性是非常好的。当你决定使用缓存提供程序时,请务必阅读其文档,以了解如何配置应用程序使用的缓存。几乎所有的提供程序都要求你显式地配置应用程序中使用的每个缓存。一些提供了一种方法来定制由spring.cache.cache-names属性定义的默认缓存。

还可以透明地从缓存中获取update (opens new window)evict (opens new window)数据。

# 1.1.支持的缓存提供程序

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

如果你没有定义类型CacheManager的 Bean 或类型CacheResolvercacheResolver(参见[CachingConfigurer](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/javadoc-api/org/springframework/cache/annotation/cachingconfigurer.html)), Spring boot 将尝试检测以下提供者(按指定的顺序):

  1. Generic

  2. JCache(JSR-107)(Ehcache3、黑泽尔卡斯特、Infinispan 等)

  3. Ehcache2.x

  4. Hazelcast

  5. Infinispan

  6. Couchbase

  7. Redis

  8. Caffeine

  9. Simple

通过设置spring.cache.type属性,也可以特定的缓存提供程序。
如果在某些环境(例如测试)中需要完全禁用缓存,请使用此属性。
使用spring-boot-starter-cache“starter”快速添加基本的缓存依赖关系。
starter 带来spring-context-support
如果手动添加依赖关系,则必须包含spring-context-support才能使用 JCache、EhCache2.x 或咖啡因支持。

如果CacheManager是通过 Spring 引导自动配置的,那么可以通过公开实现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);
    }

}

在前面的示例中,需要自动配置ConcurrentMapCacheManager。,如果不是这样(要么你提供了自己的配置,要么自动配置了其他缓存提供程序),则根本不会调用自定义程序,
你可以根据需要配置任意多的自定义程序,你还可以使用@OrderOrdered来订购它们。

# 1.1.1.泛型

如果上下文定义至少oneorg.springframework.cache.Cache Bean,则使用泛型缓存。创建了包装该类型的所有 bean 的CacheManager

# 1.1.2.JCache(JSR-107)

JCache (opens new window)是通过在 Classpath 上存在javax.cache.spi.CachingProvider(即在 Classpath 上存在符合 JSR-107 的缓存库)来引导的,而JCacheCacheManager是由spring-boot-starter-cache“starter”提供的。各种兼容的库是可用的,并且 Spring Boot 为 Ehcache3、HazelCast 和 Infinispan 提供了依赖管理。还可以添加任何其他兼容的库。

可能会出现多个提供程序,在这种情况下,必须显式地指定提供程序。 Spring 即使 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

Yaml

# Only necessary if more than one provider is present
spring:
  cache:
    jcache:
      provider: "com.example.MyCachingProvider"
      config: "classpath:example.xml"
Spring 当缓存库同时提供本机实现和 JSR-107 支持时,引导更倾向于 JSR-107 支持,这样,如果切换到不同的 JSR-107 实现,就可以使用相同的功能。
Spring 引导具有对 Hazelcast 的一般支持
如果单个HazelcastInstance是可用的,那么它也会自动被用于CacheManager,除非指定了spring.cache.jcache.config属性。

有两种方法可以定制底层javax.cache.cacheManager:

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

  • org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizerbean 是用CacheManager的引用调用的,用于完全定制。

如果定义了一个标准javax.cache.CacheManager Bean,则它会自动包装在抽象所期望的org.springframework.cache.CacheManager实现中。
不会对其应用进一步的自定义。

# 1.1.3.Ehcache2.x

如果在 Classpath 的根位置可以找到名为ehcache.xml的文件,则使用EhCache (opens new window)2.x。如果找到了 Ehcache2.x,则使用spring-boot-starter-cache“starter”提供的EhCacheCacheManager引导缓存管理器。还可以提供一个替代配置文件,如以下示例所示:

属性

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

Yaml

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

# 1.1.4.黑泽尔卡斯特

Spring 引导有对 Hazelcast 的一般支持。如果HazelcastInstance已被自动配置,则它将自动包装在CacheManager中。

# 1.1.5.无限极

Infinispan (opens new window)没有默认的配置文件位置,因此必须显式地指定它。否则,将使用默认的引导程序。

属性

spring.cache.infinispan.config=infinispan.xml

Yaml

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

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

Spring boot 中对 Infinispan 的支持仅限于嵌入式模式,非常基本。
如果你想要更多选项,应该使用官方的 Infinispan Spring boot starter。
有关更多详细信息,请参见Infinispan 的文档 (opens new window)

# 1.1.6.Couchbase

如果 Spring 数据 Couchbase 是可用的,并且 Couchbase 是configured,则将自动配置CouchbaseCacheManager。通过设置spring.cache.cache-names属性,可以在启动时创建额外的缓存,并且可以使用spring.cache.couchbase.*属性配置缓存默认值。例如,下面的配置使用一个 10 分钟的条目过期创建cache1cache2缓存:

属性

spring.cache.cache-names=cache1,cache2
spring.cache.couchbase.expiration=10m

Yaml

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 (opens new window)可用并已配置,则自动配置RedisCacheManager。通过设置spring.cache.cache-names属性,可以在启动时创建额外的缓存,并且可以使用spring.cache.redis.*属性配置缓存默认值。例如,下面的配置使用 10 分钟的活下去的时间创建cache1cache2缓存:

属性

spring.cache.cache-names=cache1,cache2
spring.cache.redis.time-to-live=10m

Yaml

spring:
  cache:
    cache-names: "cache1,cache2"
    redis:
      time-to-live: "10m"
默认情况下,会添加一个密钥前缀,这样,如果两个独立的缓存使用相同的密钥,则 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 (opens new window)是对番石榴缓存的 Java8 重写,取代了对番石榴的支持。如果存在咖啡因,则自动配置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
spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s

Yaml

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.简单

如果找不到其他提供程序,则配置一个使用ConcurrentHashMap作为缓存存储的简单实现。如果应用程序中不存在缓存库,这是默认的。默认情况下,缓存是根据需要创建的,但是你可以通过设置cache-names属性来限制可用缓存的列表。例如,如果只想要cache1cache2缓存,请将cache-names属性设置如下:

属性

spring.cache.cache-names=cache1,cache2

Yaml

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

如果你这样做,而你的应用程序使用了一个未列出的缓存,那么当需要缓存时,它会在运行时失败,而不是在启动时。如果使用未声明的缓存,这类似于“真正的”缓存提供者的行为。

# 1.1.10.无

@EnableCaching出现在你的配置中时,还需要一个合适的缓存配置。如果需要在某些环境中完全禁用缓存,请强制缓存类型none使用无操作实现,如以下示例所示:

属性

spring.cache.type=none

Yaml

spring:
  cache:
    type: "none"

# 2. Hazelcast

如果Hazelcast (opens new window)在 Classpath 上,并且找到了合适的配置, Spring 引导自动配置一个HazelcastInstance,你可以将其注入到应用程序中。

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

  • acom.hazelcast.client.config.ClientConfig Bean 的存在。

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

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

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

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

Spring Boot 同时支持 Hazelcast4 和 Hazelcast3。
如果降级为 Hazelcast3,则应将hazelcast-client添加到 Classpath 中以配置客户端。

如果不能创建客户端, Spring 引导将尝试配置嵌入式服务器。如果你定义了com.hazelcast.config.Config Bean, Spring boot 就会使用它。如果你的配置定义了一个实例名, Spring 引导将尝试定位一个现有的实例,而不是创建一个新的实例。

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

属性

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

Yaml

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

否则, Spring 引导将尝试从默认位置查找 Hazelcast 配置:在工作目录中或 Classpath 的根目录中的hazelcast.xml,或在相同位置中的.yaml对应位置。我们还检查hazelcast.config系统属性是否已设置。有关更多详细信息,请参见Hazelcast 文档 (opens new window)

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

# 3. 石英调度器

Spring Boot 为使用石英调度器 (opens new window)提供了几种便利,包括spring-boot-starter-quartz“starter”。如果 Quartz 可用,则自动配置Scheduler(通过SchedulerFactoryBean抽象)。

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

  • JobDetail:定义特定的作业。JobDetail实例可以用JobBuilderAPI 构建。

  • Calendar.

  • Trigger:定义触发特定作业的时间。

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

属性

spring.quartz.job-store-type=jdbc

Yaml

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

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

属性

spring.quartz.jdbc.initialize-schema=always

Yaml

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

要让 Quartz 使用应用程序的主DataSource以外的DataSource,请声明一个DataSource Bean,并用@QuartzDataSource注释其@Bean方法。这样做可以确保SchedulerFactoryBean和模式初始化都使用特定于石英的DataSource。类似地,要使 Quartz 使用一个TransactionManager而不是应用程序的主TransactionManager声明一个TransactionManager Bean,用@QuartzTransactionManager注释其@Bean方法。

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

Quartz 调度器配置可以使用spring.quartz属性和SchedulerFactoryBeanCustomizerbean 进行自定义,这允许程序化的SchedulerFactoryBean自定义。可以使用spring.quartz.properties.*定制高级石英配置属性。

特别地,Executor Bean 不与调度程序相关联,因为 Quartz 提供了一种通过spring.quartz.properties配置调度程序的方法。
如果需要定制任务执行器,请考虑实现SchedulerFactoryBeanCustomizer

作业可以定义 setter 来注入数据映射属性。普通的豆子也可以以类似的方式注入,如以下示例所示:

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 框架提供了用于通过使用JavaMailSender接口发送电子邮件的抽象,并且 Spring 启动为其提供了自动配置以及一个启动模块。

有关如何使用JavaMailSender的详细说明,请参见参考文献 (opens new window)

如果spring.mail.host和相关的库(由spring-boot-starter-mail定义)是可用的,则如果不存在,则创建一个默认的JavaMailSender。发送方可以通过spring.mail名称空间中的配置项进行进一步定制。详见[Mail属性](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofigure/mail/mailproperties.java)了解更多详情。

特别是,某些默认的超时值是无限的,你可能希望对其进行更改,以避免线程被无响应的邮件服务器阻塞,如以下示例所示:

属性

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

Yaml

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

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

属性

spring.mail.jndi-name=mail/Session

Yaml

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

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

# 5. 验证

Bean 验证 1.1 支持的方法验证功能是自动启用的,只要在 Classpath 上有一个 JSR-303 实现(例如 Hibernate 验证器)。这使得 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文件](features.html#features.internationalization)来获取 Bean 验证消息。一旦解决了参数问题,就使用 Bean 验证的默认内插器完成消息插值。

# 6. 呼叫 REST 服务

如果你的应用程序调用远程 REST 服务, Spring boot 使用RestTemplateWebClient使这一点非常方便。

# 6.1.RESTTemplate

如果需要从应用程序调用远程 REST 服务,可以使用 Spring 框架的[RestTemplate](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/javadoc-api/org/springframework/web/client/resttemplate.html)类。由于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
例如,要添加基本的 auth 支持,可以使用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。这样做将取代自动配置的构建器。如果你希望将任何RestTemplateCustomizerbean 应用到你的自定义构建器,正如自动配置所做的那样,请使用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。除了替换自动配置的生成器外,这还可以防止使用任何RestTemplateCustomizerbean。

# 6.2.WebClient

如果你的 Classpath 上有 Spring WebFlux,你还可以选择使用WebClient来调用远程 REST 服务。与RestTemplate相比,该客户机具有更多的功能感觉,并且完全具有反应性。你可以在专用的section in the Spring Framework docs (opens new window)中了解有关WebClient的更多信息。

Spring 引导为你创建并预配置一个WebClient.Builder。强烈建议将其注入到组件中,并使用它来创建WebClient实例。 Spring 引导正在配置该构建器来共享 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 启动将自动检测使用哪个ClientHttpConnector来驱动WebClient,这取决于应用程序上可用的库 Classpath。目前,反应堆网络和 Jetty RS 客户端是受支持的。

默认情况下,spring-boot-starter-webfluxstarter 依赖于io.projectreactor.netty:reactor-netty,这带来了服务器和客户端实现。如果你选择使用 Jetty 作为反应性服务器,那么你应该在 Jetty 反应性 HTTP 客户库上添加一个依赖项,org.eclipse.jetty:jetty-reactive-httpclient。使用同样的技术对服务器和客户端都有它的优点,因为它会自动地在客户端和服务器之间共享 HTTP 资源。

开发人员可以通过提供自定义ReactorResourceFactoryJettyResourceFactory Bean 来覆盖 Jetty 和反应器网络的资源配置-这将同时应用于客户机和服务器。

如果你希望为客户机重写该选择,那么你可以定义自己的ClientHttpConnector Bean 并对客户机配置拥有完全的控制权。

你可以在 Spring 框架参考文档中了解有关[WebClient配置选项的更多信息](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/reference/html/web-active.html#webflux-client-builder)。

# 6.2.2.WebClient 自定义

对于WebClient定制,有三种主要的方法,这取决于你希望定制应用的范围。

要使任何自定义的范围尽可能窄,请插入自动配置的WebClient.Builder,然后根据需要调用其方法。WebClient.Builder实例是有状态的:构建器上的任何更改都会反映在随后使用它创建的所有客户机中。如果你想用同一个构建器创建多个客户机,还可以考虑用WebClient.Builder other = builder.clone();克隆构建器。

要对所有WebClient.Builder实例进行应用程序范围的加性定制,你可以声明WebClientCustomizerbean,并在注入点本地更改WebClient.Builder

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

# 7. Web 服务

Spring 启动提供了 Web 服务自动配置,因此你所必须做的就是定义你的Endpoints

Spring Web Services features (opens new window)模块可以轻松访问spring-boot-starter-webservices

SimpleWsdl11DefinitionSimpleXsdSchemabean 可以分别为你的 WSDL 和 XSD 自动创建。要做到这一点,请配置它们的位置,如以下示例所示:

Properties

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

Yaml

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

# 7.1.使用 WebServiceTemplate 调用 Web 服务

如果需要从应用程序调用远程 Web 服务,可以使用[WebServiceTemplate](https://DOCS. Spring.io/ Spring-ws/DOCS/3.1.2/reference/html/#client-web-service-template)类。由于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 通过使用Atomikos (opens new window)嵌入式事务管理器,启动支持跨多个 XA 资源的分布式 JTA 事务。当部署到合适的 Java EE 应用程序服务器时,也支持 JTA 事务。

当检测到 JTA 环境时,将使用 Spring 的JtaTransactionManager来管理事务。升级了自动配置的 JMS、数据源和 JPA bean,以支持 XA 事务。你可以使用标准 Spring 习惯用法,例如@Transactional,来参与分布式事务。如果你处于 JTA 环境中,并且仍然希望使用本地事务,那么可以将spring.jta.enabled属性设置为false以禁用 JTA 自动配置。

# 8.1.使用 Atomikos 事务管理器

Atomikos (opens new window)是一种流行的开源事务管理器,它可以嵌入到你的 Spring 引导应用程序中。你可以使用spring-boot-starter-jta-atomikos启动器来获取适当的 Atomikos 库。 Spring 引导自动配置 Atomikos,并确保将适当的depends-on设置应用到你的 Spring bean,以进行正确的启动和关闭排序。

默认情况下,Atomikos 事务日志被写入应用程序主目录(应用程序 jar 文件所在的目录)中的transaction-logs目录。可以通过在application.properties文件中设置spring.jta.log-dir属性来自定义此目录的位置。以spring.jta.atomikos.properties开头的属性也可用于自定义 AtomikosUserTransactionServiceImp。有关完整的详细信息,请参见[AtomikosPropertiesJavadoc](https://DOCS. Spring.io/ Spring-boot/DOCS/2.6.4/api/org/springframework/boot/jta/atomikos/atomikosproperties.html)。

为了确保多个事务管理器能够安全地协调相同的资源管理器,每个 Atomikos 实例都必须配置一个唯一的 ID,
默认情况下,这个 ID 是运行 Atomikos 的机器的 IP 地址,
以确保生产中的唯一性,你应该为应用程序的每个实例配置具有不同值的spring.jta.transaction-manager-id属性。

# 8.2.使用 Java EE 托管事务管理器

如果你将 Spring 引导应用程序打包为warear文件并将其部署到 Java EE 应用程序服务器,则可以使用应用程序服务器的内置事务管理器。 Spring 引导试图通过查看常见的 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 时,主 JMSConnectionFactory Bean 是可感知 XA 的,并参与分布式事务。你可以在不需要使用任何@Qualifier的情况下注入到你的 Bean:

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

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

如果要使用非 XAConnectionFactory,则可以使用nonXaJmsConnectionFactory Bean:

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

为了保持一致性,jmsConnectionFactory Bean 还通过使用 Bean 别名xaJmsConnectionFactory来提供:

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

# 8.4.支持另一种嵌入式事务管理器

[XAConnectionFactoryWrapper](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot/SRC/main/java/java/org/springframework/boot/boot/jms/xaconnectionfactorywrapper.java.java)和[<<gtr="336"/>](https://github.com/ Spring-projects/[ Spring-tree/-tree/tree/v2.6.4/ Spring-boo 接口负责包装XAConnectionFactoryXADataSourcebean,并将它们公开为常规的ConnectionFactoryDataSourcebean,这些 bean 透明地登记在分布式事务中。数据源和 JMS 自动配置使用 JTA 变体,只要你有JtaTransactionManager Bean 和在ApplicationContext中注册的适当的 XA 包装 bean。

AtomikosxaconnectionFactoryWrapper (opens new window)Atomikosxadatasourcewrapper (opens new window)为如何编写 XA 包装器提供了很好的示例。

# 9. 接下来要读什么?

你现在应该对 Spring boot 的核心功能以及 Spring boot 通过自动配置提供支持的各种技术有了很好的了解。

接下来的几节将详细介绍如何将应用程序部署到云平台上。你可以在下一节中阅读有关构建容器图像的内容,也可以跳到可投入生产的功能的部分。