Appearance
11. IO
大多数应用程序在某些时候需要处理输入和输出问题。 Spring Boot 提供实用程序和与一系列技术的集成,以在您需要 IO 功能时提供帮助。 本节涵盖标准 IO 功能(例如缓存和验证)以及更高级的主题(例如调度和分布式事务)。 我们还将介绍调用远程 REST 或 SOAP 服务以及发送电子邮件。
11.1. 缓存
Spring Framework 支持以透明的方式向应用程序添加缓存. 从本质上讲,将缓存应用于方法上,根据缓存数据减少方法的执行次数. 缓存逻辑是透明的,不会对调用者造成任何干扰. 通过 @EnableCaching
注解启用缓存支持,Spring Boot 就会自动配置缓存设置.
TIP
有关更多详细信息,请查看 Spring Framework 参考文档的 相关部分 .
简而言之,为服务添加缓存的操作就像在其方法中添加注解一样简单,如下所示:
java
@Component
public class MyMathService {
@Cacheable("piDecimals")
public int computePiDecimal(int precision) {
...
}
}
此示例展示了如何在代价可能高昂的操作上使用缓存. 在调用 computePiDecimal
之前,缓存支持会在 piDecimals
缓存中查找与 i
参数匹配的项. 如果找到,则缓存中的内容会立即返回给调用者,并不会调用该方法. 否则,将调用该方法,并在返回值之前更新缓存.
TIP
您还可以使用标准 JSR-107 (JCache) 注解 (例如 @CacheResult
) . 但是,我们强烈建议您不要将 Spring Cache 和 JCache 注解混合使用.
如果您不添加任何指定的缓存库,Spring Boot 会自动配置一个使用 concurrent map 的 simple provider . 当需要缓存时 (例如前面示例中的 piDecimals
) ,该 simple provider 会为您创建缓存. 不推荐将 simple provider 用于生产环境,但它非常适合入门并帮助您了解这些功能. 当您决定使用缓存提供者时,请务必阅读其文档以了解如何配置应用程序. 几乎所有提供者都要求您显式配置应用程序中使用的每个缓存. 有些提供了自定义 spring.cache.cache-names
属性以定义默认缓存.
11.1.1. 支持的缓存提供者
缓存抽象不提供存储实现,其依赖于 org.springframework.cache.Cache
和 org.springframework.cache.CacheManager
接口的抽象实现.
如果您未定义 CacheManager
类型的 bean 或名为 cacheResolver
的 CacheResolver
(请参阅 CachingConfigurer
) ,则 Spring Boot 会尝试检测以下提供者 (按序号顺序) :
JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)
此外,https://github.com/spring-projects/spring-boot-data-geode[Spring Boot for Apache Geode] 提供了 使用 Apache Geode 作为自动配置缓存提供者。
TIP
也可以通过设置 spring.cache.type
属性来强制指定缓存提供者. 如果您需要在某些环境 (比如测试) 中 完全禁用缓存,请使用此属性.
TIP
使用 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:
java
@Configuration(proxyBeanMethods = false)
public class MyCacheManagerConfiguration {
@Bean
public CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
return (cacheManager) -> cacheManager.setAllowNullValues(false);
}
}
TIP
在前面示例中,需要一个自动配置的 ConcurrentMapCacheManager
. 如果不是这种情况 (您提供了自己的配置或自动配置了不同的缓存提供者) ,则根本不会调用 customizer. 您可以拥有多个 customizer,也可以使用 @Order
或 Ordered
来排序它们.
Generic
如果上下文定义了至少一个 org.springframework.cache.Cache
bean,则使用 Generic 缓存. 将创建一个包装所有该类型 bean 的 CacheManager
.
JCache (JSR-107)
JCache 通过 classpath 上的 javax.cache.spi.CachingProvider
(即 classpath 上存在符合 JSR-107 的缓存库) 来引导,jCacheCacheManager
由 spring-boot-starter-cache
starter 提供. 您可以使用各种兼容库,Spring Boot 为 Ehcache 3、Hazelcast 和 Infinispan 提供依赖管理. 您还可以添加任何其他兼容库.
可能存在多个提供者,在这种情况下必须明确指定提供者. 即使 JSR-107 标准没有强制规定一个定义配置文件位置的标准化方法,Spring Boot 也会尽其所能设置一个包含实现细节的缓存,如下所示:
yaml
# Only necessary if more than one provider is present
spring:
cache:
jcache:
provider: "com.example.MyCachingProvider"
config: "classpath:example.xml"
TIP
- 当缓存库同时提供原生实现和 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 以进行完全自定义.
TIP
如果定义了一个标准的 javax.cache.CacheManager
bean,它将自动包装进一个抽象所需的 org.springframework.cache.CacheManager
实现中,而不会应用自定义配置.
EhCache 2.x
如果可以在 classpath 的根目录中找到名为 ehcache.xml
的文件,则使用 EhCache 2.x. 如果找到 EhCache 2.x,则使用 spring-boot-starter-cache
starter 提供的 EhCacheCacheManager
来启动缓存管理器. 还可以提供其他配置文件,如下所示:
yaml
spring:
cache:
ehcache:
config: "classpath:config/another-config.xml"
Hazelcast
Spring Boot 对 Hazelcast 的支持一般. 如果自动配置了一个 HazelcastInstance
,它将自动包装进 CacheManager
中.
Infinispan
Infinispan 没有默认的配置文件位置,因此必须明确指定. 否则将使用默认配置加载.
yaml
spring:
cache:
infinispan:
config: "infinispan.xml"
可以通过设置 spring.cache.cache-names
属性在启动时创建缓存. 如果定义了自定义 ConfigurationBuilder
bean,则它将用于自定义缓存.
TIP
Infinispan 在 Spring Boot 中的支持仅限于内嵌模式,非常简单. 如果你想要更多选项,你应该使用官方的 Infinispan Spring Boot starter. 有关更多详细信息,请参阅 Infinispan 文档 .
Couchbase
如果 Spring Data Couchbase 可用并且已 配置 Couchbase,则会自动配置 CouchbaseCacheManager
. 通过设置 spring.cache.cache-names
属性可以在启动时创建其他缓存,并且可以使用 spring.cache.couchbase.*
属性配置缓存默认值. 以下配置创建 cache1
和 cache2
缓存,他们的有效时间为 10 分钟:
yaml
spring:
cache:
cache-names: "cache1,cache2"
couchbase:
expiration: "10m"
如果需要对配置进行更多控制,请考虑注册 CouchbaseCacheManagerBuilderCustomizer
bean.以下示例显示了一个定制器,该定制器为 cache1
和 cache2
配置到期:
java
@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 分钟:
yaml
spring:
cache:
cache-names: "cache1,cache2"
redis:
time-to-live: "10m"
TIP
- 默认情况下,会添加一个 key 前缀,这样做是因为如果两个单独的缓存使用了相同的键,Redis 不支持重叠 key,而缓存也不能返回无效值. 如果您创建自己的
RedisCacheManager
,我们强烈建议您启用此设置. - 您可以通过添加自己的
RedisCacheConfiguration
@Bean
来完全控制配置. 如果您想自定义序列化策略,这种方式可能很有用.
如果您需要控制更多的配置,请考虑注册 RedisCacheManagerBuilderCustomizer
bean. 以下示例显示了一个自定义的配置,配置了 cache1
和 cache2
的失效时间:
java
@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 分钟:
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>
. 自动配置会忽略所有其他泛型类型.
Simple
如果找不到其他提供者,则配置使用一个 ConcurrentHashMap
作为缓存存储的简单实现. 如果您的应用程序中没有缓存库,则该项为默认值. 默认情况下,会根据需要创建缓存,但您可以通过设置 cache-names
属性来限制可用缓存的列表. 例如,如果只需要 cache1
和 cache2
缓存,请按如下设置 cache-names
属性:
yaml
spring:
cache:
cache-names: "cache1,cache2"
如果这样做了,并且您的应用程序使用了未列出的缓存,则运行时在它需要缓存时会触发失败,但在启动时则不会. 这类似于真实缓存提供者在使用未声明的缓存时触发的行为方式.
None
当配置中存在 @EnableCaching
时,也需要合适的缓存配置. 如果需要在某些环境中完全禁用缓存,请将缓存类型强制设置为 none
以使用 no-op 实现,如下所示:
yaml
spring:
cache:
type: "none"
11.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
.
TIP
Spring Boot 支持 Hazelcast 4 和 Hazelcast 3. 如果降级到 Hazelcast 3, 应该将 hazelcast-client
添加到类路径中以配置客户端.
如果无法创建客户端,则 Spring Boot 尝试配置嵌入式服务器.
如果定义了 com.hazelcast.config.Config
bean,则 Spring Boot 会使用它. 如果您的配置定义了实例名称,Spring Boot 会尝试查找现有的实例,而不是创建新实例.
您还可以指定通过配置使用的 Hazelcast 配置文件,如以下示例所示:
yaml
spring:
hazelcast:
config: "classpath:config/my-hazelcast.xml"
否则,Spring Boot 会尝试从默认位置查找 Hazelcast 配置: 工作目录或 classpath 根目录中的 hazelcast.xml
,或相同位置中的 .yaml
文件. 我们还检查是否设置了 hazelcast.config
系统属性. 有关更多详细信息,请参见 Hazelcast documentation.
TIP
Spring Boot 还为 Hazelcast 提供了缓存支持. 如果启用了缓存,HazelcastInstance
将自动包装在 CacheManager
实现中.
11.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 的存储,如下所示:
yaml
spring:
quartz:
job-store-type: "jdbc"
使用 JDBC 存储时,可以在启动时初始化 schema (表结构) ,如下所示:
yaml
spring:
quartz:
jdbc:
initialize-schema: "always"
TIP
默认将使用 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 配置属性.
TIP
需要强调的是,Executor
bean 与调度程序没有关联,因为 Quartz 提供了通过 spring.quartz.properties
配置调度器的方法. 如果需要自定义 Actuator ,请考虑实现 SchedulerFactoryBeanCustomizer
.
job 可以定义 setter 以注入数据映射属性. 也可以以类似的方式注入常规的 bean,如下所示:
java
public class MySampleJob extends QuartzJobBean {
// fields ...
// 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);
}
}
11.4. 发送邮件
Spring Framework 提供了一个使用 JavaMailSender
接口发送电子邮件的简单抽象,Spring Boot 为其提供了自动配置以及一个 starter 模块.
TIP
有关如何使用 JavaMailSender 的详细说明,请参阅 参考文档.
如果 spring.mail.host
和相关库 (由 spring-boot-starter-mail
定义) 可用,则创建默认的 JavaMailSender
(如果不存在) . 可以通过 spring.mail
命名空间中的配置项进一步自定义发件人. 有关更多详细信息,请参阅 MailProperties
.
特别是,某些默认超时时间的值是无限的,您可能想更改它以避免线程被无响应的邮件服务器阻塞,如下示例所示:
yaml
spring:
mail:
properties:
"[mail.smtp.connectiontimeout]": 5000
"[mail.smtp.timeout]": 3000
"[mail.smtp.writetimeout]": 5000
也可以使用 JNDI 中的现有 Session
配置一个 JavaMailSender
:
yaml
spring:
mail:
jndi-name: "mail/Session"
设置 jndi-name
时,它优先于所有其他与 Session 相关的设置.
11.5. Validation
只要 classpath 上存在 JSR-303 实现 (例如 Hibernate 验证器) ,就会自动启用 Bean Validation 1.1 支持的方法验证功能. 这允许 bean 方法在其参数和/或返回值上使用 javax.validation
约束进行注解. 带有此类注解方法的目标类需要在类级别上使用 @Validated
进行注解,以便搜索其内联约束注解的方法.
例如,以下服务触发第一个参数的验证,确保其大小在 8 到 10 之间:
java
@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 的默认插值器完成消息插值。
11.6. 调用 REST Services
如果您的应用程序需要调用远程 REST 服务,这可以使用 Spring Framework 的 RestTemplate 或 WebClient
类.
11.6.1. RestTemplate
如果您的应用程序需要调用远程 REST 服务,这可以使用 Spring Framework 的 RestTemplate 类. 由于 RestTemplate
实例在使用之前通常需要进行自定义,因此 Spring Boot 不提供任何自动配置的 RestTemplate
bean. 但是, 它会自动配置 RestTemplateBuilder
,可在需要时创建 RestTemplate
实例. 自动配置的 RestTemplateBuilder
确保将合适的 HttpMessageConverters
应用于 RestTemplate
实例.
以下代码展示了一个典型示例:
java
@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); }}
TIP
RestTemplateBuilder
包含许多可用于快速配置 RestTemplate
的方法. 例如,要添加 BASIC auth 支持,可以使用 builder.basicAuthentication("user", "password").build()
.
自定义 RestTemplate
RestTemplate
自定义有三种主要方法,具体取决于您希望自定义的程度.
要想自定义的作用域尽可能地窄,请注入自动配置的 RestTemplateBuilder
,然后根据需要调用其方法. 每个方法调用都返回一个新的 RestTemplateBuilder
实例,因此自定义只会影响当前构建器.
要在应用程序作用域内添加自定义配置,请使用 RestTemplateCustomizer
bean. 所有这些 bean 都会自动注册到自动配置的 RestTemplateBuilder
,并应用于使用它构建的所有模板.
以下示例展示了一个 customizer,它为除 192.168.0.5
之外的所有主机配置代理:
java
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 将自动配置它,但也指定了自定义连接和读取超时:
java
@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.
11.6.2. WebClient
如果在 classpath 上存在 Spring WebFlux,则还可以选择使用 WebClient
来调用远程 REST 服务. 与 RestTemplate
相比,该客户端更具函数式风格并且完全响应式. 您可以在 Spring Framework 文档的相关部分中了解有关 WebClient
的更多信息.
Spring Boot 为您创建并预配置了一个 WebClient.Builder
. 强烈建议将其注入您的组件中并使用它来创建 WebClient
实例. Spring Boot 配置该构建器以共享 HTTP 资源,以与服务器相同的方式反射编解码器设置 (请参阅 WebFlux HTTP 编解码器自动配置) 等.
以下代码是一个典型示例:
java
@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 运行时
Spring Boot 将自动检测用于驱动 WebClient
的 ClientHttpConnector
,具体取决于应用程序 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 资源.
开发人员可以通过提供自定义的 ReactorResourceFactory
或 JettyResourceFactory
bean 来覆盖 Jetty 和 Reactor Netty 的资源配置 —— 这将同时应用于客户端和服务器.
如果您只希望覆盖客户端选项,则可以定义自己的 ClientHttpConnector
bean 并完全控制客户端配置.
您可以在 Spring Framework 参考文档中了解有关 WebClient
配置选项的更多信息.
自定义 WebClient
WebClient
自定义有三种主要方法,具体取决于您希望自定义的程度.
要想自定义的作用域尽可能地窄,请注入自动配置的 WebClient.Builder
,然后根据需要调用其方法. WebClient.Builder
实例是有状态的: 构建器上的任何更改都会影响到之后所有使用它创建的客户端. 如果要使用相同的构建器创建多个客户端,可以考虑使用 WebClient.Builder other = builder.clone()
; 的方式克隆构建器.
要在应用程序作用域内对所有 WebClient.Builder
实例添加自定义,可以声明 WebClientCustomizer
bean 并在注入点局部更改 WebClient.Builder
.
最后,您可以回退到原始 API 并使用 WebClient.create(). 在这种情况下,不会应用自动配置或 WebClientCustomizer
.
11.7. Web Services
Spring Boot 提供 Web Service 自动配置,因此您要做的就是定义 Endpoints.
可以使用 spring-boot-starter-webservices
模块轻松访问 Spring Web Services 功能.
可以分别为 WSDL 和 XSD 自动创建 SimpleWsdl11Definition
和 SimpleXsdSchema
bean. 为此,请配置其位置,如下所示:
yaml
spring:
webservices:
wsdl-locations: "classpath:/wsdl"
11.7.1. 使用 WebServiceTemplate 调用 Web Service
如果您需要从应用程序调用远程 Web 服务,则可以使用 WebServiceTemplate
类. 由于 WebServiceTemplate
实例在使用之前通常需要进行自定义,因此 Spring Boot 不提供任何自动配置的 WebServiceTemplate
bean. 但是,它会自动配置 WebServiceTemplateBuilder
,可在需要创建 WebServiceTemplate
实例时使用.
以下代码为一个典型示例:
java
@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
. 您还可以按如下方式自定义读取和连接的超时时间:
java
@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();
}
}
11.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 自动配置.
11.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.
TIP
为确保多个事务管理器可以安全地协调相同的资源管理器,必须为每个 Atomikos 实例配置唯一 ID. 默认情况下,此 ID 是运行 Atomikos 的计算机的 IP 地址. 在生产环境中要确保唯一性,应为应用程序的每个实例配置 spring.jta.transaction-manager-id
属性,并使用不同的值.
11.8.2. 使用 Java EE 管理的事务管理器
如果将 Spring Boot 应用程序打包为 war
或 ear
文件并将其部署到 Java EE 应用程序服务器,则可以使用应用程序服务器的内置事务管理器. Spring Boot 尝试通过查找常见的 JNDI 位置 (java:comp/UserTransaction
、java:comp/TransactionManager
等) 来自动配置事务管理器. 如果使用应用程序服务器提供的事务服务, 通常还需要确保所有资源都由服务器管理并通过 JNDI 暴露. Spring Boot 尝试通过在 JNDI 路径 (java:/JmsXA
或 java:/JmsXA
) 中查找 ConnectionFactory
来自动配置 JMS,并且可以使用 spring.datasource.jndi-name
属性 属性来配置 DataSource
.
11.8.3. 混合使用 XA 与非 XA JMS 连接
使用 JTA 时,primary (主) JMS ConnectionFactory
bean 可识别 XA 并参与分布式事务.您可以注入到您的 bean 中,而无需使用任何 @Qualifier
:
java
public MyBean(ConnectionFactory connectionFactory) {
// ...
}
在某些情况下,您可能希望使用非 XA ConnectionFactory
处理某些 JMS 消息. 例如,您的 JMS 处理逻辑可能需要比 XA 超时时间更长的时间.
如果要使用非 XA ConnectionFactory
,可以使用 nonXaJmsConnectionFactory
bean:
java
public MyBean(@Qualifier("nonXaJmsConnectionFactory") ConnectionFactory connectionFactory) {
// ...
}
为了保持一致性,提供的 jmsConnectionFactory
bean 还需要使用 xaJmsConnectionFactory
别名.
java
public MyBean(@Qualifier("xaJmsConnectionFactory") ConnectionFactory connectionFactory) {
// ...
}
11.8.4. 支持嵌入式事务管理器
XAConnectionFactoryWrapper
和 XADataSourceWrapper
接口可用于支持其他嵌入式事务管理器. 接口负责包装 XAConnectionFactory
和 XADataSource
bean,并将它们暴露为普通的 ConnectionFactory
和 DataSource
bean,它们透明地加入分布式事务. DataSource
和 JMS 自动配置使用 JTA 变体,前提是您需要有一个 JtaTransactionManager
bean 和在 ApplicationContext
中注册有的相应 XA 包装器 (wrapper) bean.
AtomikosXAConnectionFactoryWrapper 和 AtomikosXADataSourceWrapper 为如何编写 XA 包装器提供了很好示例.
11.9. 下一步
您现在应该对 Spring Boot 的 core features 以及 Spring Boot 通过自动配置提供支持的各种技术有了很好的了解。
接下来的几节将详细介绍如何将应用程序部署到云平台。 您可以在下一节中阅读有关 构建容器映像 或跳到 生产就绪功能 部分。