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

1. 缓存

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

查看Spring Framework参考文档中的相关章节以获取更多详细信息。

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

Java
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

@Component
public class MyMathService {

    @Cacheable("piDecimals")
    public int computePiDecimal(int precision) {
        ...
    }

}
Kotlin
import org.springframework.cache.annotation.Cacheable
import org.springframework.stereotype.Component

@Component
class MyMathService {

    @Cacheable("piDecimals")
    fun computePiDecimal(precision: Int): Int {
        ...
    }

}

此示例演示了在可能耗时的操作上使用缓存。在调用computePiDecimal之前,抽象会查找与i参数匹配的piDecimals缓存条目。如果找到条目,则立即将缓存中的内容返回给调用者,并且不会调用该方法。否则,将调用该方法,并在返回值之前更新缓存。

您还可以透明地使用标准的JSR-107(JCache)注解(如@CacheResult)。但是,我们强烈建议不要混合使用Spring Cache和JCache注解。

如果没有添加任何特定的缓存库,Spring Boot会自动配置一个简单提供者,该提供者在内存中使用并发映射。当需要缓存(例如前面示例中的piDecimals)时,此提供者会为您创建缓存。简单提供者并不真正推荐用于生产环境,但非常适合入门,确保您了解这些功能。当您确定要使用的缓存提供程序时,请务必阅读其文档,以了解如何配置应用程序使用的缓存。几乎所有提供程序都要求您显式配置应用程序中使用的每个缓存。有些提供了一种自定义默认缓存的方式,这些默认缓存由spring.cache.cache-names属性定义。

还可以透明地更新清除缓存中的数据。

1.1. 支持的缓存提供程序

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

如果您没有定义类型为CacheManager或名为cacheResolverCacheResolver的bean(参见CachingConfigurer),Spring Boot会尝试检测以下提供程序(按照指定的顺序):

  1. 通用

  2. JCache(JSR-107)(EhCache 3、Hazelcast、Infinispan等)

  3. Hazelcast

  4. Infinispan

  5. Couchbase

  6. Redis

  7. Caffeine

  8. Cache2k

  9. Simple

如果Spring Boot自动配置了CacheManager,可以通过设置spring.cache.type属性来强制使用特定的缓存提供程序。如果需要在某些环境(如测试)中使用无操作缓存,可以使用此属性。
使用spring-boot-starter-cache“Starter”可以快速添加基本的缓存依赖项。该Starter引入了spring-context-support。如果手动添加依赖项,则必须包含spring-context-support以便使用JCache或Caffeine支持。

如果Spring Boot自动配置了CacheManager,您可以在完全初始化之前通过公开实现CacheManagerCustomizer接口的bean来进一步调整其配置。以下示例设置一个标志,表示不应将null值传递给底层映射:

Java
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);
    }

}
Kotlin
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)
class MyCacheManagerConfiguration {

    @Bean
    fun cacheManagerCustomizer(): CacheManagerCustomizer<ConcurrentMapCacheManager> {
        return CacheManagerCustomizer { cacheManager ->
            cacheManager.isAllowNullValues = false
        }
    }

}
在上述示例中,预期存在自动配置的ConcurrentMapCacheManager。如果不是这种情况(您提供了自己的配置或自动配置了不同的缓存提供程序),则根本不会调用自定义程序。您可以拥有任意数量的自定义程序,并且还可以使用@OrderOrdered对它们进行排序。

1.1.1. 通用

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

1.1.2. JCache(JSR-107)

JCache通过类路径上存在javax.cache.spi.CachingProvider(即类路径上存在符合JSR-107的缓存库)来引导启动,并且JCacheCacheManagerspring-boot-starter-cache“Starter”提供。有多个符合标准的库可用,Spring Boot为Ehcache 3、Hazelcast和Infinispan提供了依赖管理。还可以添加任何其他符合标准的库。

可能会出现多个提供程序的情况,此时必须明确指定提供程序。即使JSR-107标准没有强制规定定义配置文件位置的标准化方式,Spring Boot也会尽力适应设置具有实现细节的缓存,如下例所示:

属性
# 仅在存在多个提供程序时才需要
spring.cache.jcache.provider=com.example.MyCachingProvider
spring.cache.jcache.config=classpath:example.xml
Yaml
# 仅在存在多个提供程序时才需要
spring:
  cache:
    jcache:
      provider: "com.example.MyCachingProvider"
      config: "classpath:example.xml"
