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

1. 缓存

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

检查Spring 框架参考的 相关部分以获取更多详细信息。

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

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,抽象会在piDecimals缓存中查找与参数匹配的条目i。如果找到条目,则立即将缓存中的内容返回给调用者,并且不调用该方法。否则,将调用该方法,并在返回值之前更新缓存。

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

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

还可以透明地从缓存中 更新逐出数据。

1.1. 支持的缓存提供程序

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

如果您尚未定义类型CacheManagerCacheResolver命名的 bean cacheResolver(请参阅 参考资料CachingConfigurer),Spring Boot 会尝试检测以下提供程序(按指定的顺序):

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

如果CacheManagerSpring Boot 自动配置,您可以在完全初始化之前通过公开实现该接口的 bean 来进一步调整其配置CacheManagerCustomizer。以下示例设置一个标志来表示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在前面的示例中,需要 自动配置。如果情况并非如此(您提供了自己的配置或自动配置了不同的缓存提供程序),则根本不会调用定制器。您可以拥有任意数量的定制器,也可以使用@Order或来订购它们Ordered

1.1.1. 通用的

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

1.1.2. JCache (JSR-107)

JCache通过javax.cache.spi.CachingProvider类路径上存在的 a 进行引导(即,类路径上存在符合 JSR-107 的缓存库),并且JCacheCacheManager由“Starter”提供spring-boot-starter-cache。提供各种兼容的库,Spring Boot 为 Ehcache 3、Hazelcast 和 Infinispan 提供依赖管理。也可以添加任何其他兼容的库。

可能会出现多个提供者,在这种情况下必须显式指定该提供者。即使 JSR-107 标准没有强制采用标准化方法来定义配置文件的位置,Spring Boot 也会尽力通过实现细节来设置缓存,如以下示例所示:

Properties
# Only necessary if more than one provider is present
spring.cache.jcache.provider=com.example.MyCachingProvider
spring.cache.jcache.config=classpath:example.xml
Yaml
# Only necessary if more than one provider is present
spring:
  cache:
    jcache:
      provider: "com.example.MyCachingProvider"
      config: "classpath:example.xml"
当缓存库同时提供本机实现和 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.JCacheManagerCustomizerbeans 通过 的引用来调用CacheManager以实现完全定制。

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

1.1.3. 榛卡斯特

Spring Boot对 Hazelcast 具有普遍支持。如果 aHazelcastInstance已自动配置并且com.hazelcast:hazelcast-spring位于类路径上,则它会自动包装在CacheManager.

Hazelcast 可以用作 JCache 兼容缓存或 SpringCacheManager兼容缓存。当设置spring.cache.type为 时hazelcast,Spring Boot 将使用CacheManager基于的实现。如果您想使用 Hazelcast 作为 JCache 兼容缓存,请设置spring.cache.typejcache。如果您有多个符合 JCache 标准的缓存提供程序并希望强制使用 Hazelcast,则必须显式设置 JCache 提供程序

1.1.4. 无限跨度

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

Properties
spring.cache.infinispan.config=infinispan.xml
Yaml
spring:
  cache:
    infinispan:
      config: "infinispan.xml"

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

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

1.1.5。沙发底座

如果 Spring Data Couchbase 可用并且 Couchbase 已配置,则 aCouchbaseCacheManager会自动配置。可以通过设置spring.cache.cache-names属性在启动时创建额外的缓存,并且可以使用spring.cache.couchbase.*属性配置缓存默认值。例如,以下配置创建cache1cache2缓存条目过期时间为 10 分钟:

Properties
spring.cache.cache-names=cache1,cache2
spring.cache.couchbase.expiration=10m
Yaml
spring:
  cache:
    cache-names: "cache1,cache2"
    couchbase:
      expiration: "10m"

如果您需要对配置进行更多控制,请考虑注册CouchbaseCacheManagerBuilderCustomizerbean。以下示例显示了为cache1和配置特定条目到期时间的定制程序cache2

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

