IO
Большинству приложений в какой-то момент потребуется обрабатывать ввод и вывод. Spring Boot предоставляет утилиты и интеграции с рядом технологий, которые помогут вам, когда потребуются возможности ввода-вывода. Этот раздел охватывает стандартные функции ввода-вывода, такие как кэширование и валидация, а также более сложные темы, такие как планирование и распределенные транзакции. Мы также рассмотрим вызов удаленных REST- или SOAP-служб и отправку электронных писем.
1. Кэширование
Spring Framework предоставляет поддержку прозрачного добавления кэширования в приложение. В основе лежит абстракция, применяющая кэширование к методам, тем самым уменьшая количество выполнений, исходя из информации, доступной в кэше. Логика кэширования применяется прозрачно, без каких-либо помех для вызывающего кода. Spring Boot автоматически настраивает инфраструктуру кэша, если поддержка кэширования включена с помощью аннотации @EnableCaching.
| Подробную информацию см. в соответствующем разделе справочника Spring Framework. |
Короче говоря, чтобы добавить кэширование к операции вашего сервиса, добавьте соответствующую аннотацию в его метод, как показано в следующем примере:
@Component
public class MyMathService {
@Cacheable("piDecimals")
public int computePiDecimal(int precision) {
...
}
}
@Component
class MyMathService {
@Cacheable("piDecimals")
fun computePiDecimal(precision: Int): Int {
...
}
}
Этот пример демонстрирует использование кэширования для потенциально дорогостоящей операции. Перед вызовом computePiDecimal, абстракция ищет запись в кэше piDecimals, соответствующую аргументу i. Если запись найдена, содержимое кэша немедленно возвращается вызывающей стороне, и метод не вызывается. В противном случае, метод вызывается, а кэш обновляется перед возвратом значения.
Вы также можете использовать стандартные аннотации JSR-107 (JCache) (например, @CacheResult) прозрачно. Однако мы настоятельно рекомендуем не смешивать аннотации Spring Cache и JCache. |
Если вы не добавляете какую-либо конкретную библиотеку кэша, Spring Boot автоматически настраивает простой провайдер simple provider, использующий конкурентные карты в памяти. Когда требуется кэш (например, piDecimals в предыдущем примере), этот провайдер создает его для вас. Простой провайдер не рекомендуется для использования в производстве, но он отлично подходит для начала работы и понимания функций. Когда вы определились с провайдером кэша, убедитесь, что прочитали его документацию, чтобы понять, как настроить кэши, используемые вашим приложением. Почти все провайдеры требуют явного конфигурирования каждого используемого в приложении кэша. Некоторые предоставляют возможность настройки по умолчанию, определённых свойством spring.cache.cache-names.
1.1. Поддерживаемые поставщики кэша
Абстракция кэша не предоставляет фактическое хранилище и полагается на абстракцию, реализованную интерфейсами org.springframework.cache.Cache и org.springframework.cache.CacheManager.
Если вы не определили бин типа CacheManager или бин CacheResolver с именем cacheResolver (см. CachingConfigurer), Spring Boot пытается обнаружить следующие поставщики (в указанном порядке):
-
JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan и другие)
Кроме того, Spring Boot для Apache Geode предоставляет автоконфигурацию для использования Apache Geode в качестве поставщика кэша.
Если поставщик кэша CacheManager автоматически настроен Spring Boot, можно вынужденно указать определённый поставщик кэша, установив свойство spring.cache.type. Используйте это свойство, если вам нужно использовать кэши no-op в определённых средах (например, в тестах). |
Используйте spring-boot-starter-cache «Starter», чтобы быстро добавить базовые зависимости кэширования. Starter подключает spring-context-support. Если вы добавляете зависимости вручную, вам необходимо включить spring-context-support, чтобы использовать поддержку JCache или Caffeine. |
Если поставщик кэша CacheManager автоматически настроен Spring Boot, можно дополнительно настроить его конфигурацию до полного инициализации, экспонировав бин, реализующий интерфейс CacheManagerCustomizer. Следующий пример устанавливает флаг, указывающий, что значения null не должны передаваться в подлежащую карту:
@Configuration(proxyBeanMethods = false)
public class MyCacheManagerConfiguration {
@Bean
public CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
return (cacheManager) -> cacheManager.setAllowNullValues(false);
}
}
@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.Cache. Создается бин CacheManager , охватывающий все бины этого типа.
1.1.2. JCache (JSR-107)
JCache инициализируется наличием javax.cache.spi.CachingProvider в classpath (то есть, в classpath присутствует библиотека кэширования, совместимая с JSR-107), а JCacheCacheManager предоставляется spring-boot-starter-cache «Starter». Доступны различные совместимые библиотеки, и Spring Boot предоставляет управление зависимостями для Ehcache 3, Hazelcast и Infinispan. Также можно добавить любые другие совместимые библиотеки.
Возможно, что присутствует более одного поставщика, в этом случае поставщик должен быть указан явно. Даже если стандарт JSR-107 не устанавливает стандартизированный способ определения расположения файла конфигурации, Spring Boot делает всё возможное, чтобы адаптироваться к настройке кэша с реализацией деталей, как показано в следующем примере:
# Only necessary if more than one provider is present
spring.cache.jcache.provider=com.example.MyCachingProvider
spring.cache.jcache.config=classpath:example.xml # 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, он используется для их настройки. -
Бины
org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizerвызываются со ссылкой наCacheManagerдля полной настройки.
Если определен стандартный бин javax.cache.CacheManager, он автоматически обернётся в реализацию org.springframework.cache.CacheManager, которую ожидает абстракция. Дальнейшая настройка к нему не применяется. |
1.1.3. Hazelcast
Spring Boot имеет общую поддержку Hazelcast. Если HazelcastInstance был автоматически настроен и com.hazelcast:hazelcast-spring присутствует в classpath, он автоматически обернётся в 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 spring:
cache:
infinispan:
config: "infinispan.xml" Кэши могут быть созданы при запуске, задав свойство spring.cache.cache-names. Если определен кастомизированный бин ConfigurationBuilder, он используется для настройки кэшей.
Для совместимости с базовой линией Jakarta EE 9 Spring Boot, должны использоваться модули -jakarta Infinispan. Для каждого модуля с вариантом -jakarta, необходимо использовать вариант вместо стандартного модуля. Например, infinispan-core-jakarta и infinispan-commons-jakarta необходимо использовать вместо infinispan-core и infinispan-commons соответственно.
1.1.5. 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 spring:
cache:
cache-names: "cache1,cache2"
couchbase:
expiration: "10m" Если вам нужен больший контроль над конфигурацией, рассмотрите регистрацию бина CouchbaseCacheManagerBuilderCustomizer. Следующий пример показывает кастомайзер, который настраивает конкретное время истечения для cache1 и cache2:
@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)));
}
}
@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.*. Например, следующая конфигурация создаёт кэши cache1 и cache2 со временем жизни 10 минут:
spring.cache.cache-names=cache1,cache2
spring.cache.redis.time-to-live=10m spring:
cache:
cache-names: "cache1,cache2"
redis:
time-to-live: "10m" По умолчанию добавляется префикс ключа, чтобы, если два отдельных кэша используют один и тот же ключ, Redis не имел перекрывающихся ключей и не мог возвращать некорректные значения. Мы настоятельно рекомендуем оставлять эту настройку включенной, если вы создаёте собственные RedisCacheManager. |
Вы можете полностью контролировать стандартную конфигурацию, добавив собственный RedisCacheConfiguration @Bean. Это может быть полезно, если вам нужно настроить стратегию сериализации по умолчанию. |
Если вам нужен больший контроль над конфигурацией, рассмотрите возможность регистрации bean RedisCacheManagerBuilderCustomizer. Следующий пример демонстрирует кастомайзер, настраивающий конкретное время жизни для cache1 и cache2.
@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)));
}
}
@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 — это переписанная на Java 8 версия кэша Guava, которая заменяет поддержку Guava. Если Caffeine присутствует, автоматически настраивается CaffeineCacheManager (предоставленный spring-boot-starter-cache “Starter”). Кэши могут быть созданы при запуске путём установки свойства spring.cache.cache-names и могут быть настраиваемы одним из следующих способов (в указанном порядке):
-
Спецификация кэша, определённая
spring.cache.caffeine.spec -
Определён bean
com.github.benmanes.caffeine.cache.CaffeineSpec -
Определён bean
com.github.benmanes.caffeine.cache.Caffeine
Например, следующая конфигурация создаёт кэши cache1 и cache2 с максимальным размером 500 и временем жизни 10 минут.
spring.cache.cache-names=cache1,cache2
spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s spring:
cache:
cache-names: "cache1,cache2"
caffeine:
spec: "maximumSize=500,expireAfterAccess=600s" Если определён bean com.github.benmanes.caffeine.cache.CacheLoader, он автоматически ассоциируется с CaffeineCacheManager. Поскольку CacheLoader будет связан со всеми кэшами, управляемыми менеджером кэшей, он должен быть определён как CacheLoader<Object, Object>. Автонастройка игнорирует любой другой общий тип.
1.1.8. Cache2k
Cache2k — кэш в оперативной памяти. Если интегрирование Cache2k с Spring присутствует, автоматически настраивается SpringCache2kCacheManager.
Кэши могут быть созданы при запуске путём установки свойства spring.cache.cache-names Настройка параметров кэша по умолчанию может быть изменена с помощью bean Cache2kBuilderCustomizer. Следующий пример показывает кастомайзер, настраивающий ёмкость кэша на 200 записей со сроком действия 5 минут:
@Configuration(proxyBeanMethods = false)
public class MyCache2kDefaultsConfiguration {
@Bean
public Cache2kBuilderCustomizer myCache2kDefaultsCustomizer() {
return (builder) -> builder.entryCapacity(200)
.expireAfterWrite(5, TimeUnit.MINUTES);
}
}
@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 Например, если вам нужны только кэши cache1 и cache2, установите свойство cache-names следующим образом:
spring.cache.cache-names=cache1,cache2 spring:
cache:
cache-names: "cache1,cache2" Если вы так сделаете, и ваше приложение использует кэш, не указанный в списке, произойдёт ошибка во время выполнения, когда кэш потребуется, но не при запуске. Это аналогично поведению «настоящих» поставщиков кэшей, если вы используете не объявленный кэш.
1.1.10. None
Если @EnableCaching присутствует в вашей конфигурации, ожидается также соответствующая конфигурация кэша. Если у вас есть пользовательский CacheManager, рассмотрите возможность определения его в отдельном классе @Configuration, чтобы можно было переопределить его при необходимости. None использует реализацию no-op, которая полезна в тестах, и тесты slice используют её по умолчанию через @AutoConfigureCache.
Если вам нужно использовать кэш no-op вместо автоматически настроенного менеджера кэша в определённой среде, установите тип кэша на none, как показано в следующем примере:
spring.cache.type=none spring:
cache:
type: "none" 2. Hazelcast
Если Hazelcast находится в классе и найдена подходящая конфигурация, Spring Boot автоматически настраивает HazelcastInstance, который можно внедрить в ваше приложение.
Spring Boot сначала пытается создать клиента, проверяя следующие параметры конфигурации:
-
Наличие bean
com.hazelcast.client.config.ClientConfig. -
Файл конфигурации, определённый свойством
spring.hazelcast.config. -
Наличие системной переменной
hazelcast.client.config. -
Файл
hazelcast-client.xmlв рабочей директории или в корне classpath. -
Файл
hazelcast-client.yaml(илиhazelcast-client.yml) в рабочей директории или в корне classpath.
Если клиент не может быть создан, Spring Boot пытается настроить встроенный сервер. Если вы определяете bean com.hazelcast.config.Config , Spring Boot использует его. Если в вашей конфигурации определено имя экземпляра, Spring Boot пытается найти существующий экземпляр вместо создания нового.
Вы также можете указать используемый файл конфигурации Hazelcast через конфигурацию, как показано в следующем примере:
spring.hazelcast.config=classpath:config/my-hazelcast.xml spring:
hazelcast:
config: "classpath:config/my-hazelcast.xml" В противном случае Spring Boot пытается найти конфигурацию Hazelcast из стандартных расположений: hazelcast.xml в рабочей директории или в корне classpath, или YAML-аналог в тех же местах. Мы также проверяем, установлена ли системная переменная hazelcast.config. Для получения более подробной информации см. документацию Hazelcast.
По умолчанию поддержка @SpringAware на компонентах Hazelcast включена. Можно переопределить ManagementContext с помощью объявления bean HazelcastConfigCustomizer с @Order выше нуля. |
Spring Boot также имеет явную поддержку кэширования для Hazelcast. Если кэширование включено, HazelcastInstance автоматически оборачивается в реализацию CacheManager . |
3. Планировщик Quartz
Spring Boot предлагает удобства для работы с планировщиком Quartz, включая модуль spring-boot-starter-quartz. Если Quartz доступен, планировщик Scheduler автоматически настроен (через абстракцию SchedulerFactoryBean).
Компоненты следующих типов автоматически подхватываются и связываются с планировщиком Scheduler:
-
JobDetail: определяет конкретную задачу. ЭкземплярыJobDetailмогут быть созданы с помощью APIJobBuilder. -
Calendar. -
Trigger: определяет, когда запускается конкретная задача.
По умолчанию используется JobStore в памяти. Однако можно настроить хранилище на основе JDBC, если в приложении доступен компонент DataSource и свойство spring.quartz.job-store-type настроено соответствующим образом, как показано в следующем примере:
spring.quartz.job-store-type=jdbc spring:
quartz:
job-store-type: "jdbc" При использовании хранилища JDBC схема может быть инициализирована при запуске, как показано в следующем примере:
spring.quartz.jdbc.initialize-schema=always spring:
quartz:
jdbc:
initialize-schema: "always" По умолчанию база данных обнаруживается и инициализируется с помощью стандартных скриптов, предоставляемых библиотекой Quartz. Эти скрипты удаляют существующие таблицы, удаляя все триггеры при каждом перезапуске. Также можно предоставить пользовательский скрипт, установив свойство spring.quartz.jdbc.schema. |
Чтобы использовать DataSource вместо основного DataSource приложения, объявите компонент DataSource, аннотируя его метод @Bean аннотацией @QuartzDataSource. Это гарантирует, что специфичный для Quartz DataSource используется как для SchedulerFactoryBean, так и для инициализации схемы. Аналогично, для использования TransactionManager вместо основного TransactionManager приложения, объявите компонент TransactionManager, аннотируя его метод @Bean аннотацией @QuartzTransactionManager.
По умолчанию задачи, созданные с помощью конфигурации, не будут перезаписывать уже зарегистрированные задачи, которые были прочитаны из постоянного хранилища задач. Чтобы разрешить перезапись существующих определений задач, установите свойство spring.quartz.overwrite-existing-jobs.
Конфигурацию Quartz Scheduler можно настроить, используя свойства spring.quartz и компоненты SchedulerFactoryBeanCustomizer, которые позволяют программно настраивать SchedulerFactoryBean. Расширенные свойства конфигурации Quartz можно настроить с помощью spring.quartz.properties.*.
В частности, компонент Executor не связан с планировщиком, так как Quartz предлагает способ настройки планировщика через spring.quartz.properties. Если вам нужно настроить исполнителя задач, рассмотрите реализацию SchedulerFactoryBeanCustomizer. |
Задачи могут определять сеттеры для ввода свойств карты данных. Регулярные компоненты также могут быть введены аналогичным образом, как показано в следующем примере:
public class MySampleJob extends QuartzJobBean {
// 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);
}
}
class MySampleJob : QuartzJobBean() {
// 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 Framework предоставляет абстракцию для отправки электронной почты, используя интерфейс 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 spring:
mail:
properties:
"[mail.smtp.connectiontimeout]": 5000
"[mail.smtp.timeout]": 3000
"[mail.smtp.writetimeout]": 5000 Также можно настроить JavaMailSender с использованием существующего Session из JNDI:
spring.mail.jndi-name=mail/Session spring:
mail:
jndi-name: "mail/Session" Когда задаётся jndi-name, он имеет приоритет над всеми другими настройками, связанными с сеансом.
5. Валидация
Функциональность валидации методов, поддерживаемая Bean Validation 1.1, автоматически включена, если реализация JSR-303 (например, Hibernate validator) находится в пути к классам. Это позволяет аннотировать методы bean ограничениями jakarta.validation для параметров и/или возвращаемого значения. Классы-цели с такими аннотированными методами должны быть аннотированы аннотацией @Validated на уровне типа, чтобы их методы могли быть проверены на наличие встроенных аннотаций ограничений.
Например, следующий сервис запускает валидацию первого аргумента, убеждаясь, что его размер находится в диапазоне от 8 до 10:
@Service
@Validated
public class MyBean {
public Archive findByCodeAndAuthor(@Size(min = 8, max = 10) String code, Author author) {
return ...
}
}
@Service
@Validated
class MyBean {
fun findByCodeAndAuthor(code: @Size(min = 8, max = 10) String?, author: Author?): Archive? {
return null
}
}
В приложении используется MessageSource при разрешении {parameters} в сообщениях ограничений. Это позволяет использовать файлы messages.properties приложения для сообщений Bean Validation. После разрешения параметров завершается интерполяция сообщений с помощью стандартного интерполятора Bean Validation.
Чтобы настроить Configuration для построения ValidatorFactory, определите компонент ValidationConfigurationCustomizer. Если определено несколько компонентов-настроек, они вызываются в порядке, основанном на аннотации @Order или реализации Ordered.
6. Вызов REST-сервисов
Если ваше приложение обращается к удалённым REST-сервисам, Spring Boot делает это очень удобным с помощью RestTemplate или WebClient.
6.1. RestTemplate
Если вам нужно обращаться к удалённым REST-сервисам из вашего приложения, вы можете использовать класс RestTemplate Spring Framework. Поскольку экземпляры RestTemplate часто требуют настройки перед использованием, Spring Boot не предоставляет единственный автоматически настроенный RestTemplate бин. Однако он автоматически настраивает RestTemplateBuilder, который можно использовать для создания экземпляров RestTemplate при необходимости. Автоматически настроенный RestTemplateBuilder гарантирует, что разумные HttpMessageConverters применяются к экземплярам 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);
}
}
@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-клиент
Spring Boot автоматически определит, какой HTTP-клиент использовать с RestTemplate в зависимости от библиотек, доступных в пути к классам приложения. В порядке приоритета поддерживаются следующие клиенты:
-
Apache HttpClient
-
OkHttp
-
Простой JDK-клиент (
HttpURLConnection)
Если на пути к классам доступны несколько клиентов, будет использован наиболее предпочтительный клиент.
6.1.2. Настройка RestTemplate
Существует три основных подхода к настройке RestTemplate, в зависимости от того, насколько широко вы хотите применить настройки.
Чтобы сделать область действия любых настроек максимально узкой, введите автоматически настроенный RestTemplateBuilder и затем вызовите его методы по мере необходимости. Каждый вызов метода возвращает новый экземпляр RestTemplateBuilder, поэтому настройки влияют только на это использование билдера.
Чтобы осуществить прикладную, аддитивную настройку для всего приложения, используйте бин RestTemplateCustomizer. Все такие бины автоматически регистрируются в автоматически настроенном RestTemplateBuilder и применяются к любым шаблонам, которые строятся с его помощью.
Следующий пример показывает кастомайзер, который настраивает использование прокси для всех хостов, кроме 192.168.0.5:
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);
}
}
}
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. Это заменит автоматически настроенный билдер. Если вы хотите, чтобы какие-либо бины RestTemplateCustomizer были применены к вашему пользовательскому билдеру, как это сделала бы автоматическая настройка, настройте его с помощью RestTemplateBuilderConfigurer. Следующий пример демонстрирует RestTemplateBuilder bean, соответствующий тому, что сделала бы автоматическая настройка Spring Boot, за исключением того, что также указаны пользовательские таймауты подключения и чтения:
@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));
}
}
@Configuration(proxyBeanMethods = false)
class MyRestTemplateBuilderConfiguration {
@Bean
fun restTemplateBuilder(configurer: RestTemplateBuilderConfigurer): RestTemplateBuilder {
return configurer.configure(RestTemplateBuilder()).setConnectTimeout(Duration.ofSeconds(5))
.setReadTimeout(Duration.ofSeconds(2))
}
}
Самый экстремальный (и редко используемый) вариант — создать свой собственный бин RestTemplateBuilder без использования конфигуратора. Помимо замены автоматически настроенного билдера, это также предотвращает использование любых бинов RestTemplateCustomizer.
6.1.3. Поддержка SSL в RestTemplate
Если вам нужна настройка SSL в RestTemplate, вы можете применить SSL-пакет SSL-пакет к RestTemplateBuilder как показано в этом примере:
@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);
}
}
@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. WebClient
Если у вас Spring WebFlux присутствует в пути к классам, вы также можете использовать WebClient для вызова удалённых REST-сервисов. По сравнению с RestTemplate, этот клиент имеет более функциональный вид и полностью реактивен. Вы можете узнать больше о WebClient в посвященном разделе документации Spring Framework.
Spring Boot создаёт и предварительно настраивает WebClient.Builder для вас. Настоятельно рекомендуется вводить его в компоненты и использовать для создания экземпляров WebClient. Spring Boot настраивает этот билдер для совместного использования HTTP-ресурсов, отражения настроек кодировщиков таким же образом, как и серверные (см. автонастройку WebFlux HTTP-кодировщиков), и многое другое.
Следующий код показывает типичный пример:
@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);
}
}
@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. WebClient Runtime
Spring Boot автоматически определит, какой ClientHttpConnector использовать для управления WebClient в зависимости от библиотек, доступных в пути к классам приложения. В порядке приоритета поддерживаются следующие клиенты:
-
Reactor Netty
-
Клиент Jetty RS
-
Apache HttpClient
-
JDK HttpClient
Если на пути к классам доступны несколько клиентов, будет использован наиболее предпочтительный клиент.
Инициализатор spring-boot-starter-webflux по умолчанию зависит от io.projectreactor.netty:reactor-netty, что включает в себя как серверные, так и клиентские реализации. Если вы выбираете Jetty в качестве реактивного сервера, вы должны добавить зависимость от библиотеки реактивного HTTP-клиента Jetty, org.eclipse.jetty:jetty-reactive-httpclient. Использование одной и той же технологии для сервера и клиента имеет свои преимущества, так как это позволит автоматически совместно использовать HTTP-ресурсы между клиентом и сервером.
Разработчики могут переопределить конфигурацию ресурсов для Jetty и Reactor Netty, предоставив пользовательский бин ReactorResourceFactory или JettyResourceFactory, — это будет применено как к клиентам, так и к серверам.
Если вы хотите переопределить этот выбор для клиента, вы можете определить свой собственный бин ClientHttpConnector и иметь полный контроль над конфигурацией клиента.
Вы можете узнать больше о WebClient параметрах конфигурации в справочной документации Spring Framework.
6.2.2. Настройка WebClient
Существует три основных подхода к WebClient настройке, в зависимости от того, насколько широко вы хотите применить настройки.
Чтобы сделать область действия любых настроек максимально узкой, введите автоматически настроенный WebClient.Builder и затем вызовите его методы по мере необходимости. Экземпляры WebClient.Builder являются состоятельными: любые изменения в билдере отражаются во всех клиентах, созданных с ним. Если вы хотите создать несколько клиентов с одним и тем же билдером, вы также можете рассмотреть клонирование билдера с помощью WebClient.Builder other = builder.clone();.
Чтобы осуществить прикладную, аддитивную настройку для всех WebClient.Builder экземпляров, вы можете объявить бины WebClientCustomizer и изменить WebClient.Builder локально в точке ввода.
Наконец, вы можете вернуться к исходному API и использовать WebClient.create(). В этом случае ни автоматическая настройка, ни WebClientCustomizer не применяются.
6.2.3. Поддержка SSL в WebClient
Если вам нужна настройка SSL для ClientHttpConnector используемого WebClient, вы можете ввести экземпляр WebClientSsl, который можно использовать с методом билдера apply.
Интерфейс WebClientSsl предоставляет доступ к любым SSL-пакетам, которые вы определили в вашем файле application.properties или application.yaml.
Следующий код показывает типичный пример:
@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);
}
}
@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 предоставляет автоматическую настройку веб-сервисов, так что вам нужно только определить ваши Endpoints.
Функции Spring Web Services могут быть легко доступны с помощью модуля spring-boot-starter-webservices.
SimpleWsdl11Definition и SimpleXsdSchema бин могут быть автоматически созданы для ваших WSDL и XSD соответственно. Для этого настройте их расположение, как показано в следующем примере:
spring.webservices.wsdl-locations=classpath:/wsdl spring:
webservices:
wsdl-locations: "classpath:/wsdl" 7.1. Вызов веб-сервисов с помощью WebServiceTemplate
Если вам нужно вызывать удаленные веб-сервисы из вашего приложения, вы можете использовать класс WebServiceTemplate. Поскольку экземпляры WebServiceTemplate часто требуют настройки перед использованием, Spring Boot не предоставляет ни одного автоматически настроенного бинa WebServiceTemplate. Однако он автоматически настраивает бин 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"));
}
}
@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-сервис WebServiceMessageSender с помощью доступных библиотек HTTP в классе. Вы также можете настроить таймауты чтения и подключения следующим образом:
@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();
}
}
@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 поддерживает распределенные транзакции JTA по нескольким ресурсам XA, используя менеджер транзакций, полученный из JNDI.
Когда среда JTA обнаружена, Spring использует свой JtaTransactionManager для управления транзакциями. Автоматически настроенные бин JMS, DataSource и JPA обновляются для поддержки XA транзакций. Вы можете использовать стандартные выражения Spring, такие как @Transactional, чтобы участвовать в распределенной транзакции. Если вы находитесь в среде JTA и всё ещё хотите использовать локальные транзакции, вы можете установить свойство spring.jta.enabled в значение false для отключения автоматической настройки JTA.
8.1. Использование управляемого менеджера транзакций Jakarta EE
Если вы упаковываете своё приложение Spring Boot в файл war или ear и развертываете его на сервере приложений Jakarta EE, вы можете использовать встроенный менеджер транзакций сервера приложений. Spring Boot пытается автоматически настроить менеджер транзакций, просматривая общие расположения JNDI (java:comp/UserTransaction, java:comp/TransactionManager, и так далее). При использовании сервиса транзакций, предоставляемого сервером приложений, вам также необходимо убедиться, что все ресурсы управляются сервером и доступны через JNDI. Spring Boot пытается автоматически настроить JMS, ища ConnectionFactory по пути JNDI (java:/JmsXA или java:/XAConnectionFactory), и вы можете использовать свойство spring.datasource.jndi-name для настройки вашего DataSource.
8.2. Смешивание XA и не-XA подключений JMS
При использовании JTA основной бин JMS ConnectionFactory понимает XA и участвует в распределенных транзакциях. Вы можете вводить его в свой бин без необходимости использовать любые @Qualifier:
public MyBean(ConnectionFactory connectionFactory) {
// ...
}
В некоторых ситуациях вы можете захотеть обработать определённые сообщения JMS с помощью не-XA ConnectionFactory. Например, ваша логика обработки JMS может занимать больше времени, чем таймаут XA.
Если вы хотите использовать не-XA ConnectionFactory, вы можете использовать бин nonXaJmsConnectionFactory:
public MyBean(@Qualifier("nonXaJmsConnectionFactory") ConnectionFactory connectionFactory) {
// ...
}
Для согласованности также предоставляется бин jmsConnectionFactory с псевдонимом бинa xaJmsConnectionFactory:
public MyBean(@Qualifier("xaJmsConnectionFactory") ConnectionFactory connectionFactory) {
// ...
}
8.3. Поддержка встроенного менеджера транзакций
Интерфейсы XAConnectionFactoryWrapper и XADataSourceWrapper могут использоваться для поддержки встроенных менеджеров транзакций. Интерфейсы отвечают за обёртку бинa XAConnectionFactory и XADataSource и выставление их как обычных бинa ConnectionFactory и DataSource, которые прозрачно регистрируются в распределённой транзакции. Автоматическая настройка DataSource и JMS использует варианты JTA при наличии бинa JtaTransactionManager и соответствующих бинa-обёрток XA, зарегистрированных в вашем ApplicationContext.
9. Что читать дальше
Теперь у вас должно быть хорошее понимание основных функций Spring Boot и различных технологий, которые Spring Boot поддерживает с помощью автоматической настройки.
Следующие несколько разделов подробно рассказывают о развертывании приложений на облачных платформах. Вы можете узнать о создании образов контейнеров в следующем разделе или перейти к разделу готовых к производству функций.
Copyright © 2012-2023 VMware, Inc.
Licensed under the Apache License, Version 2.0.
https://docs.spring.io/spring-boot/docs/3.1.3/reference/html/io.html