当缓存库同时提供本地实现和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,则用于自定义它们。

  • 调用org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer bean,使用CacheManager的引用进行完全自定义。

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

1.1.3. 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提供程序

1.1.4. Infinispan

Infinispan没有默认的配置文件位置,因此必须明确指定。否则,将使用默认的引导。

属性
spring.cache.infinispan.config=infinispan.xml
Yaml
spring:
  cache:
    infinispan:
      config: "infinispan.xml"

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

为了与Spring Boot的Jakarta EE 9基线兼容,必须使用Infinispan的-jakarta模块。对于每个带有-jakarta变体的模块,必须使用该变体代替标准模块。例如,infinispan-core-jakartainfinispan-commons-jakarta必须分别用于替代infinispan-coreinfinispan-commons

1.1.5. Couchbase

如果Spring Data Couchbase可用并且Couchbase已配置,将自动配置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配置了特定的条目过期时间:

Java
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)));

    }

}
Kotlin
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
import java.time.Duration

@Configuration(proxyBeanMethods = false)
class MyCouchbaseCacheManagerConfiguration {

    @Bean
    fun myCouchbaseCacheManagerBuilderCustomizer(): CouchbaseCacheManagerBuilderCustomizer {
        return CouchbaseCacheManagerBuilderCustomizer { builder ->
            builder
                .withCacheConfiguration(
                    "cache1", CouchbaseCacheConfiguration
                        .defaultCacheConfig().entryExpiry(Duration.ofSeconds(10))
                )
                .withCacheConfiguration(
                    "cache2", CouchbaseCacheConfiguration
                        .defaultCacheConfig().entryExpiry(Duration.ofMinutes(1))
                )
        }
    }

}

1.1.6. Redis

如果Redis可用并已配置,将自动配置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配置了特定的存活时间:

Java
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)));

    }

}
Kotlin
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
import java.time.Duration

@Configuration(proxyBeanMethods = false)
class MyRedisCacheManagerConfiguration {

    @Bean
    fun myRedisCacheManagerBuilderCustomizer(): RedisCacheManagerBuilderCustomizer {
        return RedisCacheManagerBuilderCustomizer { builder ->
            builder
                .withCacheConfiguration(
                    "cache1", RedisCacheConfiguration
                        .defaultCacheConfig().entryTtl(Duration.ofSeconds(10))
                )
                .withCacheConfiguration(
                    "cache2", RedisCacheConfiguration
                        .defaultCacheConfig().entryTtl(Duration.ofMinutes(1))
                )
        }
    }

}

1.1.7. Caffeine

Caffeine是Guava缓存的Java 8重写版本,取代了对Guava的支持。如果存在Caffeine,则会自动配置一个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.8. Cache2k

Cache2k是一个内存缓存。如果存在Cache2k spring集成,则会自动配置一个SpringCache2kCacheManager

可以通过设置spring.cache.cache-names属性在启动时创建缓存。可以使用Cache2kBuilderCustomizer bean自定义缓存默认值。以下示例显示了一个配置器,将缓存容量配置为200条记录,过期时间为5分钟:

Java
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);
    }

}
Kotlin
import org.springframework.boot.autoconfigure.cache.Cache2kBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.util.concurrent.TimeUnit

@Configuration(proxyBeanMethods = false)
class MyCache2kDefaultsConfiguration {

    @Bean
    fun myCache2kDefaultsCustomizer(): Cache2kBuilderCustomizer {
        return Cache2kBuilderCustomizer { builder ->
            builder.entryCapacity(200)
                .expireAfterWrite(5, TimeUnit.MINUTES)
        }
    }
}

1.1.9. Simple

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

属性
spring.cache.cache-names=cache1,cache2
Yaml
spring:
  cache:
    cache-names: "cache1,cache2"

如果这样做,且应用程序使用未列出的缓存,则在需要缓存但启动时未找到缓存时会在运行时失败。这类似于如果使用未声明的缓存,则“真实”缓存提供程序的行为方式。

1.1.10. None