Properties
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您可以通过添加自己的 配置来完全控制默认配置。如果您需要自定义默认序列化策略,这可能很有用。

如果您需要对配置进行更多控制,请考虑注册RedisCacheManagerBuilderCustomizerbean。以下示例显示了配置特定生存时间cache1和 的定制程序cache2

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是 Guava 缓存的 Java 8 重写,取代了对 Guava 的支持。如果存在咖啡因,则会自动配置CaffeineCacheManager(由“Starter”提供)。spring-boot-starter-cache可以通过设置属性在启动时创建缓存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 分钟

Properties
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。缓存2k

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

可以通过设置属性在启动时创建缓存spring.cache.cache-names。可以使用Cache2kBuilderCustomizerbean 自定义缓存默认值。以下示例显示了一个自定义程序,它将缓存容量配置为 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。简单的

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

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

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

1.1.10. 没有任何

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

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

Properties
spring.cache.type=none
Yaml
spring:
  cache:
    type: "none"

2. 榛果

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

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

  • 豆子的存在com.hazelcast.client.config.ClientConfig

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

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

  • Ahazelcast-client.xml位于工作目录或类路径的根目录中。

  • A hazelcast-client.yaml(或hazelcast-client.yml)位于工作目录或类路径的根目录中。

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

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

Properties
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 文档。

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

3.石英调度器

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

以下类型的 Bean 会自动选取并与 相关联Scheduler

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

  • Calendar

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

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

Properties
spring.quartz.job-store-type=jdbc
Yaml
spring:
  quartz:
    job-store-type: "jdbc"

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

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

要让 Quartz 使用DataSource应用程序的 main 之外的其他容器DataSource,请声明一个DataSourcebean,@Bean并用 注释其方法@QuartzDataSourceDataSource这样做可以确保和 都使用Quartz 特定的SchedulerFactoryBean模式初始化。类似地,要让 Quartz 使用TransactionManager应用程序的 main 之外的其他组件,TransactionManager请声明一个TransactionManagerbean,并用 注释其@Bean方法@QuartzTransactionManager

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

spring.quartzQuartz Scheduler 配置可以使用属性和SchedulerFactoryBeanCustomizerbean进行定制,从而允许编程SchedulerFactoryBean定制。可以使用 来定制高级 Quartz 配置属性spring.quartz.properties.*

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

作业可以定义设置器来注入数据映射属性。普通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了解更多详情。

特别是,某些默认超时值是无限的,您可能需要更改该值以避免线程被无响应的邮件服务器阻塞,如以下示例所示:

Properties
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

还可以JavaMailSender使用 JNDI 中的现有配置进行配置Session

Properties
spring.mail.jndi-name=mail/Session
Yaml
spring:
  mail:
    jndi-name: "mail/Session"

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

5. 验证

只要 JSR-303 实现(例如 Hibernate 验证器)位于类路径上,Bean Validation 1.1 支持的方法验证功能就会自动启用。这使得 bean 方法可以通过jakarta.validation对其参数和/或返回值的约束进行注释。具有此类注释方法的目标类需要在类型级别使用注释进行注释,@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 的默认插值器完成。

要自定义Configuration用于构建的ValidatorFactory,请定义一个ValidationConfigurationCustomizerbean。@Order当定义多个定制器 bean 时,将根据它们的注释或实现按顺序调用它们Ordered

6. 调用REST服务

如果您的应用程序调用远程 REST 服务,Spring Boot 使用RestTemplateWebClient.

6.1. 休息模板

如果您需要从应用程序调用远程 REST 服务,您可以使用 Spring Framework 的RestTemplate类。由于RestTemplate实例在使用之前通常需要进行自定义,因此 Spring Boot 不提供任何单个自动配置的RestTemplatebean。但是,它会自动配置 a RestTemplateBuilder,可用于RestTemplate在需要时创建实例。自动配置RestTemplateBuilder可确保将合理的内容HttpMessageConverters应用于RestTemplate实例。

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

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.1.1. RestTemplate HTTP 客户端

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

  1. Apache HttpClient

  2. 好的http

  3. 简单的 JDK 客户端 ( HttpURLConnection)

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

