Spring Boot IO官方文档中文版
本文为官方文档直译版本。原文链接
Spring Boot IO官方文档中文版
引言
大多数应用程序都需要处理输入和输出问题。当您需要 IO 功能时,Spring Boot 提供了实用工具并与一系列技术集成。本节将介绍缓存和验证等标准 IO 功能,以及调度和分布式事务等更高级的主题。我们还将介绍调用远程 REST 或 SOAP 服务和发送电子邮件。
缓存
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 会自动配置一个使用内存中并发映射的简单提供程序。当需要缓存时(如上例中的 piDecimals
),该提供程序会为您创建缓存。我们不建议在生产中使用简单提供程序,但它非常适合入门和确保您了解其功能。在决定使用哪种缓存提供程序后,请务必阅读其文档,了解如何配置应用程序使用的缓存。几乎所有提供商都要求您明确配置应用程序中使用的每一个缓存。有些提供商提供了自定义由 spring.cache.cache-names
属性定义的默认缓存的方法。
还可以透明地更新或删除缓存中的数据。
支持的缓存提供程序
缓存抽象不提供实际存储,而是依赖于由 org.springframework.cache.Cache
和 org.springframework.cache.CacheManager
接口具体化的抽象。
如果您没有定义 CacheManager
类型的 Bean 或名为 cacheResolver
的 CacheResolver
(请参阅 CachingConfigurer),Spring Boot 会尝试检测以下提供程序(按指定顺序):
- Generic
- JCache (JSR-107)(EhCache 3、Hazelcast、Infinispan 等)。
- Hazelcast
- Infinispan
- Couchbase
- Redis
- Caffeine
- Cache2k
- Simple
此外,Spring Boot for Apache Geode 还提供将 Apache Geode 用作缓存提供程序的自动配置功能。
如果
CacheManager
是由 Spring Boot 自动配置的,则可以通过设置spring.cache.type
属性来强制使用特定的缓存提供程序。如果需要在某些环境(如测试)中使用免操作缓存,请使用此属性。
使用
spring-boot-starter-cache
“Starter” 可快速添加基本的缓存依赖项。启动器引入了spring-context-support
。如果手动添加依赖项,则必须包含spring-context-support
才能使用 JCache 或 Caffeine 支持。
如果 CacheManager
是由 Spring Boot 自动配置的,那么在完全初始化之前,您可以通过公开一个实现 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
。如果不是这种情况(要么是您提供了自己的配置,要么是自动配置了不同的缓存提供者),自定义器根本不会被调用。您可以拥有任意多个自定义器,也可以使用@Order
或Ordered
对它们进行排序。
Generic
如果上下文至少定义了一个 org.springframework.cache.Cache
Bean,就会使用通用缓存。会创建一个封装所有该类型 Bean 的 CacheManager
。
JCache (JSR-107)
JCache 通过类路径上的 javax.cache.spi.CachingProvider
进行引导(即类路径上存在符合 JSR-107 标准的缓存库),JCacheCacheManager
由 spring-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
可用,除非指定了spring.cache.jcache.config
属性,否则CacheManager
也会自动重用该HazelcastInstance
。
自定义底层 javax.cache.cacheManager
有两种方法:
- 可以通过设置 s
pring.cache.cache-names
属性在启动时创建缓存。如果定义了自定义javax.cache.configuration.Configuration
Bean,则可使用该 Bean 对缓存进行自定义。 org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizerBean
会调用CacheManager
的引用进行完全自定义。
如果定义了标准的
javax.cache.CacheManager
Bean,它将被自动封装在抽象所期望的org.springframework.cache.CacheManager
实现中。不会对其进行进一步的定制。
Hazelcast
Spring Boot 普遍支持 Hazelcast。如果 HazelcastInstance
已被自动配置,且 com.hazelcast:hazelcast-spring
位于类路径上,它将自动被封装在 CacheManager
中。
Hazelcast 可用作兼容 JCache 的缓存或兼容 Spring
CacheManager
的缓存。将spring.cache.type
设置为hazelcast
时,Spring Boot 将使用基于CacheManager
的实现。如果要将 Hazelcast 用作符合 JCache 标准的缓存,请将spring.cache.type
设置为jcache
。如果您有多个兼容 JCache 的缓存提供程序,并希望强制使用 Hazelcast,则必须显式设置 JCache 提供程序。
Infinispan
Infinispan 没有默认的配置文件位置,因此必须明确指定。否则,将使用默认的引导文件。
spring:
cache:
infinispan:
config: "infinispan.xml"
缓存可通过设置 spring.cache.cache-names
属性在启动时创建。如果定义了自定义 ConfigurationBuilder
Bean,则可使用它来自定义缓存。
为了与 Spring Boot 的 Jakarta EE 9 基线兼容,必须使用 Infinispan
的 -jakarta
模块。对于每个带有 -jakarta
变体的模块,必须使用该变体来代替标准模块。例如,必须分别使用 infinispan-core-jakarta
和 infinispan-commons-jakarta
来代替 infinispan-core
和 infinispan-commons
。
Couchbase
如果 Spring Data Couchbase 可用且已配置 Couchbase,则会自动配置 CouchbaseCacheManager
。通过设置 spring.cache.cache-names
属性,可以在启动时创建附加缓存,使用 spring.cache.couchbase.*
属性可以配置缓存默认值。例如,以下配置创建了条目有效期为 10 分钟的 cache1
和 cache2
缓存:
spring:
cache:
cache-names: "cache1,cache2"
couchbase:
expiration: "10m"
如果您需要对配置进行更多控制,可以考虑注册一个 CouchbaseCacheManagerBuilderCustomizer
Bean。下面的示例展示了一个自定义器,用于为 cache1
和 cache2
配置特定的条目过期时间:
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)));
}
}
Redis
如果 Redis 可用并已配置,则会自动配置 RedisCacheManager
。通过设置 spring.cache.cache-names
属性,可以在启动时创建附加缓存,使用 spring.cache.redis.*
属性可以配置缓存默认值。例如,下面的配置创建了 cache1
和 cache2
缓存,其生存时间为 10 分钟:
spring:
cache:
cache-names: "cache1,cache2"
redis:
time-to-live: "10m"
默认情况下,Redis 会添加键前缀,这样如果两个独立缓存使用相同的键,Redis 就不会出现键重叠的情况,也就不会返回无效值。如果你创建了自己的
RedisCacheManager
,我们强烈建议你保持启用此设置。
你可以通过添加自己的
RedisCacheConfiguration
@Bean
来完全控制默认配置。如果需要自定义默认序列化策略,这将非常有用。
如果您需要对配置进行更多控制,可以考虑注册 RedisCacheManagerBuilderCustomizer
Bean。下面的示例展示了一个自定义器,用于为 cache1
和 cache2
配置特定的存活时间:
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)));
}
}
Caffeine
Caffeine 是 Java 8 对 Guava 缓存的重写,取代了对 Guava 的支持。如果存在 Caffeine,就会自动配置 CaffeineCacheManager
(由 spring-boot-starter-cache
"Starter "提供)。缓存可通过设置 spring.cache.cache-names
属性在启动时创建,并可通过以下方式之一进行自定义(按指定顺序):
- 由
spring.cache.caffeine.spec
定义的缓存规范 - 定义了一个
com.github.benmanes.caffeine.cache.CaffeineSpec
Bean - 定义了一个
com.github.benmanes.caffeine.cache.Caffeine
Bean
例如,以下配置创建了cache1
和cache2
,缓存的最大容量为 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>
。自动配置会忽略任何其他通用类型。
Cache2k
Cache2k 是一种内存缓存。如果存在 Cache2k Spring 集成,就会自动配置一个 SpringCache2kCacheManager
。
缓存可通过设置 spring.cache.cache-names
属性在启动时创建。缓存默认值可使用 Cache2kBuilderCustomizer
Bean 进行自定义。下面的示例显示了一个自定义器,它将缓存容量配置为 200 个条目,过期时间为 5 分钟:
import java.util.concurrent.TimeUnit;
import org.springframework.boot.autoconfigure.cache.Cache2kBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyCache2kDefaultsConfiguration {
@Bean
public Cache2kBuilderCustomizer myCache2kDefaultsCustomizer() {
return (builder) -> builder.entryCapacity(200)
.expireAfterWrite(5, TimeUnit.MINUTES);
}
}
Simple
如果找不到其他提供程序,则会配置使用 ConcurrentHashMap
作为缓存存储的简单实现。如果应用程序中没有缓存库,则默认使用此方法。默认情况下,会根据需要创建缓存,但可以通过设置缓存名称属性来限制可用缓存列表。例如,如果只想使用 cache1
和 cache2
缓存,可将 cache-names 属性设置如下:
spring:
cache:
cache-names: "cache1,cache2"
如果您这样做了,而您的应用程序使用了未列出的缓存,那么在运行时需要缓存时就会失败,但启动时不会。这与 “真正的” 缓存提供者在使用未声明缓存时的行为类似。
None
当 @EnableCaching
出现在配置中时,预计也会出现合适的缓存配置。如果您有自定义的 CacheManager
,请考虑在单独的 @Configuration
类中定义它,以便在必要时覆盖它。在测试中,None 使用的是无操作实现,切片测试默认通过 @AutoConfigureCache
使用该实现。
如果需要在特定环境中使用无操作缓存而不是自动配置的缓存管理器,请将缓存类型设置为 none
,如下例所示:
spring:
cache:
type: "none"
Hazelcast
如果 Hazelcast 位于类路径上,并且找到了合适的配置,Spring Boot 就会自动配置一个 HazelcastInstance
,您可以将其注入应用程序中。
Spring Boot 会首先检查以下配置选项,尝试创建客户端:
com.hazelcast.client.config.ClientConfig
Bean 的存在。- 有
spring.hazelcast.config
属性定义的配置文件。 - 存在
hazelcast.client.config
系统属性。 - 工作目录或类路径根目录中的
hazelcast-client.xml
。 - 工作目录下或类路径根目录下的
hazelcast-client.yml
(或hazelcast-client.yml
)。
如果无法创建客户端,Spring Boot 会尝试配置嵌入式服务器。如果您定义了 com.hazelcast.config.Config
Bean,Spring Boot 就会使用该 Bean。如果您的配置定义了实例名称,Spring Boot 会尝试查找现有实例,而不是创建新实例。
您还可以通过配置指定要使用的 Hazelcast 配置文件,如下例所示:
spring:
hazelcast:
config: "classpath:config/my-hazelcast.xml"
否则,Spring Boot 会尝试从默认位置找到 Hazelcast 配置:工作目录或类路径根目录中的 hazelcast.xml
,或相同位置的 YAML 对应文件。我们还会检查是否设置了 hazelcast.config
系统属性。更多详情,请参阅 Hazelcast 文档。
默认情况下,支持 Hazelcast 组件上的
@SpringAware
。可以通过声明一个@Order
大于0
的HazelcastConfigCustomizer
Bean 来重写ManagementContext
。
Spring Boot 还为 Hazelcast 提供了显式缓存支持。如果启用了缓存,
HazelcastInstance
会自动封装在CacheManager
实现中。
Quartz Scheduler
Spring Boot 为使用 Quartz scheduler提供了多种便利,包括 spring-boot-starter-quartz
“Starter”。如果 Quartz 可用,Scheduler 就会自动配置(通过 SchedulerFactoryBean
抽象)。
以下类型的 Bean 会被自动提取并与调度器关联:
JobDetail
:定义一个特定的作业。JobDetail
实例可通过JobBuilder
API 生成。Calendar
。Trigger
:定义特定作业的触发时间。
默认情况下,使用的是内存中的 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
和数据库初始化都使用特定于 Quartz 的DataSource
。同样,若要让 Quartz 使用应用程序主事务管理器(TransactionManager
)以外的事务管理器(TransactionManager
),可声明一个 TransactionManager
Bean,并用 @QuartzTransactionManager
对其 @Bean
方法进行注解。
默认情况下,通过配置创建的作业不会覆盖已从持久作业存储区读取的已注册作业。要启用覆盖现有作业定义,请设置 spring.quartz.overwrite-existing-jobs
属性。
Quartz Scheduler 配置可使用 spring.quartz
属性和 SchedulerFactoryBeanCustomizer
Bean 进行自定义,后者允许对 SchedulerFactoryBean
进行编程式自定义。高级 Quartz 配置属性可使用 spring.quartz.properties.*
Quartz 属性来定制。
特别是,由于 Quartz 提供了一种通过
spring.quartz.properties
配置调度器的方法,因此Executor
Bean 与调度器无关。如果您需要自定义任务执行器,请考虑实现SchedulerFactoryBeanCustomizer
。
工作可以定义设置器来注入数据映射属性。常规 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);
}
}
发送邮件
Spring Framework 通过使用 JavaMailSender
接口为发送电子邮件提供了一个抽象,Spring Boot 还为其提供了自动配置和启动模块。
有关如何使用 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 中的现有会话配置 JavaMailSender
:
spring:
mail:
jndi-name: "mail/Session"
设置 jndi-name
时,它优先于所有其他会话相关设置。
Validation
只要类路径上有 JSR-303 实现(如 Hibernate 验证器),Bean Validation 1.1 支持的方法验证功能就会自动启用。这样,Bean 方法就可以在其参数和/或返回值上注释 jakarta.validation
约束。带有此类注解方法的目标类需要在类型级注解 @Validated
注解,以便在其方法中搜索内联约束注解。
例如,以下服务会触发第一个参数的验证,确保其大小在 8 和 10 之间:
import jakarta.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
。这样,您就可以在 Bean 验证消息中使用应用程序的 messages.properties 文件。解析参数后,将使用 Bean 验证的默认插值器完成消息插值。
要自定义用于构建 ValidatorFactory
的配置,请定义 ValidationConfigurationCustomizer
Bean。当定义了多个定制器 Bean 时,将根据它们的 @Order
注解或 Ordered
实现依次调用它们。
调用 REST 服务
Spring Boot 提供了多种方便的方法来调用远程 REST 服务。如果您正在开发一个非阻塞的反应式应用程序,并且正在使用 Spring WebFlux,那么您可以使用 WebClient
。如果您喜欢阻塞式 API,则可以使用 RestClient
或 RestTemplate
。
WebClient
如果您的类路径上有 Spring WebFlux,我们建议您使用 WebClient 来调用远程 REST 服务。WebClient 接口提供了功能型 API,并且是完全反应式的。有关 WebClient 的更多信息,请参阅 Spring Framework 文档中的专门章节。
如果您编写的不是反应式 Spring WebFlux 应用程序,您可以使用
RestClient
代替WebClient
。它提供了类似的功能 API,但却是阻塞式而非反应式的。
Spring Boot 会为您创建并预先配置 WebClient.Builder
Bean 原型。强烈建议将其注入到您的组件中并用于创建 WebClient
实例。Spring Boot 将对该构建器进行配置,以共享 HTTP 资源,并以与服务器相同的方式反映编解码器设置(请参阅 WebFlux HTTP 编解码器自动配置)等。
以下代码展示了一个典型示例:
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);
}
}
WebClient Runtime
Spring Boot 会根据应用程序类路径上可用的库,自动检测使用哪个 ClientHttpConnector
来驱动 WebClient
。按优先级排序,支持以下客户端:
- Reactor Netty
- Jetty RS client
- Apache HttpClient
- JDK HttpClient
如果类路径上有多个客户端,则会使用最优先的客户端。
spring-boot-starter-webflux
启动器默认依赖 io.projectreactor.netty:reactor-netty
,它同时提供服务器和客户端实现。如果选择将 Jetty 用作反应式服务器,则应添加对 Jetty Reactive HTTP 客户端库 org.eclipse.jetty:jetty-reactive-httpclient
的依赖。服务器和客户端使用相同的技术有其优势,因为它会在客户端和服务器之间自动共享 HTTP 资源。
开发人员可以通过提供自定义的 ReactorResourceFactory
或 JettyResourceFactory
Bean 来覆盖 Jetty 和 Reactor Netty 的资源配置,这将同时应用于客户端和服务器。
如果您想覆盖客户端的选择,您可以定义自己的 ClientHttpConnector
Bean,并完全控制客户端配置。
有关WebClient 配置选项的更多信息,请参阅 Spring Framework 参考文档。
自定义 WebClient
WebClient 自定义有三种主要方法,这取决于您希望自定义应用的范围有多大。
要尽可能缩小自定义的范围,可注入自动配置的 WebClient.Builder
并根据需要调用其方法。WebClient.Builder
实例是有状态的: 对生成器的任何更改都会反映在随后用它创建的所有客户端中。如果想用同一个创建器创建多个客户端,也可以考虑用 WebClient.Builder other = builder.clone();
克隆创建器。
要在整个应用程序中对所有 WebClient.Builder
实例进行添加式自定义,可以声明 WebClientCustomizer
Bean,并在注入点本地更改 WebClient.Builder
。
最后,您可以回到原始 API,使用 WebClient.create()
。在这种情况下,不会应用自动配置或 WebClientCustomizer
。
WebClient SSL 支持
如果需要在 WebClient
使用的 ClientHttpConnector
上自定义 SSL 配置,可以注入一个 WebClientSsl
实例,该实例可与构建器的应用方法一起使用。
WebClientSsl
接口可访问在 application.properties
或 application.yaml
文件中定义的任何 SSL bundles。
以下代码展示了一个典型示例:
import reactor.core.publisher.Mono;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientSsl;
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, WebClientSsl ssl) {
this.webClient = webClientBuilder.baseUrl("https://example.org").apply(ssl.fromBundle("mybundle")).build();
}
public Mono<Details> someRestCall(String name) {
return this.webClient.get().uri("/{name}/details", name).retrieve().bodyToMono(Details.class);
}
}
RestClient
如果您没有在应用程序中使用 Spring WebFlux 或 Project Reactor,我们建议您使用 RestClient
来调用远程 REST 服务。
RestClient
接口提供了功能型阻塞 API。
Spring Boot 会为您创建并预先配置一个 RestClient.Builder
Bean 原型。强烈建议在您的组件中注入它,并用它来创建 RestClient
实例。Spring Boot 将使用 HttpMessageConverters
和适当的 ClientHttpRequestFactory
配置该构建器。
以下代码展示了一个典型示例:
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
@Service
public class MyService {
private final RestClient restClient;
public MyService(RestClient.Builder restClientBuilder) {
this.restClient = restClientBuilder.baseUrl("https://example.org").build();
}
public Details someRestCall(String name) {
return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
}
}
自定义 RestClient
对 RestClient
进行自定义有三种主要方法,这取决于您希望自定义应用的范围有多广。
为了尽可能缩小定制的范围,可以注入自动配置的 RestClient.Builder
并根据需要调用它的方法。RestClient.Builder
实例是有状态的: 对生成器的任何更改都会反映在随后用它创建的所有客户端中。如果想用同一个创建器创建多个客户端,也可以考虑用 RestClient.Builder other = builder.clone();
克隆创建器。
要在整个应用程序中对所有 RestClient.Builder
实例进行添加式自定义,可以声明 RestClientCustomizer
Bean,并在注入时本地更改 RestClient.Builder
。
最后,您可以回到原始 API,使用 RestClient.create()
。在这种情况下,不会应用自动配置或 RestClientCustomizer
。
RestClient SSL 支持
如果需要在 RestClient
使用的 ClientHttpRequestFactory
上自定义 SSL 配置,可以注入一个 RestClientSsl
实例,该实例可与构建器的 apply 方法一起使用。
通过 RestClientSsl
接口,可以访问在 application.properties
或 application.yaml
文件中定义的任何 SSL bundles。
以下代码展示了一个典型示例:
import org.springframework.boot.autoconfigure.web.client.RestClientSsl;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
@Service
public class MyService {
private final RestClient restClient;
public MyService(RestClient.Builder restClientBuilder, RestClientSsl ssl) {
this.restClient = restClientBuilder.baseUrl("https://example.org").apply(ssl.fromBundle("mybundle")).build();
}
public Details someRestCall(String name) {
return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
}
}
如果除了 SSL bundle还需要应用其他自定义功能,可以使用 ClientHttpRequestFactorySettings
类和 ClientHttpRequestFactories
:
import java.time.Duration;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.client.ClientHttpRequestFactories;
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
@Service
public class MyService {
private final RestClient restClient;
public MyService(RestClient.Builder restClientBuilder, SslBundles sslBundles) {
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.DEFAULTS
.withReadTimeout(Duration.ofMinutes(2))
.withSslBundle(sslBundles.getBundle("mybundle"));
ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(settings);
this.restClient = restClientBuilder.baseUrl("https://example.org").requestFactory(requestFactory).build();
}
public Details someRestCall(String name) {
return this.restClient.get().uri("/{name}/details", name).retrieve().body(Details.class);
}
}
RestTemplate
Spring Framework 的 RestTemplate 类早于 RestClient
,是许多应用程序用来调用远程 REST 服务的经典方式。当你有不想迁移到 RestClient
的现有代码时,或者因为你已经熟悉了 RestTemplate
API,你可能会选择使用 RestTemplate
。
由于 RestTemplate
实例在使用前通常需要自定义,因此 Spring Boot 并不提供任何单一的自动配置 RestTemplate
Bean。不过,它确实自动配置了一个 RestTemplateBuilder
,在需要时可用于创建 RestTemplate
实例。自动配置的 RestTemplateBuilder
可确保合理的 HttpMessageConverters
和适当的 ClientHttpRequestFactory
应用于 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()
。
自定义 RestTemplate
RestTemplate
定制有三种主要方法,这取决于您希望定制应用的范围有多大。
为了尽可能缩小定制的范围,可以注入自动配置的 RestTemplateBuilder
,然后根据需要调用它的方法。每次方法调用都会返回一个新的 RestTemplateBuilder
实例,因此定制只会影响到生成器的这次使用。
要在整个应用程序中进行添加式自定义,请使用 RestTemplateCustomizer
Bean。所有此类 Bean 都会自动注册到自动配置的 RestTemplateBuilder
中,并应用到使用它构建的任何模板中。
下面的示例展示了一个自定义器,该自定义器可配置除 192.168.0.5
以外的所有主机使用代理:
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
import org.apache.hc.client5.http.routing.HttpRoutePlanner;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.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
protected HttpHost determineProxy(HttpHost target, HttpContext context) throws HttpException {
if (target.getHostName().equals("192.168.0.5")) {
return null;
}
return super.determineProxy(target, context);
}
}
}
最后,您可以定义自己的 RestTemplateBuilder
Bean。这样做将取代自动配置的生成器。如果你希望任何 RestTemplateCustomizer
Bean 都能应用到你的自定义生成器,就像自动配置一样,请使用 RestTemplateBuilderConfigurer
对其进行配置。下面的示例展示了一个与 Spring Boot 自动配置相似的 RestTemplateBuilder
,只不过还指定了自定义连接和读取超时:
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。
RestTemplate SSL 支持
如果需要在 RestTemplate
上自定义 SSL 配置,可以将 SSL bundle应用到 RestTemplateBuilder
,如本示例所示:
import org.springframework.boot.docs.io.restclient.resttemplate.Details;
import org.springframework.boot.ssl.SslBundles;
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, SslBundles sslBundles) {
this.restTemplate = restTemplateBuilder.setSslBundle(sslBundles.getBundle("mybundle")).build();
}
public Details someRestCall(String name) {
return this.restTemplate.getForObject("/{name}/details", Details.class, name);
}
}
检测 RestClient 和 RestTemplate 的 HTTP 客户端
Spring Boot 会根据应用程序类路径上可用的库,自动检测要与 RestClient
和 RestTemplate
一起使用的 HTTP 客户端。按优先级排序,支持以下客户端:
- Apache HttpClient
- Jetty HttpClient
- OkHttp (deprecated)
- Simple JDK client (HttpURLConnection)
如果类路径上有多个客户端,将使用最优先的客户端。
Web Services
Spring Boot 提供 Web 服务自动配置功能,因此只需定义端点即可。
使用 spring-boot-starter-webservices
模块可以轻松访问 Spring Web 服务功能。
可以分别为 WSDL 和 XSD 自动创建 SimpleWsdl11Definition
和 SimpleXsdSchema
Bean。为此,请配置它们的位置,如下例所示:
spring:
webservices:
wsdl-locations: "classpath:/wsdl"
使用 WebServiceTemplate 调用网络服务
如果需要从应用程序中调用远程 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
会使用类路径上可用的 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();
}
}
使用 JTA 进行分布式事务处理
Spring Boot 通过使用从 JNDI 获取的事务管理器,支持跨多个 XA 资源的分布式 JTA 事务。
检测到 JTA 环境时,Spring 的 JtaTransactionManager
将用于管理事务。自动配置的 JMS、DataSource 和 JPA Bean 会升级以支持 XA 事务。您可以使用 @Transactional
等标准 Spring 惯用语参与分布式事务。如果您在 JTA 环境中,但仍想使用本地事务,可以将 spring.jta.enabled
属性设置为 false
,以禁用 JTA 自动配置。
使用 Jakarta EE 托管事务管理器
如果将 Spring Boot 应用程序打包为 war 或 ear 文件并部署到 Jakarta EE 应用程序服务器,就可以使用应用程序服务器的内置事务管理器。Spring Boot 会通过查看常见的 JNDI 位置(java:comp/UserTransaction
、java:comp/TransactionManager
等)来自动配置事务管理器。在使用应用服务器提供的事务服务时,您通常还希望确保所有资源都由服务器管理并通过 JNDI 公开。Spring Boot 会尝试通过在 JNDI 路径(java:/JmsXA
或 java:/XAConnectionFactory
)中查找 ConnectionFactory
来自动配置 JMS,您可以使用 spring.datasource.jndi-name 属性来配置您的数据源。
混合 XA 和非 XA JMS 连接
使用 JTA 时,主 JMS ConnectionFactory
Bean 是 XA 感知的,并参与分布式事务。您无需使用任何 @Qualifier
即可注入您的 Bean:
public MyBean(ConnectionFactory connectionFactory) {
// ...
}
在某些情况下,您可能希望使用非 XA ConnectionFactory
来处理某些 JMS 消息。例如,您的 JMS 处理逻辑可能需要比 XA 超时更长的时间。
如果您想使用非 XA ConnectionFactory
,可以使用 nonXaJmsConnectionFactory
Bean:
public MyBean(@Qualifier("nonXaJmsConnectionFactory") ConnectionFactory connectionFactory) {
// ...
}
为保持一致性,还使用 bean 别名 xaJmsConnectionFactory
来提供 jmsConnectionFactory
bean:
public MyBean(@Qualifier("xaJmsConnectionFactory") ConnectionFactory connectionFactory) {
// ...
}
支持嵌入式事务管理器
XAConnectionFactoryWrapper 和 XADataSourceWrapper 接口可用于支持嵌入式事务管理器。这些接口负责封装 XAConnectionFactory
和 XADataSource
Bean,并将它们作为常规 ConnectionFactory
和 DataSource
Bean 公开,这些 Bean 会以透明方式加入分布式事务。数据源和 JMS 自动配置使用 JTA 变体,前提是您在 ApplicationContext
中注册了 JtaTransactionManager
Bean 和适当的 XA 封装 Bean。