当您的配置中存在@EnableCaching时,也期望有适当的缓存配置。如果有自定义的CacheManager,请考虑在单独的@Configuration类中定义它,以便在必要时可以覆盖它。None使用一个无操作实现,在测试中很有用,切片测试默认通过@AutoConfigureCache使用它。

如果需要在某个特定环境中使用无操作缓存而不是自动配置的缓存管理器,请将缓存类型设置为none,如下例所示:

属性
spring.cache.type=none
Yaml
spring:
  cache:
    type: "none"

2. Hazelcast

如果在类路径中找到Hazelcast并且存在合适的配置,Spring Boot会自动配置一个HazelcastInstance,您可以在应用程序中注入它。

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

  • 存在com.hazelcast.client.config.ClientConfig bean。

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

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

  • 在工作目录或类路径根目录中存在hazelcast-client.xml

  • 在工作目录或类路径根目录中存在hazelcast-client.yaml(或hazelcast-client.yml)。

如果无法创建客户端,Spring Boot会尝试配置一个嵌入式服务器。如果您定义了一个com.hazelcast.config.Config bean,Spring Boot会使用它。如果您的配置定义了实例名称,Spring Boot会尝试定位现有实例而不是创建新实例。

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

属性
spring.hazelcast.config=classpath:config/my-hazelcast.xml
Yaml
spring:
  hazelcast:
    config: "classpath:config/my-hazelcast.xml"

否则,Spring Boot会尝试从默认位置查找Hazelcast配置:hazelcast.xml在工作目录或类路径根目录中,或者在相同位置的YAML对应文件。我们还会检查hazelcast.config系统属性是否已设置。有关更多详细信息,请参阅Hazelcast文档

默认情况下,Hazelcast组件上的@SpringAware受支持。可以通过声明一个HazelcastConfigCustomizer bean,并将其@Order设置为大于零来覆盖ManagementContext
Spring Boot还为Hazelcast提供了显式缓存支持。如果启用了缓存,则HazelcastInstance会自动包装在CacheManager实现中。

3. Quartz Scheduler

Spring Boot为使用Quartz调度程序提供了几项便利功能,包括spring-boot-starter-quartz“Starter”。如果Quartz可用,则会自动配置Scheduler(通过SchedulerFactoryBean抽象)。

以下类型的bean会自动被选中并与Scheduler关联:

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

  • 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,并将其@Bean方法注释为@QuartzDataSource。这样可以确保SchedulerFactoryBean和模式初始化都使用Quartz特定的DataSource。类似地,要使Quartz使用除应用程序主要TransactionManager之外的TransactionManager,请声明一个TransactionManager bean,并将其@Bean方法注释为@QuartzTransactionManager

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

可以使用spring.quartz属性和SchedulerFactoryBeanCustomizer bean自定义Quartz调度程序配置,这允许对SchedulerFactoryBean进行编程定制。还可以使用spring.quartz.properties.*自定义高级Quartz配置属性。

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

作业可以定义setter方法来注入数据映射属性。常规bean也可以以类似的方式注入,如以下示例所示:

Java
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);
    }

}
Kotlin
import org.quartz.JobExecutionContext
import org.springframework.scheduling.quartz.QuartzJobBean

class MySampleJob : QuartzJobBean() {

    // fields ...

    private var myService: MyService? = null

    private var name: String? = null

    // Inject "MyService" bean
    fun setMyService(myService: MyService?) {
        this.myService = myService
    }

    // Inject the "name" job data property
    fun setName(name: String?) {
        this.name = name
    }

    override fun executeInternal(context: JobExecutionContext) {
        myService!!.someMethod(context.fireTime, name)
    }

}

4. 发送邮件

Spring框架通过JavaMailSender接口提供了发送电子邮件的抽象,Spring Boot为其提供了自动配置以及一个启动器模块。

查看参考文档,详细解释如何使用JavaMailSender

如果spring.mail.host和相关库(由spring-boot-starter-mail定义)可用,则会创建默认的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
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时,它将优先于所有其他与Session相关的设置。

5. 验证

只要JSR-303实现(如Hibernate验证器)在类路径上,Bean Validation 1.1支持的方法验证功能将自动启用。这使得可以在参数和/或返回值上使用jakarta.validation约束来注释bean方法。具有此类注释方法的目标类需要在类型级别使用@Validated注释,以便搜索内联约束注释。

