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-107
(JCache
)注解(例如@CacheResult
)。但是,我们强烈建议您不要混合使用 Spring Cache 和 JCache 注解。
如果您不添加任何特定的缓存库,Spring Boot 会自动配置一个使用内存中的并发 map 的 简单供应商 。当需要缓存时(例如前面的示例 piDecimals
),此供应商将为您创建它。实际上,不建议将简单供应商用于生产用途,但是它对于入门并确保您了解功能非常有用。确定要使用的缓存供应商后,请确保阅读其文档,以了解如何配置应用程序使用的缓存。几乎所有供应商都要求您显式配置在应用程序中使用的每个缓存。有些提供了通过 spring.cache.cache-names
属性自定义默认缓存。
1.1. 支持的缓存供应商
缓存抽象不提供实际的存储,而是依赖于 org.springframework.cache.Cache
和 org.springframework.cache.CacheManager
接口实现的抽象。
如果尚未定义类型为 CacheManager
或 CacheResolver
且名称为 cacheResolver
的 bean (请参阅 CachingConfigurer
),Spring Boot 会尝试检测以下供应商(按指示的顺序,参考 org.springframework.boot.autoconfigure.cache.CacheType
):
- Generic
- JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)
- EhCache 2.x
- Hazelcast
- Infinispan
- Couchbase
- Redis
- Caffeine
- Simple
此外, Spring Boot for Apache Geode 提供了 使用 Apache Geode 作为缓存供应商的自动配置
可以通过设置
spring.cache.type
属性来强制使用特定的缓存供应商。如果您需要在某些环境(例如测试)中完全 禁用缓存 ,请使用此属性。
使用 spring-boot-starter-cache
启动器快速添加基本的缓存依赖项。启动器带进来 spring-context-support
。如果手动添加依赖项,则必须包括 spring-context-support
才能使用 JCache
,EhCache 2.x
或 Caffeine
支持。
如果 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
。如果不是这种情况(您提供了自己的配置,或者自动配置了其他缓存供应商),则根本不会调用定制器。您可以根据需要拥有任意数量的定制器,也可以使用@Order
或Ordered
对其进行排序。
1.1.1. Generic
如果上下文定义了至少一个 org.springframework.cache.Cache
bean,则使用通用缓存。CacheManager
将包装所有该类型的 bean 。
1.1.2. JCache(JSR-107)
JCache 通过类路径上的 javax.cache.spi.CachingProvider
进行引导(即,类路径上存在符合 JSR-107 的缓存库),并且 JCacheCacheManager
由 spring-boot-starter-cache
启动器提供。有各种兼容的库可用,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
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.*
属性配置缓存默认值。例如,以下配置创建 cache1
和 cache2
缓存条目,过期时间为 10 分钟:
spring.cache.cache-names=cache1,cache2
spring.cache.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)));
}
}
1.1.7. Redis
如果 Redis 可用并已配置,则会自动配置 RedisCacheManager
。可以通过设置 spring.cache.cache-names
属性在启动时创建额外的缓存,并且可以使用 spring.cache.redis.*
属性配置缓存默认值。例如,以下配置创建 cache1
和 cache2
缓存的生存时间为 10分钟:
spring.cache.cache-names=cache1,cache2
spring.cache.redis.time-to-live=10m
默认情况下,会添加 key 前缀,这样,如果两个单独的缓存使用相同的 key ,则 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)));
}
}
1.1.8. Caffeine
Caffeine 是对 Guava
缓存的 Java 8 重写,它取代了对 Guava 的支持。如果存在 Caffeine ,则 CaffeineCacheManager
将自动配置(由 spring-boot-starter-cache
启动器提供)。缓存可以在启动时通过设置 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
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
属性来限制可用缓存的列表。例如,如果只需要 cache1
和 cache2
缓存,则按如下所示设置 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 使用 RestTemplate
或 WebClient
6.1. RestTemplate
如果需要从应用程序调用远程 REST 服务,则可以使用 Spring Framework 的 RestTemplate
类。由于 RestTemplate
实例通常需要在使用前进行自定义,因此 Spring Boot 不提供任何单个自动配置的 RestTemplate
bean。但是,它会自动配置RestTemplateBuilder
,可以在需要时创建 RestTemplate
实例。自动配置 RestTemplateBuilder
可确保明智地将 HttpMessageConverters
应用于 RestTemplate
实例。
以下代码显示了一个典型示例:
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class MyService {
private final RestTemplate restTemplate;
public MyService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
public Details someRestCall(String name) {
return this.restTemplate.getForObject("/{name}/details", Details.class, name);
}
}
RestTemplateBuilder
包括许多有用的方法,可用于快速配置RestTemplate
。例如,要添加 BASIC 身份验证支持,可以使用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 Netty
和 Jetty RS
客户端。
spring-boot-starter-webflux
启动器默认依赖于 io.projectreactor.netty:reactor-netty
,附带服务器和客户端的实现。如果选择使用 Jetty 作为反应式服务器,则应在 Jetty 反应式 HTTP 客户端库上添加依赖项 org.eclipse.jetty:jetty-reactive-httpclient
。对服务器和客户端使用相同的技术具有其优势,因为它将自动在客户端和服务器之间共享 HTTP 资源。
开发人员可以通过提供一个自定义 ReactorResourceFactory
或 JettyResourceFactory
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 自动创建 SimpleWsdl11Definition
和 SimpleXsdSchema
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 应用程序打包为 war
或 ear
文件并将其部署到 Java EE 应用程序服务器,则可以使用应用程序服务器的内置事务管理器。Spring Boot 尝试通过查看常见的 JNDI 位置( java:comp/UserTransaction
、java:comp/TransactionManager
等)来自动配置事务管理器。如果您使用应用服务器提供的事务服务,您通常还希望确保所有资源都由服务器管理并通过 JNDI 公开。Spring Boot 尝试通过在 JNDI 路径(java:/JmsXA
或java:/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. 支持替代嵌入式事务管理器
XAConnectionFactoryWrapper
和 XADataSourceWrapper
接口可用于支持替代的嵌入式事务管理器。这些接口负责包装 XAConnectionFactory
和 XADataSource
bean 并将它们作为常规 ConnectionFactory
和 DataSource
bean 公开,它们透明地注册到分布式事务中。DataSource 和 JMS 自动配置使用 JTA 变体,前提是在 ApplicationContext
中注册了 JtaTransactionManager
bean 和适当的 XA 包装器 bean。
AtomikosXAConnectionFactoryWrapper 和 AtomikosXADataSourceWrapper 提供了如何编写 XA 包装器的很好的示例。