6.1.2. RestTemplate定制

自定义有三种主要方法RestTemplate,具体取决于您希望自定义应用的范围。

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

要进行应用程序范围的附加定制,请使用RestTemplateCustomizerbean。所有此类 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)
        }

    }

}

最后,您可以定义自己的RestTemplateBuilderbean。这样做将替换自动配置的构建器。如果您希望将任何RestTemplateCustomizerbean 应用于您的自定义构建器(就像自动配置一样),请使用RestTemplateBuilderConfigurer. 以下示例公开了RestTemplateBuilder与 Spring Boot 的自动配置将执行的操作相匹配的 a ,除了还指定了自定义连接和读取超时:

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

}

最极端(且很少使用)的选项是创建您自己的RestTemplateBuilderbean,而不使用配置器。除了替换自动配置的构建器之外,这还可以防止RestTemplateCustomizer使用任何 bean。

6.1.3. RestTemplate SSL 支持

如果您需要在 上进行自定义 SSL 配置RestTemplate,您可以将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.2. 网络客户端

如果您的类路径上有 Spring WebFlux,您还可以选择用于WebClient调用远程 REST 服务。与 相比RestTemplate,该客户端具有更实用的感觉并且完全响应式。您可以在 Spring 框架文档的WebClient专门部分了解更多相关信息。

WebClient.BuilderSpring Boot 会为您创建并预先配置一个。强烈建议将其注入您的组件中并使用它来创建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.2.1. 网络客户端运行时

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

  1. 反应堆网络

  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 资源。

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

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

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

6.2.2. 网页客户端定制

自定义有三种主要方法WebClient,具体取决于您希望自定义应用的范围。

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

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

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

6.2.3. WebClient SSL 支持

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

该界面提供对您在或文件中定义的任何SSL 捆绑包的WebClientSsl访问。application.propertiesapplication.yaml

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

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

}

7. 网络服务

Spring Boot 提供 Web 服务自动配置,因此您所要做的就是定义您的Endpoints.

使用该模块可以轻松访问Spring Web Services功能spring-boot-starter-webservices

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

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

7.1. 使用 WebServiceTemplate 调用 Web 服务

如果您需要从应用程序调用远程 Web 服务,则可以使用该类WebServiceTemplate。由于WebServiceTemplate实例在使用之前通常需要进行自定义,因此 Spring Boot 不提供任何单个自动配置的WebServiceTemplatebean。但是,它会自动配置 a 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
    }

}

默认情况下,使用类路径上的可用 HTTP 客户端库WebServiceTemplateBuilder检测合适的基于 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 环境时,SpringJtaTransactionManager将用于管理事务。自动配置的 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 公开。ConnectionFactorySpring Boot 尝试通过在 JNDI 路径(java:/JmsXA或)中查找来自动配置 JMS java:/XAConnectionFactory,并且您可以使用该spring.datasource.jndi-name属性来配置您的DataSource.

8.2. 混合 XA 和非 XA JMS 连接

使用 JTA 时,主 JMS ConnectionFactorybean 是 XA 感知的并参与分布式事务。您可以注入到您的 bean 中,无需使用任何@Qualifier

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

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

如果你想使用非 XA ConnectionFactory,你可以使用nonXaJmsConnectionFactorybean:

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

为了保持一致性,jmsConnectionFactory还使用 ​​bean 别名来提供 bean xaJmsConnectionFactory

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

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

和接口可用于支持嵌入式事务管理器XAConnectionFactoryWrapperXADataSourceWrapper这些接口负责包装XAConnectionFactoryBeanXADataSource并将它们公开为常规ConnectionFactoryBean DataSource,从而透明地注册到分布式事务中。DataSource 和 JMS 自动配置使用 JTA 变体,前提是您有一个JtaTransactionManagerbean 和在您的ApplicationContext.

9. 接下来读什么

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

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