例如,以下服务触发对第一个参数的验证,确保其大小在8到10之间:

Java
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 ...
    }

}
Kotlin
import jakarta.validation.constraints.Size
import org.springframework.stereotype.Service
import org.springframework.validation.annotation.Validated

@Service
@Validated
class MyBean {

    fun findByCodeAndAuthor(code: @Size(min = 8, max = 10) String?, author: Author?): Archive? {
        return null
    }

}

应用程序的MessageSource用于解析约束消息中的{parameters}。这允许您使用应用程序的messages.properties文件来进行Bean验证消息。一旦参数已解析,消息插值将使用Bean Validation的默认插值器完成。

要自定义用于构建ValidatorFactoryConfiguration,请定义一个ValidationConfigurationCustomizer bean。当定义了多个自定义器bean时,根据它们的@Order注释或Ordered实现的顺序调用它们。

6. 调用REST服务

Spring Boot提供了各种方便的方式来调用远程REST服务。如果您正在开发非阻塞的响应式应用,并且正在使用Spring WebFlux,那么您可以使用WebClient。如果您更喜欢使用阻塞API,则可以使用RestClientRestTemplate

6.1. WebClient

如果您的类路径上有Spring WebFlux,我们建议您使用WebClient来调用远程REST服务。WebClient接口提供了一种函数式风格的API,并且完全是响应式的。您可以在专门的Spring Framework文档中的部分了解更多关于WebClient的信息。

如果您不是在编写响应式的Spring WebFlux应用程序,可以使用RestClient代替WebClient。这提供了类似的函数式API,但是是阻塞的,而不是响应式的。

Spring Boot会为您创建并预配置一个原型WebClient.Builder bean。强烈建议将其注入到您的组件中,并使用它来创建WebClient实例。Spring Boot正在配置该构建器以共享HTTP资源,并以与服务器相同的方式反映编解码器设置(请参阅WebFlux HTTP编解码器自动配置),等等。

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

Java
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);
    }

}
Kotlin
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono

@Service
class MyService(webClientBuilder: WebClient.Builder) {

    private val webClient: WebClient

    init {
        webClient = webClientBuilder.baseUrl("https://example.org").build()
    }

    fun someRestCall(name: String?): Mono<Details> {
        return webClient.get().uri("/{name}/details", name)
                .retrieve().bodyToMono(Details::class.java)
    }

}

6.1.1. WebClient 运行时

Spring Boot会自动检测应用程序类路径上可用的库,以确定驱动WebClient所使用的ClientHttpConnector。按照优先顺序,支持以下客户端:

  1. Reactor Netty

  2. Jetty RS客户端

  3. Apache HttpClient

  4. JDK HttpClient

如果类路径上有多个客户端可用,则将使用最优先的客户端。

spring-boot-starter-webflux启动器默认依赖于io.projectreactor.netty:reactor-netty,它同时提供了服务器和客户端实现。如果您选择使用Jetty作为响应式服务器,您应该添加Jetty Reactive HTTP客户端库org.eclipse.jetty:jetty-reactive-httpclient的依赖。在服务器和客户端之间使用相同的技术具有其优势,因为它将自动在客户端和服务器之间共享HTTP资源。

开发人员可以通过提供自定义的ReactorResourceFactoryJettyResourceFactory bean来覆盖Jetty和Reactor Netty的资源配置 - 这将应用于客户端和服务器。

如果您希望为客户端覆盖该选择,可以定义自己的ClientHttpConnector bean,并完全控制客户端配置。

您可以在Spring Framework参考文档中了解更多关于WebClient配置选项的信息。

6.1.2. WebClient 自定义

有三种主要方法可以对WebClient进行自定义,取决于您希望自定义应用的范围有多广。

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

要对所有WebClient.Builder实例进行应用范围广泛的附加自定义,可以声明WebClientCustomizer bean,并在注入点本地更改WebClient.Builder

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

6.1.3. WebClient SSL支持

如果您需要在WebClient使用的ClientHttpConnector上进行自定义SSL配置,可以注入一个WebClientSsl实例,该实例可以与构建器的apply方法一起使用。

WebClientSsl接口提供了访问您在application.propertiesapplication.yaml文件中定义的任何SSL bundles的权限。

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

Java
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);
    }

}
Kotlin
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientSsl
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono

@Service
class MyService(webClientBuilder: WebClient.Builder, ssl: WebClientSsl) {

    private val webClient: WebClient

    init {
        webClient = webClientBuilder.baseUrl("https://example.org")
                .apply(ssl.fromBundle("mybundle")).build()
    }

    fun someRestCall(name: String?): Mono<Details> {
        return webClient.get().uri("/{name}/details", name)
                .retrieve().bodyToMono(Details::class.java)
    }

}

6.2. RestClient

如果您的应用程序中没有使用Spring WebFlux或Project Reactor,我们建议您使用RestClient来调用远程REST服务。

RestClient接口提供了一种函数式风格的阻塞API。

Spring Boot会为您创建并预配置一个原型RestClient.Builder bean。强烈建议在您的组件中注入它,并使用它来创建RestClient实例。Spring Boot会使用HttpMessageConverters和适当的ClientHttpRequestFactory来配置该构建器。

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

Java
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);
    }

}
Kotlin
import org.springframework.boot.docs.io.restclient.restclient.ssl.Details
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient

@Service
class MyService(restClientBuilder: RestClient.Builder) {

    private val restClient: RestClient

    init {
        restClient = restClientBuilder.baseUrl("https://example.org").build()
    }

    fun someRestCall(name: String?): Details {
        return restClient.get().uri("/{name}/details", name)
                .retrieve().body(Details::class.java)!!
    }

}

6.2.1. RestClient定制

有三种主要方法可以对RestClient进行定制,取决于您希望定制的范围有多广。

为了尽可能缩小任何定制的范围,可以注入自动配置的RestClient.Builder,然后根据需要调用其方法。RestClient.Builder实例是有状态的:对构建器的任何更改都会反映在随后使用它创建的所有客户端中。如果要使用相同的构建器创建多个客户端,还可以考虑使用RestClient.Builder other = builder.clone();克隆构建器。

要对所有RestClient.Builder实例进行应用范围广泛的附加定制,可以声明RestClientCustomizer bean,并在注入点本地更改RestClient.Builder

最后,您可以退回到原始API并使用RestClient.create()。在这种情况下,不会应用任何自动配置或RestClientCustomizer

6.2.2. RestClient SSL支持

如果您需要在RestClient使用的ClientHttpRequestFactory上进行自定义SSL配置,可以注入一个RestClientSsl实例,该实例可与构建器的apply方法一起使用。

RestClientSsl接口提供了访问您在application.propertiesapplication.yaml文件中定义的任何SSL bundles的方式。

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

Java
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);
    }

}
Kotlin
import org.springframework.boot.autoconfigure.web.client.RestClientSsl
import org.springframework.boot.docs.io.restclient.restclient.ssl.settings.Details
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient

@Service
class MyService(restClientBuilder: RestClient.Builder, ssl: RestClientSsl) {

    private val restClient: RestClient

    init {
        restClient = restClientBuilder.baseUrl("https://example.org")
                .apply(ssl.fromBundle("mybundle")).build()
    }

    fun someRestCall(name: String?): Details {
        return restClient.get().uri("/{name}/details", name)
                .retrieve().body(Details::class.java)!!
    }

}

如果您需要在SSL bundle之外应用其他定制,可以使用ClientHttpRequestFactorySettings类与ClientHttpRequestFactories

Java
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);
    }

}
Kotlin
import org.springframework.boot.ssl.SslBundles
import org.springframework.boot.web.client.ClientHttpRequestFactories
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings
import org.springframework.stereotype.Service
import org.springframework.web.client.RestClient
import java.time.Duration

@Service
class MyService(restClientBuilder: RestClient.Builder, sslBundles: SslBundles) {

    private val restClient: RestClient

    init {
        val settings = ClientHttpRequestFactorySettings.DEFAULTS
                .withReadTimeout(Duration.ofMinutes(2))
                .withSslBundle(sslBundles.getBundle("mybundle"))
        val requestFactory = ClientHttpRequestFactories.get(settings)
        restClient = restClientBuilder
                .baseUrl("https://example.org")
                .requestFactory(requestFactory).build()
    }

    fun someRestCall(name: String?): Details {
        return restClient.get().uri("/{name}/details", name).retrieve().body(Details::class.java)!!
    }

}

6.3. RestTemplate

Spring Framework的RestTemplate类早于RestClient,是许多应用程序调用远程REST服务的经典方式。当您有现有代码不想迁移到RestClient,或者因为您已经熟悉RestTemplate API时,您可能会选择使用RestTemplate

由于RestTemplate实例经常需要在使用之前进行自定义,Spring Boot不提供任何单个自动配置的RestTemplate bean。但是,它会自动配置一个RestTemplateBuilder,可以在需要时用于创建RestTemplate实例。自动配置的RestTemplateBuilder确保为RestTemplate实例应用了明智的HttpMessageConverters和适当的ClientHttpRequestFactory

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

Java
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);
    }

}
Kotlin
import org.springframework.boot.web.client.RestTemplateBuilder
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate

@Service
class MyService(restTemplateBuilder: RestTemplateBuilder) {

    private val restTemplate: RestTemplate

    init {
        restTemplate = restTemplateBuilder.build()
    }

    fun someRestCall(name: String): Details {
        return restTemplate.getForObject("/{name}/details", Details::class.java, name)!!
    }

}

RestTemplateBuilder包含许多有用的方法,可用于快速配置RestTemplate。例如,要添加BASIC身份验证支持,您可以使用builder.basicAuthentication("user", "password").build()

6.3.1. RestTemplate自定义

有三种主要方法可以自定义RestTemplate,取决于您希望自定义应用的广泛程度。

为了尽可能缩小任何自定义的范围,可以注入自动配置的RestTemplateBuilder,然后根据需要调用其方法。每次方法调用都会返回一个新的RestTemplateBuilder实例,因此自定义仅影响此构建器的使用。

要进行应用程序范围的附加自定义,请使用RestTemplateCustomizer bean。所有这些bean都会自动注册到自动配置的RestTemplateBuilder中,并应用于使用它构建的任何模板。

以下示例展示了一个自定义器,配置了除192.168.0.5之外的所有主机使用代理:

Java
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);
        }

    }

}
Kotlin
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

class MyRestTemplateCustomizer : RestTemplateCustomizer {

    override fun customize(restTemplate: RestTemplate) {
        val routePlanner: HttpRoutePlanner = CustomRoutePlanner(HttpHost("proxy.example.com"))
        val httpClient: HttpClient = HttpClientBuilder.create().setRoutePlanner(routePlanner).build()
        restTemplate.requestFactory = HttpComponentsClientHttpRequestFactory(httpClient)
    }

    internal class CustomRoutePlanner(proxy: HttpHost?) : DefaultProxyRoutePlanner(proxy) {

        @Throws(HttpException::class)
        public override fun determineProxy(target: HttpHost, context: HttpContext): HttpHost? {
            if (target.hostName == "192.168.0.5") {
                return null
            }
            return  super.determineProxy(target, context)
        }

    }

}

最后,您可以定义自己的RestTemplateBuilder bean。这样将替换自动配置的构建器。如果您希望任何RestTemplateCustomizer bean被应用于您的自定义构建器,就像自动配置所做的那样,可以使用RestTemplateBuilderConfigurer进行配置。以下示例公开了一个RestTemplateBuilder,与Spring Boot的自动配置所做的匹配,只是还指定了自定义的连接和读取超时时间:

Java
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));
    }

}
Kotlin
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
import java.time.Duration

@Configuration(proxyBeanMethods = false)
class MyRestTemplateBuilderConfiguration {

    @Bean
    fun restTemplateBuilder(configurer: RestTemplateBuilderConfigurer): RestTemplateBuilder {
        return configurer.configure(RestTemplateBuilder()).setConnectTimeout(Duration.ofSeconds(5))
            .setReadTimeout(Duration.ofSeconds(2))
    }

}

最极端(并且很少使用)的选项是创建自己的RestTemplateBuilder bean,而不使用配置器。除了替换自动配置的构建器外,这还会阻止使用任何RestTemplateCustomizer bean。

6.3.2. RestTemplate SSL支持

如果您需要在RestTemplate上进行自定义SSL配置,可以像本示例中所示将一个SSL包应用到RestTemplateBuilder中:

Java
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);
    }

}
Kotlin
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
class MyService(restTemplateBuilder: RestTemplateBuilder, sslBundles: SslBundles) {

    private val restTemplate: RestTemplate

    init {
        restTemplate = restTemplateBuilder.setSslBundle(sslBundles.getBundle("mybundle")).build()
    }

    fun someRestCall(name: String): Details {
        return restTemplate.getForObject("/{name}/details", Details::class.java, name)!!
    }

}

6.4. RestClient和RestTemplate的HTTP客户端检测

Spring Boot将根据应用程序类路径上可用的库自动检测要与RestClientRestTemplate一起使用的HTTP客户端。按照优先顺序,支持以下客户端:

  1. Apache HttpClient

  2. Jetty HttpClient

  3. OkHttp(已弃用)

  4. 简单的JDK客户端(HttpURLConnection

如果类路径上有多个客户端可用,则将使用最优先的客户端。

7. Web 服务

Spring Boot提供了Web服务自动配置,所以您只需定义您的端点即可。

可以通过spring-boot-starter-webservices模块轻松访问Spring Web 服务功能

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

属性
spring.webservices.wsdl-locations=classpath:/wsdl
Yaml
spring:
  webservices:
    wsdl-locations: "classpath:/wsdl"

7.1. 使用 WebServiceTemplate 调用 Web 服务

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

以下代码显示了一个典型的示例:

Java
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"));
    }

}
Kotlin
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
class MyService(webServiceTemplateBuilder: WebServiceTemplateBuilder) {

    private val webServiceTemplate: WebServiceTemplate

    init {
        webServiceTemplate = webServiceTemplateBuilder.build()
    }

    fun someWsCall(detailsReq: SomeRequest?): SomeResponse {
        return webServiceTemplate.marshalSendAndReceive(
            detailsReq,
            SoapActionCallback("https://ws.example.com/action")
        ) as SomeResponse
    }

}

默认情况下,WebServiceTemplateBuilder会使用类路径上可用的HTTP客户端库检测合适的基于HTTP的WebServiceMessageSender。您还可以按照以下方式自定义读取和连接超时时间:

Java
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();
    }

}
Kotlin
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 java.time.Duration

@Configuration(proxyBeanMethods = false)
class MyWebServiceTemplateConfiguration {

    @Bean
    fun webServiceTemplate(builder: WebServiceTemplateBuilder): WebServiceTemplate {
        val sender = HttpWebServiceMessageSenderBuilder()
            .setConnectTimeout(Duration.ofSeconds(5))
            .setReadTimeout(Duration.ofSeconds(2))
            .build()
        return builder.messageSenders(sender).build()
    }

}

8. 使用JTA进行分布式事务

Spring Boot通过从JNDI中检索的事务管理器支持跨多个XA资源的分布式JTA事务。

当检测到JTA环境时,Spring的JtaTransactionManager用于管理事务。自动配置的JMS、DataSource和JPA bean已升级以支持XA事务。您可以使用标准的Spring习语,如@Transactional,来参与分布式事务。如果您在JTA环境中,但仍希望使用本地事务,可以将spring.jta.enabled属性设置为false以禁用JTA自动配置。

8.1. 使用Jakarta EE托管的事务管理器

如果将Spring Boot应用程序打包为warear文件并部署到Jakarta EE应用服务器,则可以使用应用服务器内置的事务管理器。Spring Boot尝试通过查看常见的JNDI位置(java:comp/UserTransactionjava:comp/TransactionManager等)自动配置事务管理器。当使用应用服务器提供的事务服务时,通常还希望确保所有资源由服务器管理并通过JNDI公开。Spring Boot尝试通过查找JNDI路径(java:/JmsXAjava:/XAConnectionFactory)自动配置JMS,并且您可以使用spring.datasource.jndi-name属性来配置您的DataSource

8.2. 混合使用XA和非XA JMS连接

在使用JTA时,主要的JMS ConnectionFactory bean具有XA意识并参与分布式事务。您可以将其注入到您的bean中,而无需使用任何@Qualifier

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

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

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

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

jmsConnectionFactory bean也通过bean别名 xaJmsConnectionFactory提供:

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

8.3. 支持嵌入式事务管理器

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

9. 接下来阅读什么

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

接下来的几节将详细介绍如何将应用程序部署到云平台。您可以阅读下一节关于构建容器镜像,或者跳转到生产就绪功能部分。