Spec-Zone.ru › Spring Boot

«Руководства по использованию»

Этот раздел содержит ответы на некоторые распространенные вопросы типа «как это сделать…», которые часто возникают при использовании Spring Boot. Охват не исчерпывающий, но охватывает довольно много аспектов.

Если у вас есть специфическая проблема, которая здесь не рассматривается, вы можете проверить stackoverflow.com, чтобы посмотреть, уже ли кто-то предоставил ответ. Это также отличное место для задавания новых вопросов (пожалуйста, используйте тег spring-boot).

Мы также с удовольствием расширим этот раздел. Если вы хотите добавить «как это сделать», отправьте нам pull request.

1. Приложение Spring Boot

Этот раздел включает темы, напрямую относящиеся к приложениям Spring Boot.

1.1. Создание собственного анализатора ошибок

FailureAnalyzer — отличный способ перехватить исключение при запуске и преобразовать его в удобочитаемое сообщение, заключенное в FailureAnalysis. Spring Boot предоставляет такой анализатор для исключений, связанных с контекстом приложения, валидациями JSR-303 и другими. Вы также можете создать свой собственный.

AbstractFailureAnalyzer — это удобное расширение FailureAnalyzer, которое проверяет наличие указанного типа исключения в исключении для обработки. Вы можете расширить его, чтобы ваша реализация могла обработать исключение только тогда, когда оно действительно присутствует. Если по какой-либо причине вы не можете обработать исключение, верните null, чтобы дать другой реализации возможность обработать исключение.

Реализации FailureAnalyzer должны быть зарегистрированы в META-INF/spring.factories. Следующий пример регистрирует ProjectConstraintViolationFailureAnalyzer:

org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.ProjectConstraintViolationFailureAnalyzer
Если вам нужен доступ к BeanFactory или Environment, ваша FailureAnalyzer может реализовать соответственно BeanFactoryAware или EnvironmentAware.

1.2. Устранение неполадок в автоматической конфигурации

Автоматическая конфигурация Spring Boot старается сделать всё наилучшим образом, но иногда возникают ошибки, и определить причину может быть сложно.

В любом приложении Spring Boot доступен очень полезный ConditionEvaluationReport. Вы можете увидеть его, если включить DEBUG вывод журналов. Если вы используете spring-boot-actuator (см. главу «Актуатор»), также есть конечная точка conditions, которая отображает отчёт в формате JSON. Используйте эту конечную точку для отладки приложения и просмотра функций, добавленных (и не добавленных) Spring Boot во время выполнения.

Множество дополнительных вопросов можно ответить, изучив исходный код и JavaDoc. При чтении кода помните следующие правила:

  • Ищите классы с именем *AutoConfiguration и читайте их исходный код. Обратите особое внимание на аннотации @Conditional*, чтобы узнать, какие функции они включают и когда. Добавьте --debug в командную строку или системную переменную -Ddebug, чтобы получить в консоли журнал всех принятых решений по автоматической конфигурации в вашем приложении. В работающем приложении с активированным актуатором посмотрите на конечную точку conditions (/actuator/conditions или эквивалент JMX) для получения той же информации.

  • Ищите классы, которые являются @ConfigurationProperties (такие как ServerProperties) и читайте доступные внешние параметры конфигурации. Аннотация @ConfigurationProperties имеет атрибут name, который выступает в качестве префикса для внешних свойств. Таким образом, ServerProperties имеет prefix="server", и его свойства конфигурации являются server.port, server.address и другими. В работающем приложении с активированным актуатором посмотрите на конечную точку configprops.

  • Ищите использование метода bind на классе Binder для явного извлечения значений конфигурации из Environment лёгким способом. Часто используется с префиксом.

  • Ищите аннотации @Value, которые напрямую привязываются к Environment.

  • Ищите аннотации @ConditionalOnExpression, которые включают и выключают функции в ответ на выражения SpEL, обычно вычисляемые с разрешёнными плейсхолдерами из Environment.

1.3. Настройка среды или ApplicationContext перед запуском

У SpringApplication есть ApplicationListeners и ApplicationContextInitializers, которые используются для применения настроек к контексту или среде. Spring Boot загружает множество таких настроек для внутреннего использования из META-INF/spring.factories. Существует более одного способа регистрации дополнительных настроек:

  • Программно, на уровне приложения, вызвав методы addListeners и addInitializers на объекте SpringApplication до его запуска.

  • Декларативно, на уровне приложения, установив свойства context.initializer.classes или context.listener.classes.

  • Декларативно, для всех приложений, добавив META-INF/spring.factories и упаковав jar-файл, который все приложения используют как библиотеку.

SpringApplication отправляет некоторые специальные ApplicationEvents слушателям (некоторые даже до создания контекста), а затем регистрирует слушателей для событий, опубликованных ApplicationContext. См. «События и слушатели приложений» в разделе «Функции Spring Boot» для полного списка.

Также можно настроить Environment до обновления контекста приложения, используя EnvironmentPostProcessor. Каждая реализация должна быть зарегистрирована в META-INF/spring.factories, как показано в следующем примере:

org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor

Реализация может загружать произвольные файлы и добавлять их в Environment. Например, следующий пример загружает YAML-конфигурационный файл из classpath:

Java
import java.io.IOException;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;

public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        Resource path = new ClassPathResource("com/example/myapp/config.yml");
        PropertySource<?> propertySource = loadYaml(path);
        environment.getPropertySources().addLast(propertySource);
    }

    private PropertySource<?> loadYaml(Resource path) {
        Assert.isTrue(path.exists(), () -> "Resource " + path + " does not exist");
        try {
            return this.loader.load("custom-resource", path).get(0);
        }
        catch (IOException ex) {
            throw new IllegalStateException("Failed to load yaml configuration from " + path, ex);
        }
    }

}
Kotlin
import org.springframework.boot.SpringApplication
import org.springframework.boot.env.EnvironmentPostProcessor
import org.springframework.boot.env.YamlPropertySourceLoader
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.core.env.PropertySource
import org.springframework.core.io.ClassPathResource
import org.springframework.core.io.Resource
import org.springframework.util.Assert
import java.io.IOException

class MyEnvironmentPostProcessor : EnvironmentPostProcessor {

    private val loader = YamlPropertySourceLoader()

    override fun postProcessEnvironment(environment: ConfigurableEnvironment, application: SpringApplication) {
        val path: Resource = ClassPathResource("com/example/myapp/config.yml")
        val propertySource = loadYaml(path)
        environment.propertySources.addLast(propertySource)
    }

    private fun loadYaml(path: Resource): PropertySource<*> {
        Assert.isTrue(path.exists()) { "Resource $path does not exist" }
        return try {
            loader.load("custom-resource", path)[0]
        } catch (ex: IOException) {
            throw IllegalStateException("Failed to load yaml configuration from $path", ex)
        }
    }

}
Environment уже подготовлен со всеми обычными источниками свойств, которые Spring Boot загружает по умолчанию. Поэтому можно получить расположение файла из среды. В предыдущем примере добавляется источник свойств custom-resource в конец списка, чтобы ключ, определённый в любом другом расположении, имел приоритет. Пользовательская реализация может определить другой порядок.
Хотя использование @PropertySource в вашей @SpringBootApplication может показаться удобным способом загрузки пользовательских ресурсов в Environment, мы не рекомендуем этого делать. Такие источники свойств не добавляются в Environment до тех пор, пока контекст приложения не обновляется. Это происходит слишком поздно для настройки определённых свойств, таких как logging.* и spring.main.*, которые читаются до начала обновления.

1.4. Построение иерархии ApplicationContext (Добавление родительского или корневого контекста)

Для создания иерархий родительских/дочерних ApplicationContext контекстов можно использовать класс ApplicationBuilder. Подробнее см. «features.html» в разделе «Функции Spring Boot».

1.5. Создание не веб-приложения

Не все приложения Spring должны быть веб-приложениями (или веб-сервисами). Если вы хотите выполнить код в методе main, но также запустить приложение Spring для настройки инфраструктуры, вы можете использовать возможности Spring Boot. SpringApplication меняет свой класс ApplicationContext, в зависимости от того, считает ли он, что ему нужно веб-приложение или нет. Первое, что вы можете сделать, это убрать зависимости, связанные с сервером (например, API сервлетов) из classpath. Если вы этого сделать не можете (например, вы запускаете два приложения из одного кода), вы можете явно вызвать метод setWebApplicationType(WebApplicationType.NONE) на вашем объекте SpringApplication или установить свойство applicationContextClass (через Java API или с помощью внешних свойств). Код приложения, который вы хотите использовать как бизнес-логику, может быть реализован как CommandLineRunner и добавлен в контекст как определение @Bean.

2. Свойства и конфигурация

В этом разделе рассматриваются темы настройки и чтения свойств и параметров конфигурации, а также их взаимодействие с приложениями Spring Boot.

2.1. Автоматическое расширение свойств во время сборки

Вместо того, чтобы жестко кодировать некоторые свойства, которые также указаны в конфигурации сборки вашего проекта, вы можете автоматически расширить их, используя существующую конфигурацию сборки. Это возможно как в Maven, так и в Gradle.

2.1.1. Автоматическое расширение свойств с помощью Maven

Вы можете автоматически расширить свойства из проекта Maven, используя фильтрацию ресурсов. Если вы используете spring-boot-starter-parent, вы можете ссылаться на свои свойства Maven-проекта с помощью @..@ плейсхолдеров, как показано в следующем примере:

Свойства
app.encoding=@project.build.sourceEncoding@
app.java.version=@java.version@
Yaml
app:
  encoding: "@project.build.sourceEncoding@"
  java:
    version: "@java.version@"
Фильтруется только конфигурация для среды производства (другими словами, фильтрация не применяется к src/test/resources).
Если вы включите флаг addResources, цель spring-boot:run может добавить src/main/resources непосредственно в класспасс (для целей горячей перезагрузки). Это обойдет фильтрацию ресурсов и данную функцию. Вместо этого вы можете использовать цель exec:java или настроить конфигурацию плагина. Смотрите страницу использования плагина здесь для получения дополнительной информации.

Если вы не используете родительский стартер, вам необходимо включить следующий элемент внутри элемента <build/> вашего pom.xml:

<resources>
    <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
    </resource>
</resources>

Вам также необходимо включить следующий элемент внутри <plugins/>:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <version>2.7</version>
    <configuration>
        <delimiters>
            <delimiter>@</delimiter>
        </delimiters>
        <useDefaultDelimiters>false</useDefaultDelimiters>
    </configuration>
</plugin>
Свойство useDefaultDelimiters важно, если вы используете стандартные плейсхолдеры Spring (например, ${placeholder}) в вашей конфигурации. Если это свойство не установлено в значение false, они могут быть расширены во время сборки.

2.1.2. Автоматическое расширение свойств с помощью Gradle

Вы можете автоматически расширить свойства из проекта Gradle, настроив задачу плагина Java processResources, как показано в следующем примере:

tasks.named('processResources') {
    expand(project.properties)
}

Затем вы можете обратиться к свойствам вашего проекта Gradle, используя плейсхолдеры, как показано в следующем примере:

Свойства
app.name=${name}
app.description=${description}
Yaml
app:
  name: "${name}"
  description: "${description}"
Метод expand Gradle использует Groovy's SimpleTemplateEngine, который преобразует ${..} токены. Стиль ${..} конфликтует с собственным механизмом подстановки свойств Spring. Для использования плейсхолдеров свойств Spring вместе с автоматическим расширением, экранируйте плейсхолдеры свойств Spring следующим образом: \${..}.

2.2. Экстернализация конфигурации SpringApplication

Класс SpringApplication имеет геттеры и сеттеры свойств, поэтому вы можете использовать его Java API для изменения поведения приложения. Кроме того, вы можете экстернализировать конфигурацию, задав свойства в файле spring.main.*. Например, в application.properties могут быть следующие настройки:

Свойства
spring.main.web-application-type=none
spring.main.banner-mode=off
Yaml
spring:
  main:
    web-application-type: "none"
    banner-mode: "off"

Тогда баннер Spring Boot не будет выводиться при запуске, и приложение не будет запускать встроенный веб-сервер.

Свойства, определённые во внешней конфигурации, переопределяют и заменяют значения, заданные с помощью Java API, за исключением первичных источников. Первичные источники — это те, которые были предоставлены конструктору SpringApplication:

Java
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(MyApplication.class);
        application.setBannerMode(Banner.Mode.OFF);
        application.run(args);
    }

}
Kotlin
import org.springframework.boot.Banner
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
object MyApplication {

    @JvmStatic
    fun main(args: Array<String>) {
        val application = SpringApplication(MyApplication::class.java)
        application.setBannerMode(Banner.Mode.OFF)
        application.run(*args)
    }

}

Или метода sources(…​) класса SpringApplicationBuilder:

Java
import org.springframework.boot.Banner;
import org.springframework.boot.builder.SpringApplicationBuilder;

public class MyApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder()
            .bannerMode(Banner.Mode.OFF)
            .sources(MyApplication.class)
            .run(args);
    }

}
Kotlin
import org.springframework.boot.Banner
import org.springframework.boot.builder.SpringApplicationBuilder

object MyApplication {

    @JvmStatic
    fun main(args: Array<String>) {
        SpringApplicationBuilder()
            .bannerMode(Banner.Mode.OFF)
            .sources(MyApplication::class.java)
            .run(*args)
    }

}

Учитывая примеры выше, если у нас есть такая конфигурация:

Свойства
spring.main.sources=com.example.MyDatabaseConfig,com.example.MyJmsConfig
spring.main.banner-mode=console
Yaml
spring:
  main:
    sources: "com.example.MyDatabaseConfig,com.example.MyJmsConfig"
    banner-mode: "console"

Фактическое приложение покажет баннер (как переопределённый конфигурацией) и использует три источника для ApplicationContext. Источники приложения:

  1. MyApplication (из кода)

  2. MyDatabaseConfig (из внешней конфигурации)

  3. MyJmsConfig (из внешней конфигурации)

2.3. Изменение расположения внешних свойств приложения

По умолчанию свойства из разных источников добавляются в Spring Environment в определённом порядке (см. «features.html» в разделе «Функции Spring Boot» для точного порядка).

Вы также можете указать следующие системные свойства (или переменные окружения) для изменения поведения:

  • spring.config.name (SPRING_CONFIG_NAME): По умолчанию application в качестве корня имени файла.

  • spring.config.location (SPRING_CONFIG_LOCATION): Файл для загрузки (например, ресурс из classpath или URL). Для этого документа настраивается отдельный источник свойств Environment, который можно переопределить системными свойствами, переменными окружения или командной строкой.

Независимо от того, что вы устанавливаете в среде, Spring Boot всегда загружает application.properties, как описано выше. По умолчанию, если используется YAML, файлы с расширениями ‘.yaml’ и ‘.yml’ также добавляются в список.

Если вы хотите получить подробную информацию о загружаемых файлах, вы можете установить уровень логирования org.springframework.boot.context.config на trace.

2.4. Использование «коротких» аргументов командной строки

Некоторые предпочитают использовать (например) --port=9000 вместо --server.port=9000 для настройки свойств конфигурации в командной строке. Вы можете включить это поведение, используя плейсхолдеры в файле application.properties, как показано в следующем примере:

Свойства
server.port=${port:8080}
Yaml
server:
  port: "${port:8080}"
Если вы наследуете от spring-boot-starter-parent POM, токен фильтра по умолчанию для maven-resources-plugins был изменён с ${*} на @ (то есть @maven.token@ вместо ${maven.token}) для предотвращения конфликтов с плейсхолдерами Spring. Если вы включили фильтрацию Maven для application.properties напрямую, возможно, вам также следует изменить токен фильтра по умолчанию, используя другие разделители.
В данном случае привязка порта работает в среде PaaS, такой как Heroku или Cloud Foundry. На этих платформах переменная окружения PORT устанавливается автоматически, и Spring может привязаться к заглавным синонимам для свойств Environment.

2.5. Использование YAML для внешних свойств

YAML является надмножеством JSON и, как таковой, представляет собой удобный синтаксис для хранения внешних свойств в иерархическом формате, как показано в следующем примере:

spring:
  application:
    name: "cruncher"
  datasource:
    driver-class-name: "com.mysql.jdbc.Driver"
    url: "jdbc:mysql://localhost/test"
server:
  port: 9000

Создайте файл с именем application.yaml и поместите его в корень вашего класса. Затем добавьте snakeyaml в свои зависимости (координаты Maven org.yaml:snakeyaml, уже включенные, если вы используете spring-boot-starter). Файл YAML парсится в Java-объект Map<String,Object> (как JSON-объект), а Spring Boot делает массив одноуровневым с ключами, разделёнными точками, как многие привыкли к файлам Properties в Java.

Предыдущий пример YAML соответствует следующему файлу application.properties:

spring.application.name=cruncher
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/test
server.port=9000

См. «features.html» в разделе «Функции Spring Boot» для получения дополнительной информации о YAML.

2.6. Установка активных профилей Spring

Spring Environment имеет API для этого, но обычно вы устанавливаете системную переменную (spring.profiles.active) или переменную среды ОС (SPRING_PROFILES_ACTIVE). Также вы можете запустить своё приложение с аргументом -D (не забудьте разместить его перед именем главного класса или JAR-архивом), как показано ниже:

$ java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar

В Spring Boot вы также можете установить активный профиль в application.properties, как показано в следующем примере:

Свойства
spring.profiles.active=production
Yaml
spring:
  profiles:
    active: "production"

Значение, установленное таким образом, заменяется системной переменной или переменной среды, но не методом SpringApplicationBuilder.profiles(). Таким образом, последний API Java может использоваться для дополнения профилей без изменения значений по умолчанию.

См. «features.html» в разделе «Функции Spring Boot» для получения дополнительной информации.

2.7. Установка имени профиля по умолчанию

Профиль по умолчанию — это профиль, который активируется, если никакой профиль не активен. По умолчанию имя профиля по умолчанию — default, но его можно изменить с помощью системной переменной (spring.profiles.default) или переменной среды ОС (SPRING_PROFILES_DEFAULT).

В Spring Boot вы также можете установить имя профиля по умолчанию в application.properties, как показано в следующем примере:

Свойства
spring.profiles.default=dev
Yaml
spring:
  profiles:
    default: "dev"

См. «features.html» в разделе «Функции Spring Boot» для получения дополнительной информации.

2.8. Изменение конфигурации в зависимости от среды

Spring Boot поддерживает файлы YAML и Properties с несколькими документами (подробнее см. features.html), которые могут быть активированы условно в зависимости от активных профилей.

Если документ содержит ключ spring.config.activate.on-profile, то значение профилей (список профилей или выражение профиля, разделённые запятыми) передаётся в метод Spring Environment.acceptsProfiles(). Если выражение профиля соответствует, то этот документ включается в итоговую слияние (в противном случае — нет), как показано в следующем примере:

Свойства
server.port=9000
#---
spring.config.activate.on-profile=development
server.port=9001
#---
spring.config.activate.on-profile=production
server.port=0
Yaml
server:
  port: 9000
---
spring:
  config:
    activate:
      on-profile: "development"
server:
  port: 9001
---
spring:
  config:
    activate:
      on-profile: "production"
server:
  port: 0

В приведённом примере порт по умолчанию равен 9000. Однако, если активен профиль Spring под названием «development», порт равен 9001. Если активен профиль «production», порт равен 0.

Документы сливаются в том порядке, в котором они встречаются. Более поздние значения переопределяют более ранние.

2.9. Обнаружение встроенных параметров для внешних свойств

Spring Boot связывает внешние свойства из application.properties (или файлов YAML и других мест) с приложением во время выполнения. Не существует (и технически не может быть) исчерпывающего списка всех поддерживаемых свойств в одном месте, так как вклады могут поступать из дополнительных JAR-файлов в вашем классе.

Запущенное приложение с функциями Actuator имеет конечную точку configprops, которая отображает все связанные и связываемые свойства, доступные через @ConfigurationProperties.

В приложении есть пример application.properties со списком наиболее распространённых свойств, поддерживаемых Spring Boot. Окончательный список получается путем поиска в исходном коде аннотаций @ConfigurationProperties и @Value, а также при использовании Binder. Более подробную информацию о точном порядке загрузки свойств можно найти в «features.html».

3. Встроенные веб-серверы

Каждый веб-приложение Spring Boot включает встроенный веб-сервер. Эта функция порождает ряд вопросов по использованию, включая изменение встроенного сервера и его настройку. Этот раздел отвечает на эти вопросы.

3.1. Использование другого веб-сервера

Многие стартеры Spring Boot включают встроенные контейнеры по умолчанию.

  • Для приложений на основе servlet-стека, spring-boot-starter-web включает Tomcat, включая spring-boot-starter-tomcat, но вы можете использовать spring-boot-starter-jetty или spring-boot-starter-undertow вместо этого.

  • Для приложений на основе реактивного стека, spring-boot-starter-webflux включает Reactor Netty, включая spring-boot-starter-reactor-netty, но вы можете использовать spring-boot-starter-tomcat, spring-boot-starter-jetty или spring-boot-starter-undertow вместо этого.

При переключении на другой HTTP-сервер вам необходимо заменить зависимости по умолчанию на те, которые вам нужны. Для помощи в этом процессе Spring Boot предоставляет отдельный стартер для каждого поддерживаемого HTTP-сервера.

Следующий пример на Maven показывает, как исключить Tomcat и включить Jetty для Spring MVC:

<properties>
    <servlet-api.version>3.1.0</servlet-api.version>
</properties>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <!-- Exclude the Tomcat dependency -->
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
Версия API servlet была переопределена, так как, в отличие от Tomcat 9 и Undertow 2, Jetty 9.4 не поддерживает servlet 4.0.

Если вы хотите использовать Jetty 10, который поддерживает servlet 4.0, вы можете сделать это, как показано в следующем примере:

<properties>
    <jetty.version>10.0.8</jetty.version>
</properties>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <!-- Exclude the Tomcat dependency -->
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
    <exclusions>
        <!-- Exclude the Jetty-9 specific dependencies -->
        <exclusion>
            <groupId>org.eclipse.jetty.websocket</groupId>
            <artifactId>websocket-server</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.eclipse.jetty.websocket</groupId>
            <artifactId>javax-websocket-server-impl</artifactId>
        </exclusion>
    </exclusions>
</dependency>

Обратите внимание, что вместе с исключением стартера Tomcat, необходимо также исключить несколько зависимостей, специфичных для Jetty9.

Следующий пример на Gradle настраивает необходимые зависимости и замену модулей, чтобы использовать Undertow вместо Reactor Netty для Spring WebFlux:

dependencies {
    implementation "org.springframework.boot:spring-boot-starter-undertow"
    implementation "org.springframework.boot:spring-boot-starter-webflux"
    modules {
        module("org.springframework.boot:spring-boot-starter-reactor-netty") {
            replacedBy("org.springframework.boot:spring-boot-starter-undertow", "Use Undertow instead of Reactor Netty")
        }
    }
}
spring-boot-starter-reactor-netty требуется для использования класса WebClient, поэтому вам может потребоваться сохранить зависимость от Netty даже тогда, когда вам нужно включить другой HTTP-сервер.

3.2. Отключение веб-сервера

Если в вашем классе пути содержатся необходимые компоненты для запуска веб-сервера, Spring Boot автоматически запустит его. Для отключения этого поведения настройте WebApplicationType в вашем application.properties, как показано в следующем примере:

Свойства
spring.main.web-application-type=none
Yaml
spring:
  main:
    web-application-type: "none"

3.3. Изменение порта HTTP

В автономном приложении основной порт HTTP по умолчанию равен 8080, но может быть задан с помощью server.port (например, в application.properties или в качестве системной переменной). Благодаря ослабленному привязыванию значений Environment, вы также можете использовать SERVER_PORT (например, как переменную среды ОС).

Чтобы полностью отключить HTTP-пункты доступа, но все равно создать WebApplicationContext, используйте server.port=-1 (иногда это полезно для тестирования).

Для получения более подробной информации см. «web.html» в разделе «Функции Spring Boot» или исходный код ServerProperties.

3.4. Использование случайного неназначенного порта HTTP

Чтобы искать свободный порт (используя системные средства ОС для предотвращения конфликтов), используйте server.port=0.

3.5. Обнаружение порта HTTP во время выполнения

Вы можете получить доступ к порту, на котором работает сервер, из логов или из WebServerApplicationContext через его WebServer. Лучший способ получить его и убедиться, что он был инициализирован, — добавить @Bean типа ApplicationListener<WebServerInitializedEvent> и извлечь контейнер из события при его публикации.

Тесты, которые используют @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT), также могут ввести фактический порт в поле с помощью аннотации @LocalServerPort, как показано в следующем примере:

Java
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.server.LocalServerPort;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyWebIntegrationTests {

    @LocalServerPort
    int port;

    // ...

}
Kotlin
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
import org.springframework.boot.test.web.server.LocalServerPort

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyWebIntegrationTests {

    @LocalServerPort
    var port = 0

    // ...

}

@LocalServerPort — мета-аннотация для @Value("${local.server.port}"). Не пытайтесь вводить порт в обычном приложении. Как мы только что увидели, значение устанавливается только после того, как контейнер был инициализирован. В отличие от теста, обратные вызовы кода приложения обрабатываются ранее (до того, как значение фактически станет доступным).

3.6. Включение сжатия ответов HTTP

Сжатие ответов HTTP поддерживается Jetty, Tomcat, Reactor Netty и Undertow. Его можно включить в application.properties, как показано ниже:

Свойства
server.compression.enabled=true
Yaml
server:
  compression:
    enabled: true

По умолчанию ответы должны быть длиной не менее 2048 байтов для выполнения сжатия. Вы можете настроить это поведение, задав свойство server.compression.min-response-size.

По умолчанию ответы сжимаются только в том случае, если их тип содержимого соответствует одному из следующих:

  • text/html

  • text/xml

  • text/plain

  • text/css

  • text/javascript

  • application/javascript

  • application/json

  • application/xml

Вы можете настроить это поведение, задав свойство server.compression.mime-types.

3.7. Настройка SSL

SSL можно настроить декларативно, задав различные свойства server.ssl.*, обычно в application.properties или application.yaml. Следующий пример показывает настройку свойств SSL с помощью файла Java KeyStore:

Свойства
server.port=8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=secret
server.ssl.key-password=another-secret
Yaml
server:
  port: 8443
  ssl:
    key-store: "classpath:keystore.jks"
    key-store-password: "secret"
    key-password: "another-secret"

Следующий пример демонстрирует настройку свойств SSL с использованием файлов сертификата и закрытого ключа в формате PEM:

Свойства
server.port=8443
server.ssl.certificate=classpath:my-cert.crt
server.ssl.certificate-private-key=classpath:my-cert.key
server.ssl.trust-certificate=classpath:ca-cert.crt
Yaml
server:
  port: 8443
  ssl:
    certificate: "classpath:my-cert.crt"
    certificate-private-key: "classpath:my-cert.key"
    trust-certificate: "classpath:ca-cert.crt"

Альтернативно, материалы доверия SSL можно настроить в пакете SSL и применить к веб-серверу, как показано в этом примере:

Свойства
server.port=8443
server.ssl.bundle=example
Yaml
server:
  port: 8443
  ssl:
    bundle: "example"

См. Ssl для получения подробной информации обо всех поддерживаемых свойствах.

Использование такой конфигурации, как в предыдущем примере, означает, что приложение больше не поддерживает обычный HTTP-соединитель на порту 8080. Spring Boot не поддерживает настройку как HTTP-соединителя, так и HTTPS-соединителя через application.properties. Если вам нужно иметь оба, вам необходимо настроить один из них программно. Мы рекомендуем использовать application.properties для настройки HTTPS, поскольку HTTP-соединитель проще настроить программно.

3.8. Настройка HTTP/2

Вы можете включить поддержку HTTP/2 в своем приложении Spring Boot с помощью свойства конфигурации server.http2.enabled. Поддерживаются как h2 (HTTP/2 через TLS), так и h2c (HTTP/2 через TCP). Для использования h2, также необходимо включить SSL. Когда SSL не включен, будет использоваться h2c. Например, вы можете использовать h2c, когда ваше приложение работает за прокси-сервером, который выполняет завершение TLS.

3.8.1. HTTP/2 с Tomcat

Spring Boot по умолчанию поставляется с Tomcat 10.1.x, который поддерживает h2c и h2 без дополнительных настроек. В качестве альтернативы, вы можете использовать libtcnative для поддержки h2, если библиотека и её зависимости установлены на операционной системе хоста.

Если каталог библиотеки не доступен в пути к библиотекам JVM, его нужно сделать доступным. Это можно сделать с помощью аргумента JVM, такого как -Djava.library.path=/usr/local/opt/tomcat-native/lib. Более подробная информация в официальной документации Tomcat.

3.8.2. HTTP/2 с Jetty

Для поддержки HTTP/2 Jetty требует дополнительной зависимости org.eclipse.jetty.http2:http2-server. Для использования h2c другие зависимости не требуются. Чтобы использовать h2, вам также необходимо выбрать одну из следующих зависимостей в зависимости от вашей среды развертывания:

  • org.eclipse.jetty:jetty-alpn-java-server для использования встроенной поддержки JDK

  • org.eclipse.jetty:jetty-alpn-conscrypt-server и библиотеку Conscrypt

3.8.3. HTTP/2 с Reactor Netty

По умолчанию spring-boot-webflux-starter использует Reactor Netty в качестве сервера. Reactor Netty поддерживает h2c и h2 без дополнительных настроек. Для оптимальной производительности сервер также поддерживает h2 с нативными библиотеками. Для этого ваше приложение должно иметь дополнительную зависимость.

Spring Boot управляет версией io.netty:netty-tcnative-boringssl-static «uber jar», содержащей нативные библиотеки для всех платформ. Разработчики могут выбрать импорт только необходимых зависимостей с помощью классификатора (см. официальную документацию Netty).

3.8.4. HTTP/2 с Undertow

Undertow поддерживает h2c и h2 по умолчанию.

3.9. Настройка веб-сервера

В целом, вы должны сначала рассмотреть использование одного из многих доступных ключей конфигурации и настроить свой веб-сервер, добавив новые записи в файл application.properties или application.yaml. См. «Поиск встроенных параметров для внешних свойств». Пространство имен server.* здесь довольно полезно, и оно включает подпространства, такие как server.tomcat.*, server.jetty.* и другие, для функций, специфичных для сервера. См. список application-properties.html.

Предыдущие разделы уже охватывали многие распространённые случаи использования, такие как сжатие, SSL или HTTP/2. Однако, если для вашего случая использования не существует ключа конфигурации, следует обратиться к WebServerFactoryCustomizer. Вы можете объявить такой компонент и получить доступ к фабрике сервера, соответствующей вашему выбору: вы должны выбрать вариант для выбранного сервера (Tomcat, Jetty, Reactor Netty, Undertow) и выбранной веб-стек (servlet или реактивный).

Следующий пример предназначен для Tomcat со стеком spring-boot-starter-web (servlet):

Java
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class MyTomcatWebServerCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        // customize the factory here
    }

}
Kotlin
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.stereotype.Component

@Component
class MyTomcatWebServerCustomizer : WebServerFactoryCustomizer<TomcatServletWebServerFactory?> {

    override fun customize(factory: TomcatServletWebServerFactory?) {
        // customize the factory here
    }

}
Spring Boot использует эту инфраструктуру внутри для автоматической настройки сервера. Автоконфигурированные WebServerFactoryCustomizer бины имеют порядок 0 и будут обработаны перед любыми пользовательскими кастомайзерами, если только не указан явный порядок.

После получения доступа к WebServerFactory с помощью кастомайзера, вы можете использовать его для настройки отдельных частей, таких как коннекторы, ресурсы сервера или сам сервер — все с использованием API, специфичных для сервера.

В дополнение Spring Boot предоставляет:

Сервер Servlet-стек Реактивный стек

Tomcat

TomcatServletWebServerFactory

TomcatReactiveWebServerFactory

Jetty

JettyServletWebServerFactory

JettyReactiveWebServerFactory

Undertow

UndertowServletWebServerFactory

UndertowReactiveWebServerFactory

Reactor

N/A

NettyReactiveWebServerFactory

В крайнем случае, вы также можете объявить свой собственный WebServerFactory бин, который переопределит бин, предоставленный Spring Boot. Когда вы это сделаете, автоконфигурированные кастомайзеры всё равно применяются к вашей пользовательской фабрике, поэтому используйте этот вариант с осторожностью.

3.10. Добавление сервлета, фильтра или слушателя в приложение

В приложении с servlet-стеком, то есть с spring-boot-starter-web, есть два способа добавить Servlet, Filter, ServletContextListener и других слушателей, поддерживаемых Servlet API, в ваше приложение:

  • Добавление сервлета, фильтра или слушателя с помощью Spring Bean

  • Добавление сервлетов, фильтров и слушателей с помощью сканирования по классовой дорожке

3.10.1. Добавление сервлета, фильтра или слушателя с помощью Spring Bean

Чтобы добавить Servlet, Filter или сервлет *Listener с помощью Spring Bean, вы должны предоставить определение @Bean для него. Это может быть полезно, когда вам нужно ввести конфигурацию или зависимости. Однако, будьте очень внимательны, чтобы они не вызывали немедленную инициализацию слишком большого количества других бинов, так как они должны быть установлены в контейнере очень рано на жизненном цикле приложения. (Например, не стоит зависеть от вашей DataSource или конфигурации JPA.) Вы можете обойти такие ограничения, инициируя бины лениво, при первом использовании, а не при инициализации.

В случае фильтров и сервлетов вы также можете добавить отображения и параметры инициализации, добавив FilterRegistrationBean или ServletRegistrationBean вместо или дополнительно к базовому компоненту.

Если для регистрации фильтра не указан dispatcherType, используется REQUEST. Это согласуется со значением по умолчанию для типа диспетчера в спецификации сервлета.

Как и любой другой Spring-бин, вы можете определить порядок бинов фильтров сервлетов; пожалуйста, убедитесь, что проверили раздел «web.html».

Отключение регистрации сервлета или фильтра

Как уже описано, все Servlet или Filter бины автоматически регистрируются в контейнере сервлетов. Чтобы отключить регистрацию определенного Filter или Servlet бина, создайте бин регистрации для него и отметьте его как отключенный, как показано в следующем примере:

Java
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyFilterConfiguration {

    @Bean
    public FilterRegistrationBean<MyFilter> registration(MyFilter filter) {
        FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(filter);
        registration.setEnabled(false);
        return registration;
    }

}
Kotlin
import org.springframework.boot.web.servlet.FilterRegistrationBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyFilterConfiguration {

    @Bean
    fun registration(filter: MyFilter): FilterRegistrationBean<MyFilter> {
        val registration = FilterRegistrationBean(filter)
        registration.isEnabled = false
        return registration
    }

}

3.10.2. Добавление сервлетов, фильтров и слушателей с помощью сканирования по классовой дорожке

Классы, аннотированные @WebServlet, @WebFilter и @WebListener, могут быть автоматически зарегистрированы в встроенном контейнере сервлетов, если аннотировать класс @Configuration с помощью @ServletComponentScan и указать пакет(ы), содержащие компоненты, которые вы хотите зарегистрировать. По умолчанию, @ServletComponentScan сканирует пакет класса с аннотацией.

3.11. Настройка ведения журнала доступа

Журналы доступа можно настроить для Tomcat, Undertow и Jetty через соответствующие пространства имён.

Например, следующие настройки ведут журнал доступа в Tomcat с пользовательским шаблоном.

Свойства
server.tomcat.basedir=my-tomcat
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%t %a %r %s (%D ms)
Yaml
server:
  tomcat:
    basedir: "my-tomcat"
    accesslog:
      enabled: true
      pattern: "%t %a %r %s (%D ms)"
По умолчанию журналы находятся в каталоге logs относительно базового каталога Tomcat. По умолчанию каталог logs является временным, поэтому возможно нужно будет изменить базовый каталог Tomcat или использовать абсолютный путь к журналам. В приведённом примере журналы доступны в my-tomcat/logs относительно рабочей директории приложения.

Журнал доступа для Undertow можно настроить аналогичным образом, как показано в следующем примере:

Свойства
server.undertow.accesslog.enabled=true
server.undertow.accesslog.pattern=%t %a %r %s (%D ms)
server.undertow.options.server.record-request-start-time=true
Yaml
server:
  undertow:
    accesslog:
      enabled: true
      pattern: "%t %a %r %s (%D ms)"
    options:
      server:
        record-request-start-time: true

Обратите внимание, что помимо включения ведения журнала доступа и настройки его шаблона, также включено регистрирование времени начала запросов. Это необходимо при включении времени ответа (%D) в шаблон журнала доступа. Журналы хранятся в каталоге logs относительно рабочей директории приложения. Вы можете настроить это расположение, установив свойство server.undertow.accesslog.dir.

Наконец, ведение журнала доступа для Jetty также можно настроить следующим образом:

Свойства
server.jetty.accesslog.enabled=true
server.jetty.accesslog.filename=/var/log/jetty-access.log
Yaml
server:
  jetty:
    accesslog:
      enabled: true
      filename: "/var/log/jetty-access.log"

По умолчанию журналы перенаправляются в System.err. Более подробную информацию см. в документации Jetty.

3.12. Работа за прокси-сервером

Если ваше приложение работает за прокси, балансировщиком нагрузки или в облаке, информация о запросе (например, хост, порт, схема…) может измениться по пути. Ваше приложение может работать на 10.10.10.10:8080, но клиенты HTTP должны видеть только example.org.

RFC7239 «Forwarded Headers» определяет заголовок HTTP Forwarded; прокси-серверы могут использовать этот заголовок для предоставления информации об исходном запросе. Вы можете настроить своё приложение для чтения этих заголовков и автоматического использования этой информации при создании ссылок и отправке их клиентам в ответах HTTP 302, JSON документах или HTML страницах. Существуют также нестандартные заголовки, такие как X-Forwarded-Host, X-Forwarded-Port, X-Forwarded-Proto, X-Forwarded-Ssl и X-Forwarded-Prefix.

Если прокси добавляет обычно используемые заголовки X-Forwarded-For и X-Forwarded-Proto, достаточно установить server.forward-headers-strategy на значение NATIVE для поддержки этих заголовков. В этом случае веб-серверы сами поддерживают эту функцию; вы можете проверить их конкретную документацию, чтобы узнать о специфическом поведении.

Если этого недостаточно, Spring Framework предоставляет ForwardedHeaderFilter. Вы можете зарегистрировать его как фильтр сервлета в своём приложении, установив server.forward-headers-strategy на значение FRAMEWORK.

Если вы используете Tomcat и завершаете SSL на прокси, server.tomcat.redirect-context-root должно быть установлено на false. Это позволяет заголовку X-Forwarded-Proto учитывать до выполнения любых перенаправлений.
Если ваше приложение выполняется в Cloud Foundry, Heroku или Kubernetes, свойство server.forward-headers-strategy по умолчанию устанавливается в NATIVE. Во всех остальных случаях оно по умолчанию устанавливается в NONE.

3.12.1. Настройка прокси-конфигурации Tomcat

Если вы используете Tomcat, вы можете дополнительно настроить имена заголовков, используемых для передачи информации о «перенаправлении», как показано в следующем примере:

Свойства
server.tomcat.remoteip.remote-ip-header=x-your-remote-ip-header
server.tomcat.remoteip.protocol-header=x-your-protocol-header
Yaml
server:
  tomcat:
    remoteip:
      remote-ip-header: "x-your-remote-ip-header"
      protocol-header: "x-your-protocol-header"

Tomcat также настроен с регулярным выражением, которое сопоставляет внутренние прокси-серверы, которым необходимо доверять. См. server.tomcat.remoteip.internal-proxies запись в приложении для её значения по умолчанию. Вы можете настроить конфигурацию клапана, добавив запись в application.properties, как показано в следующем примере:

Свойства
server.tomcat.remoteip.internal-proxies=192\\.168\\.\\d{1,3}\\.\\d{1,3}
Yaml
server:
  tomcat:
    remoteip:
      internal-proxies: "192\\.168\\.\\d{1,3}\\.\\d{1,3}"
Вы можете доверять всем прокси, установив internal-proxies в пустое значение (но не делайте этого в производстве).

Вы можете полностью контролировать конфигурацию RemoteIpValve Tomcat, отключив автоматическое управление (для этого установите server.forward-headers-strategy=NONE) и добавив новый экземпляр клапана, используя bean WebServerFactoryCustomizer.

3.13. Включение нескольких коннекторов в Tomcat

Вы можете добавить org.apache.catalina.connector.Connector в TomcatServletWebServerFactory, что позволит иметь несколько коннекторов, включая HTTP и HTTPS коннекторы, как показано в следующем примере:

Java
import org.apache.catalina.connector.Connector;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyTomcatConfiguration {

    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> connectorCustomizer() {
        return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createConnector());
    }

    private Connector createConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setPort(8081);
        return connector;
    }

}
Kotlin
import org.apache.catalina.connector.Connector
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyTomcatConfiguration {

    @Bean
    fun connectorCustomizer(): WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
        return WebServerFactoryCustomizer { tomcat: TomcatServletWebServerFactory ->
            tomcat.addAdditionalTomcatConnectors(
                createConnector()
            )
        }
    }

    private fun createConnector(): Connector {
        val connector = Connector("org.apache.coyote.http11.Http11NioProtocol")
        connector.port = 8081
        return connector
    }

}

3.14. Включение реестра MBean Tomcat

Реестр MBean встроенного Tomcat по умолчанию отключен. Это минимизирует использование памяти Tomcat. Если вы хотите использовать MBeans Tomcat, например, для того, чтобы Micrometer мог использовать их для экспонирования метрик, вы должны использовать свойство server.tomcat.mbeanregistry.enabled, как показано в следующем примере:

Свойства
server.tomcat.mbeanregistry.enabled=true
Yaml
server:
  tomcat:
    mbeanregistry:
      enabled: true

3.15. Включение нескольких слушателей в Undertow

Добавьте UndertowBuilderCustomizer в UndertowServletWebServerFactory и добавьте слушателя в Builder, как показано в следующем примере:

Java
import io.undertow.Undertow.Builder;

import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyUndertowConfiguration {

    @Bean
    public WebServerFactoryCustomizer<UndertowServletWebServerFactory> undertowListenerCustomizer() {
        return (factory) -> factory.addBuilderCustomizers(this::addHttpListener);
    }

    private Builder addHttpListener(Builder builder) {
        return builder.addHttpListener(8080, "0.0.0.0");
    }

}
Kotlin
import io.undertow.Undertow
import org.springframework.boot.web.embedded.undertow.UndertowBuilderCustomizer
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyUndertowConfiguration {

    @Bean
    fun undertowListenerCustomizer(): WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
        return WebServerFactoryCustomizer { factory: UndertowServletWebServerFactory ->
            factory.addBuilderCustomizers(
                UndertowBuilderCustomizer { builder: Undertow.Builder -> addHttpListener(builder) })
        }
    }

    private fun addHttpListener(builder: Undertow.Builder): Undertow.Builder {
        return builder.addHttpListener(8080, "0.0.0.0")
    }

}

3.16. Создание конечных точек WebSocket с помощью @ServerEndpoint

Если вы хотите использовать @ServerEndpoint в приложении Spring Boot, которое использует встроенный контейнер, вы должны объявить единственный ServerEndpointExporter @Bean, как показано в следующем примере:

Java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration(proxyBeanMethods = false)
public class MyWebSocketConfiguration {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}
Kotlin
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.socket.server.standard.ServerEndpointExporter

@Configuration(proxyBeanMethods = false)
class MyWebSocketConfiguration {

    @Bean
    fun serverEndpointExporter(): ServerEndpointExporter {
        return ServerEndpointExporter()
    }

}

Bean, показанный в предыдущем примере, регистрирует все @ServerEndpoint аннотированные бины в базовом контейнере WebSocket. При развертывании в автономном контейнере сервлетов эта роль выполняется инициализатором сервлета контейнера, и bean ServerEndpointExporter не требуется.

4. Spring MVC

Spring Boot имеет ряд стартеров, которые включают Spring MVC. Обратите внимание, что некоторые стартеры включают зависимость от Spring MVC, а не включают его напрямую. В этом разделе отвечаются на часто задаваемые вопросы о Spring MVC и Spring Boot.

4.1. Написание JSON REST-сервиса

Любой Spring @RestController в приложении Spring Boot должен отображать JSON-ответ по умолчанию, если Jackson2 находится в пути к классам, как показано в следующем примере:

Java
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @RequestMapping("/thing")
    public MyThing thing() {
        return new MyThing();
    }

}
Kotlin
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class MyController {

    @RequestMapping("/thing")
    fun thing(): MyThing {
        return MyThing()
    }

}

До тех пор, пока MyThing может быть сериализован Jackson2 (это верно для обычного POJO или объекта Groovy), тогда localhost:8080/thing по умолчанию отображает JSON-представление этого объекта. Обратите внимание, что в браузере иногда можно увидеть XML-ответы, потому что браузеры, как правило, отправляют заголовки accept, которые отдают предпочтение XML.

4.2. Написание XML REST-сервиса

Если у вас есть расширение Jackson XML (jackson-dataformat-xml) в пути к классам, вы можете использовать его для отображения XML-ответов. Предыдущий пример, который мы использовали для JSON, будет работать. Чтобы использовать рендерер Jackson XML, добавьте следующую зависимость в свой проект:

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

Если расширение XML Jackson недоступно, а JAXB доступно, XML можно отобразить с дополнительным требованием наличия MyThing, аннотированной как @XmlRootElement, как показано в следующем примере:

Java
import jakarta.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class MyThing {

    private String name;

    // getters/setters ...

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
Kotlin
import jakarta.xml.bind.annotation.XmlRootElement

@XmlRootElement
class MyThing {

    var name: String? = null

}

Вам нужно убедиться, что библиотека JAXB является частью вашего проекта, например, добавив:

<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
</dependency>
Чтобы заставить сервер отобразить XML вместо JSON, вам, возможно, придется отправить заголовок Accept: text/xml (или использовать браузер).

4.3. Настройка Jackson ObjectMapper

Spring MVC (клиентская и серверная стороны) использует HttpMessageConverters для согласования преобразования содержимого в HTTP-обмене. Если Jackson находится в пути к классам, вы уже получаете преобразователь(и) по умолчанию, предоставляемые Jackson2ObjectMapperBuilder, экземпляр которого настроен для вас автоматически.

Экземпляр ObjectMapper (или XmlMapper для преобразователя Jackson XML) (созданный по умолчанию) имеет следующие настроенные свойства:

  • MapperFeature.DEFAULT_VIEW_INCLUSION отключен

  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES отключен

  • SerializationFeature.WRITE_DATES_AS_TIMESTAMPS отключен

Spring Boot также имеет некоторые функции, которые облегчают настройку этого поведения.

Вы можете настроить экземпляры ObjectMapper и XmlMapper, используя среду. Jackson предоставляет обширную suite функций включения/выключения, которые можно использовать для настройки различных аспектов обработки. Эти функции описаны в шести перечислениях (в Jackson), которые сопоставляются со свойствами в среде:

Перечисление Свойство Значения

com.fasterxml.jackson.databind.DeserializationFeature

spring.jackson.deserialization.<feature_name>

true, false

com.fasterxml.jackson.core.JsonGenerator.Feature

spring.jackson.generator.<feature_name>

true, false

com.fasterxml.jackson.databind.MapperFeature

spring.jackson.mapper.<feature_name>

true, false

com.fasterxml.jackson.core.JsonParser.Feature

spring.jackson.parser.<feature_name>

true, false

com.fasterxml.jackson.databind.SerializationFeature

spring.jackson.serialization.<feature_name>

true, false

com.fasterxml.jackson.annotation.JsonInclude.Include

spring.jackson.default-property-inclusion

always, non_null, non_absent, non_default, non_empty

Например, чтобы включить красивую печать, установите spring.jackson.serialization.indent_output=true. Обратите внимание, что благодаря использованию расслабленной привязки, регистр indent_output не обязательно должен совпадать с регистром соответствующего константы перечисления, который есть INDENT_OUTPUT.

Эта настройка на основе среды применяется к автоматически настроенному бину Jackson2ObjectMapperBuilder и применяется к любым мапперам, созданным с использованием конструктора, включая автоматически настроенный ObjectMapper.

Контекст’s Jackson2ObjectMapperBuilder можно настроить с помощью одного или нескольких Jackson2ObjectMapperBuilderCustomizer бинов. Такие пользовательские бины-настройщики могут быть отсортированы (собственный настройщик Boot имеет порядок 0), что позволяет применять дополнительную настройку как до, так и после настройки Boot.

Любые бины типа com.fasterxml.jackson.databind.Module автоматически регистрируются с автоматически настроенным Jackson2ObjectMapperBuilder и применяются ко всем экземплярам ObjectMapper, которые он создает. Это обеспечивает глобальный механизм для внесения пользовательских модулей при добавлении новых функций в ваше приложение.

Если вы хотите полностью заменить стандартный ObjectMapper, либо определите @Bean этого типа и отметьте его как @Primary, или, если вы предпочитаете подход на основе конструктора, определите Jackson2ObjectMapperBuilder @Bean. Обратите внимание, что в любом случае это отключит всю автоматическую настройку ObjectMapper.

Если вы предоставите любые @Beans типа MappingJackson2HttpMessageConverter, они заменят значение по умолчанию в настройке MVC. Также предоставлен удобный бин типа HttpMessageConverters (он всегда доступен, если вы используете стандартную конфигурацию MVC). Он имеет несколько полезных методов для доступа к стандартным и пользовательским улучшенным преобразователям сообщений.

См. раздел «Настройка отображения @ResponseBody» и исходный код WebMvcAutoConfiguration для получения более подробной информации.

4.4. Настройка отображения @ResponseBody

Spring использует HttpMessageConverters для отображения @ResponseBody (или ответов от @RestController). Вы можете внести дополнительные преобразователи, добавив бины соответствующего типа в контекст Spring Boot. Если добавленный вами бин имеет тип, который должен был быть включён по умолчанию (например, MappingJackson2HttpMessageConverter для преобразований JSON), он заменит значение по умолчанию. Предоставлен удобный бин типа HttpMessageConverters, который всегда доступен, если вы используете стандартную конфигурацию MVC. Он имеет несколько полезных методов для доступа к стандартным и пользовательским улучшенным преобразователям сообщений (например, это может быть полезно, если вы хотите вручную ввести их в пользовательский RestTemplate).

Как и в обычном использовании MVC, любые WebMvcConfigurer бины, которые вы предоставите, также могут внести преобразователи, перезаписав метод configureMessageConverters. Однако, в отличие от обычного MVC, вы можете предоставить только дополнительные преобразователи, которые вам нужны (потому что Spring Boot использует тот же механизм для предоставления своих значений по умолчанию). Наконец, если вы откажетесь от стандартной конфигурации Spring Boot MVC, предоставив свою собственную конфигурацию @EnableWebMvc, вы можете полностью взять под контроль и сделать всё вручную, используя getMessageConverters из WebMvcConfigurationSupport.

См. исходный код WebMvcAutoConfiguration для получения более подробной информации.

4.5. Обработка загрузок файлов с множественной частью

Spring Boot использует API сервлета 5 jakarta.servlet.http.Part для поддержки загрузки файлов. По умолчанию Spring Boot настраивает Spring MVC с максимальным размером 1 МБ на файл и максимальным размером данных файлов в 10 МБ в одном запросе. Вы можете переопределить эти значения, местоположение, в котором хранятся промежуточные данные (например, в каталог /tmp), и порог, после которого данные выгружаются на диск, используя свойства, доступные в классе MultipartProperties. Например, если вы хотите указать, что файлы должны быть без ограничений, установите свойство spring.servlet.multipart.max-file-size в значение -1.

Поддержка множественной части полезна, когда вы хотите получить данные файла с кодировкой множественной части в качестве параметра, аннотированного @RequestParam, типа MultipartFile, в методе обработчика контроллера Spring MVC.

См. исходный код MultipartAutoConfiguration для получения более подробной информации.

Рекомендуется использовать встроенную поддержку контейнера для загрузки файлов с множественной частью, а не добавлять дополнительную зависимость, такую как Apache Commons File Upload.

4.6. Отключение DispatcherServlet Spring MVC

По умолчанию все содержимое отображается с корня вашего приложения (/). Если вы хотите сопоставить с другим путем, вы можете настроить его следующим образом:

Свойства
spring.mvc.servlet.path=/mypath
Yaml
spring:
  mvc:
    servlet:
      path: "/mypath"

Если у вас есть дополнительные сервлеты, вы можете объявить @Bean типа Servlet или ServletRegistrationBean для каждого, и Spring Boot прозрачно зарегистрирует их в контейнере. Поскольку сервлеты регистрируются таким образом, они могут быть сопоставлены с подконтекстом DispatcherServlet без вызова его.

Настройка DispatcherServlet самостоятельно — необычно, но если вам действительно нужно это сделать, необходимо также предоставить @Bean типа DispatcherServletPath для указания пути к вашему пользовательскому DispatcherServlet.

4.7. Отключение стандартной конфигурации MVC

Самый простой способ полностью контролировать конфигурацию MVC — предоставить собственную @Configuration с аннотацией @EnableWebMvc. Это позволит вам полностью контролировать конфигурацию MVC.

4.8. Настройка ViewResolvers

ViewResolver — это ключевой компонент Spring MVC, переводящий имена представлений в @Controller в фактические реализации View. Обратите внимание, что ViewResolvers в основном используются в приложениях с пользовательским интерфейсом, а не в сервисах в стиле REST (View не используется для рендеринга @ResponseBody). Существует множество реализаций ViewResolver на выбор, и Spring не навязывает предпочтений. Spring Boot, в свою очередь, устанавливает одну или две по умолчанию, в зависимости от того, что найдено в классе и в контексте приложения. DispatcherServlet использует все найденные в приложении разрешители, пробуя каждый по очереди до получения результата. Если вы добавите собственный разрешитель, вам нужно учитывать порядок его добавления.

WebMvcAutoConfiguration добавляет следующие ViewResolvers в ваш контекст:

  • InternalResourceViewResolver с именем ‘defaultViewResolver’. Этот разрешитель находит физические ресурсы, которые могут быть отображены с использованием DefaultServlet (включая статические ресурсы и страницы JSP, если вы их используете). Он применяет префикс и суффикс к имени представления и затем ищет физический ресурс с этим путем в контексте сервлета (значения по умолчанию пустые, но доступны для внешней конфигурации через spring.mvc.view.prefix и spring.mvc.view.suffix). Вы можете переопределить его, предоставив бины того же типа.

  • BeanNameViewResolver с именем ‘beanNameViewResolver’. Это полезный член цепочки разрешителей представлений, выбирающий любые бины с тем же именем, что и разрешаемое View. Не должно потребоваться переопределение или замена этого разрешителя.

  • ContentNegotiatingViewResolver с именем ‘viewResolver’ добавляется только в том случае, если фактически присутствуют бины типа View. Это составной разрешитель, делегирующий всем остальным и пытающийся найти соответствие переданному клиентом HTTP-заголовку «Accept». Существует полезная статья о ContentNegotiatingViewResolver, которую вы можете изучить для получения дополнительной информации, а также исходный код для подробного изучения. Вы можете отключить автоматически настроенный ContentNegotiatingViewResolver, определив бины с именем ‘viewResolver’.

  • Если вы используете Thymeleaf, у вас также есть ThymeleafViewResolver с именем ‘thymeleafViewResolver’. Он ищет ресурсы, окружая имя представления префиксом и суффиксом. Префикс — spring.thymeleaf.prefix, а суффикс — spring.thymeleaf.suffix. Значения префикса и суффикса по умолчанию равны ‘classpath:/templates/’ и ‘.html’ соответственно. Вы можете переопределить ThymeleafViewResolver, предоставив бины с таким же именем.

  • Если вы используете FreeMarker, у вас также есть FreeMarkerViewResolver с именем ‘freeMarkerViewResolver’. Он ищет ресурсы в пути загрузчика (который внешне указан в spring.freemarker.templateLoaderPath и имеет значение по умолчанию ‘classpath:/templates/’), окружая имя представления префиксом и суффиксом. Префикс внешне задан в spring.freemarker.prefix, а суффикс — в spring.freemarker.suffix. Значения префикса и суффикса по умолчанию пустые и ‘.ftlh’ соответственно. Вы можете переопределить FreeMarkerViewResolver, предоставив бины с таким же именем.

  • Если вы используете шаблоны Groovy (на самом деле, если groovy-templates находится в вашем классе), у вас также есть GroovyMarkupViewResolver с именем ‘groovyMarkupViewResolver’. Он ищет ресурсы в пути загрузчика, окружая имя представления префиксом и суффиксом (внешне указанными в spring.groovy.template.prefix и spring.groovy.template.suffix). Префикс и суффикс имеют значения по умолчанию ‘classpath:/templates/’ и ‘.tpl’ соответственно. Вы можете переопределить GroovyMarkupViewResolver, предоставив бины с таким же именем.

  • Если вы используете Mustache, у вас также есть MustacheViewResolver с именем ‘mustacheViewResolver’. Он ищет ресурсы, окружая имя представления префиксом и суффиксом. Префикс — spring.mustache.prefix, а суффикс — spring.mustache.suffix. Значения префикса и суффикса по умолчанию равны ‘classpath:/templates/’ и ‘.mustache’ соответственно. Вы можете переопределить MustacheViewResolver, предоставив бины с таким же именем.

Для получения дополнительной информации ознакомьтесь со следующими разделами:

  • WebMvcAutoConfiguration

  • ThymeleafAutoConfiguration

  • FreeMarkerAutoConfiguration

  • GroovyTemplateAutoConfiguration

5. Jersey

5.1. Защита конечных точек Jersey с помощью Spring Security

Spring Security может использоваться для защиты веб-приложения, основанного на Jersey, аналогично тому, как это делается для веб-приложения на основе Spring MVC. Однако, если вы хотите использовать уровень безопасности Spring Security на уровне методов с Jersey, вам необходимо настроить Jersey на использование setStatus(int), а не sendError(int). Это предотвращает Jersey от завершения ответа до того, как Spring Security получит возможность сообщить клиенту об ошибках аутентификации или авторизации.

Свойство jersey.config.server.response.setStatusOverSendError должно быть установлено в значение true в бине приложения ResourceConfig, как показано в следующем примере:

import java.util.Collections;

import org.glassfish.jersey.server.ResourceConfig;

import org.springframework.stereotype.Component;

@Component
public class JerseySetStatusOverSendErrorConfig extends ResourceConfig {

    public JerseySetStatusOverSendErrorConfig() {
        register(Endpoint.class);
        setProperties(Collections.singletonMap("jersey.config.server.response.setStatusOverSendError", true));
    }

}

5.2. Использование Jersey вместе с другой веб-фреймворком

Чтобы использовать Jersey вместе с другим веб-фреймворком, таким как Spring MVC, его следует настроить таким образом, чтобы он позволял другому фреймворку обрабатывать запросы, которые он не может обработать. Во-первых, настройте Jersey на использование фильтра, а не сервлета, настроив свойство приложения spring.jersey.type со значением filter. Во-вторых, настройте ваш ResourceConfig на перенаправление запросов, которые привели бы к ошибке 404, как показано в следующем примере.

import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletProperties;

import org.springframework.stereotype.Component;

@Component
public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        register(Endpoint.class);
        property(ServletProperties.FILTER_FORWARD_ON_404, true);
    }

}

6. Клиенты HTTP

Spring Boot предлагает ряд стартеров, работающих с клиентами HTTP. В этом разделе рассматриваются вопросы, связанные с их использованием.

6.1. Настройка RestTemplate для использования прокси

Как описано в io.html, вы можете использовать RestTemplateCustomizer с RestTemplateBuilder для построения настроенного RestTemplate. Это рекомендуемый подход для создания RestTemplate, настроенного для использования прокси.

Точные детали настройки прокси зависят от используемого базового фабрики запросов клиента.

6.2. Настройка TcpClient, используемого WebClient на основе Reactor Netty

Когда Reactor Netty присутствует в classpath, настраивается WebClient на основе Reactor Netty. Для настройки обработки сетевых соединений клиентом, укажите бин ClientHttpConnector. Следующий пример настраивает таймаут подключения в 60 секунд и добавляет ReadTimeoutHandler:

Java
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import reactor.netty.http.client.HttpClient;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.client.reactive.ReactorResourceFactory;

@Configuration(proxyBeanMethods = false)
public class MyReactorNettyClientConfiguration {

    @Bean
    ClientHttpConnector clientHttpConnector(ReactorResourceFactory resourceFactory) {
        HttpClient httpClient = HttpClient.create(resourceFactory.getConnectionProvider())
                .runOn(resourceFactory.getLoopResources())
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 60000)
                .doOnConnected((connection) -> connection.addHandlerLast(new ReadTimeoutHandler(60)));
        return new ReactorClientHttpConnector(httpClient);
    }

}
Kotlin
import io.netty.channel.ChannelOption
import io.netty.handler.timeout.ReadTimeoutHandler
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.client.reactive.ClientHttpConnector
import org.springframework.http.client.reactive.ReactorClientHttpConnector
import org.springframework.http.client.reactive.ReactorResourceFactory
import reactor.netty.http.client.HttpClient

@Configuration(proxyBeanMethods = false)
class MyReactorNettyClientConfiguration {

    @Bean
    fun clientHttpConnector(resourceFactory: ReactorResourceFactory): ClientHttpConnector {
        val httpClient = HttpClient.create(resourceFactory.connectionProvider)
            .runOn(resourceFactory.loopResources)
            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 60000)
            .doOnConnected { connection ->
                connection.addHandlerLast(ReadTimeoutHandler(60))
            }
        return ReactorClientHttpConnector(httpClient)
    }

}
Обратите внимание на использование ReactorResourceFactory для поставщика подключений и ресурсов цикла событий. Это обеспечивает эффективное совместное использование ресурсов для сервера, принимающего запросы, и клиента, отправляющего запросы.

7. Ведение журнала

В Spring Boot нет обязательной зависимости для ведения журнала, за исключением API Commons Logging, который обычно предоставляется модулем spring-jcl Spring Framework. Для использования Logback необходимо включить его и spring-jcl в пути к классам. Рекомендуемый способ сделать это — через стартеры, которые все зависят от spring-boot-starter-logging. Для веб-приложения вам нужен только spring-boot-starter-web, так как он транзитивно зависит от стартера ведения журнала. Если вы используете Maven, следующая зависимость добавит для вас ведение журнала:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Spring Boot имеет абстракцию LoggingSystem, которая пытается настроить ведение журнала на основе содержимого пути к классам. Если Logback доступен, он используется в первую очередь.

Если единственное изменение, которое вам нужно внести в ведение журнала, — это установить уровни различных логгеров, вы можете сделать это в application.properties, используя префикс «logging.level», как показано в следующем примере:

Свойства
logging.level.org.springframework.web=debug
logging.level.org.hibernate=error
Yaml
logging:
  level:
    org.springframework.web: "debug"
    org.hibernate: "error"

Вы также можете установить расположение файла, в который будет записываться журнал (в дополнение к консоли), используя logging.file.name.

Для настройки более подробных параметров системы ведения журнала необходимо использовать собственный формат конфигурации, поддерживаемый LoggingSystem. По умолчанию Spring Boot подбирает собственную конфигурацию из своего расположения по умолчанию для системы (например, classpath:logback.xml для Logback), но вы можете установить расположение файла конфигурации, используя свойство logging.config.

7.1. Настройка Logback для ведения журнала

Если вам необходимо внести настройки в Logback, выходящие за рамки тех, которые можно достичь с помощью application.properties, вам необходимо добавить стандартный файл конфигурации logback. Вы можете добавить файл logback.xml в корень вашего пути к классам, чтобы Logback его нашел. Вы также можете использовать logback-spring.xml, если хотите использовать расширения Spring Boot Logback.

Документация Logback содержит специальный раздел, подробно описывающий конфигурацию.

Spring Boot предоставляет ряд конфигураций Logback, которые могут быть included в вашей собственной конфигурации. Эти конфигурации предназначены для повторного применения некоторых общих соглашений Spring Boot.

Следующие файлы предоставляются в org/springframework/boot/logging/logback/:

  • defaults.xml — предоставляет правила преобразования, свойства шаблонов и общие конфигурации логгеров.

  • console-appender.xml — добавляет ConsoleAppender, используя CONSOLE_LOG_PATTERN.

  • file-appender.xml — добавляет RollingFileAppender, используя FILE_LOG_PATTERN и ROLLING_FILE_NAME_PATTERN с соответствующими настройками.

Кроме того, предоставляется устаревший файл base.xml для совместимости со старыми версиями Spring Boot.

Типичный настраиваемый файл logback.xml будет выглядеть примерно так:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>
    <logger name="org.springframework.web" level="DEBUG"/>
</configuration>

Ваш файл конфигурации logback также может использовать системные свойства, которые LoggingSystem создает для вас:

  • ${PID}: Текущий идентификатор процесса.

  • ${LOG_FILE}: Был ли установлен logging.file.name во внешней конфигурации Boot.

  • ${LOG_PATH}: Был ли установлен logging.file.path (представляющий каталог для хранения файлов логов) во внешней конфигурации Boot.

  • ${LOG_EXCEPTION_CONVERSION_WORD}: Был ли установлен logging.exception-conversion-word во внешней конфигурации Boot.

  • ${ROLLING_FILE_NAME_PATTERN}: Был ли установлен logging.pattern.rolling-file-name во внешней конфигурации Boot.

Spring Boot также предоставляет приятный вывод ANSI-цвета в терминале консоли (но не в файле журнала) с помощью пользовательского преобразователя Logback. См. CONSOLE_LOG_PATTERN в defaults.xml конфигурации для примера.

Если Groovy находится в пути к классам, вы должны иметь возможность настроить Logback с помощью logback.groovy также. Если эта настройка присутствует, ей отдаётся предпочтение.

Расширения Spring не поддерживаются с конфигурацией Groovy. Любые файлы logback-spring.groovy не будут обнаружены.

7.1.1. Настройка Logback для вывода только в файл

Если вы хотите отключить вывод в консоль и записывать вывод только в файл, вам нужна настраиваемая logback-spring.xml, которая импортирует file-appender.xml, но не console-appender.xml, как показано в следующем примере:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>
    <include resource="org/springframework/boot/logging/logback/file-appender.xml" />
    <root level="INFO">
        <appender-ref ref="FILE" />
    </root>
</configuration>

Вам также нужно добавить logging.file.name в вашу application.properties или application.yaml, как показано в следующем примере:

Свойства
logging.file.name=myapplication.log
Yaml
logging:
  file:
    name: "myapplication.log"

7.2. Настройка Log4j для ведения журнала

Spring Boot поддерживает Log4j 2 для конфигурации ведения журнала, если он находится в пути к классам. Если вы используете стартеры для сборки зависимостей, вам нужно исключить Logback и затем включить Log4j 2 вместо него. Если вы не используете стартеры, вам необходимо предоставить (по крайней мере) spring-jcl в дополнение к Log4j 2.

Рекомендуемый путь — через стартеры, хотя это требует некоторых манипуляций. Следующий пример показывает, как настроить стартеры в Maven:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

Gradle предоставляет несколько способов настройки стартеров. Один способ — использовать замену модулей. Для этого объявите зависимость от стартера Log4j 2 и сообщите Gradle, что все случаи стандартного стартера ведения журнала должны быть заменены стартером Log4j 2, как показано в следующем примере:

dependencies {
    implementation "org.springframework.boot:spring-boot-starter-log4j2"
    modules {
        module("org.springframework.boot:spring-boot-starter-logging") {
            replacedBy("org.springframework.boot:spring-boot-starter-log4j2", "Use Log4j2 instead of Logback")
        }
    }
}
Стартеры Log4j собирают зависимости для общих требований к ведению журнала (например, для того, чтобы Tomcat использовал java.util.logging, но настраивал вывод с помощью Log4j 2).
Чтобы убедиться, что отладочный вывод ведения журнала, выполняемый с использованием java.util.logging, направляется в Log4j 2, настройте его адаптер JDK-логирования, установив системное свойство java.util.logging.manager в значение org.apache.logging.log4j.jul.LogManager.

7.2.1. Использование YAML или JSON для конфигурации Log4j 2

Помимо стандартного формата конфигурации XML, Log4j 2 также поддерживает форматы YAML и JSON для файлов конфигурации. Для настройки Log4j 2 на использование альтернативного формата файла конфигурации добавьте соответствующие зависимости в путь к классам и назовите ваши файлы конфигурации в соответствии с выбранным форматом, как показано в следующем примере:

Формат Зависимости Имена файлов

YAML

com.fasterxml.jackson.core:jackson-databind + com.fasterxml.jackson.dataformat:jackson-dataformat-yaml

log4j2.yaml + log4j2.yml

JSON

com.fasterxml.jackson.core:jackson-databind

log4j2.json + log4j2.jsn

7.2.2. Использование составной конфигурации для настройки Log4j 2

Log4j 2 поддерживает объединение нескольких файлов конфигурации в единую составную конфигурацию. Для использования этой поддержки в Spring Boot настройте logging.log4j2.config.override с расположениями одного или нескольких дополнительных файлов конфигурации. Дополнительные файлы конфигурации будут объединены с основной конфигурацией, независимо от того, является ли источник основной конфигурации параметрами по умолчанию Spring Boot, стандартным расположением, например log4j.xml, или расположением, настроенным свойством logging.config.

8. Доступ к данным

Spring Boot включает ряд стартеров для работы со источниками данных. В этом разделе рассматриваются вопросы, связанные с этим.

8.1. Настройка пользовательского источника данных

Для настройки собственного DataSource, определите @Bean такого типа в своей конфигурации. Spring Boot повторно использует ваш DataSource везде, где он требуется, включая инициализацию базы данных. Если вам нужно внешне задать некоторые параметры, вы можете привязать свой DataSource к среде (см. «features.html»).

Следующий пример показывает, как определить источник данных в компоненте:

Java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "app.datasource")
    public SomeDataSource dataSource() {
        return new SomeDataSource();
    }

}
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "app.datasource")
    fun dataSource(): SomeDataSource {
        return SomeDataSource()
    }

}

Следующий пример показывает, как определить источник данных, задав свойства:

Properties
app.datasource.url=jdbc:h2:mem:mydb
app.datasource.username=sa
app.datasource.pool-size=30
Yaml
app:
  datasource:
    url: "jdbc:h2:mem:mydb"
    username: "sa"
    pool-size: 30

Предполагая, что SomeDataSource имеет обычные свойства JavaBean для URL, имени пользователя и размера пула, эти настройки автоматически привязываются перед тем, как DataSource станет доступным для других компонентов.

Spring Boot также предоставляет утилитарный класс-билдер, называемый DataSourceBuilder, который можно использовать для создания одного из стандартных источников данных (если он находится в классе). Библиотека может определить, какой использовать, исходя из того, что доступно в классе. Она также автоматически обнаруживает драйвер на основе JDBC URL.

Следующий пример показывает, как создать источник данных, используя DataSourceBuilder:

Java
import javax.sql.DataSource;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {

    @Bean
    @ConfigurationProperties("app.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }

}
Kotlin
import javax.sql.DataSource

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {

    @Bean
    @ConfigurationProperties("app.datasource")
    fun dataSource(): DataSource {
        return DataSourceBuilder.create().build()
    }

}

Для запуска приложения с этим DataSource, вам нужны только данные подключения. Также можно указать параметры, специфичные для пула. Для получения дополнительной информации см. реализацию, используемую во время выполнения.

Следующий пример показывает, как определить источник данных JDBC, задав свойства:

Properties
app.datasource.url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30
Yaml
app:
  datasource:
    url: "jdbc:mysql://localhost/test"
    username: "dbuser"
    password: "dbpass"
    pool-size: 30

Однако есть подвох. Поскольку фактический тип пула подключений не показан, никакие ключи не генерируются в метаданных для вашего пользовательского DataSource, и нет возможности завершения в вашем IDE (поскольку интерфейс DataSource не содержит свойств). Кроме того, если у вас есть Hikari в классе, эта базовая настройка не работает, поскольку у Hikari нет свойства url (но есть свойство jdbcUrl). В этом случае необходимо переписать конфигурацию следующим образом:

Properties
app.datasource.jdbc-url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30
Yaml
app:
  datasource:
    jdbc-url: "jdbc:mysql://localhost/test"
    username: "dbuser"
    password: "dbpass"
    pool-size: 30

Вы можете исправить это, принудительно заставив пул подключений использовать и возвращать специальную реализацию, а не DataSource. Вы не можете изменить реализацию во время выполнения, но список вариантов будет явным.

Следующий пример показывает, как создать HikariDataSource с DataSourceBuilder:

Java
import com.zaxxer.hikari.HikariDataSource;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {

    @Bean
    @ConfigurationProperties("app.datasource")
    public HikariDataSource dataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

}
Kotlin
import com.zaxxer.hikari.HikariDataSource
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {

    @Bean
    @ConfigurationProperties("app.datasource")
    fun dataSource(): HikariDataSource {
        return DataSourceBuilder.create().type(HikariDataSource::class.java).build()
    }

}

Вы даже можете пойти дальше, используя то, что делает DataSourceProperties за вас — то есть, предоставив по умолчанию встроенную базу данных с разумным именем пользователя и паролем, если URL не предоставлен. Вы можете легко инициализировать DataSourceBuilder из состояния любого объекта DataSourceProperties, поэтому вы также можете ввести DataSource, который Spring Boot создаёт автоматически. Однако это разделит вашу конфигурацию на два пространства имён: url, username, password, type и driver в spring.datasource, а остальное — в вашем пользовательском пространстве имён (app.datasource). Чтобы избежать этого, вы можете переопределить пользовательский DataSourceProperties в вашем пользовательском пространстве имён, как показано в следующем примере:

Java
import com.zaxxer.hikari.HikariDataSource;

import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource")
    public DataSourceProperties dataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @ConfigurationProperties("app.datasource.configuration")
    public HikariDataSource dataSource(DataSourceProperties properties) {
        return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

}
Kotlin
import com.zaxxer.hikari.HikariDataSource
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary

@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource")
    fun dataSourceProperties(): DataSourceProperties {
        return DataSourceProperties()
    }

    @Bean
    @ConfigurationProperties("app.datasource.configuration")
    fun dataSource(properties: DataSourceProperties): HikariDataSource {
        return properties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
    }

}

Эта настройка синхронизирует вас с тем, что Spring Boot делает по умолчанию, за исключением того, что выбран специальный пул подключений (в коде), и его настройки отображаются в подпространстве app.datasource.configuration. Поскольку DataSourceProperties заботится о переводе url/jdbcUrl за вас, вы можете настроить его следующим образом:

Properties
app.datasource.url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.configuration.maximum-pool-size=30
Yaml
app:
  datasource:
    url: "jdbc:mysql://localhost/test"
    username: "dbuser"
    password: "dbpass"
    configuration:
      maximum-pool-size: 30
Spring Boot предоставит настройки, специфичные для Hikari, для spring.datasource.hikari. Этот пример использует более общее подпространство configuration, так как пример не поддерживает несколько реализаций источника данных.
Поскольку ваша пользовательская конфигурация выбрала Hikari, app.datasource.type не имеет эффекта. На практике билдер инициализируется любым значением, которое вы можете установить, а затем переопределяется вызовом .type().

См. «data.html» в разделе «Функции Spring Boot» и класс DataSourceAutoConfiguration для получения дополнительной информации.

8.2. Настройка двух источников данных

Если вам нужно настроить несколько источников данных, вы можете применить те же приёмы, что описаны в предыдущем разделе. Однако вы должны пометить один из DataSource экземпляров как @Primary, потому что различные автоконфигурации в будущем ожидают получить один по типу.

Если вы создаёте собственный DataSource, автоконфигурация откатывается. В следующем примере мы предоставляем точно тот же набор функций, что и автоконфигурация для основного источника данных:

Java
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;

import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration(proxyBeanMethods = false)
public class MyDataSourcesConfiguration {

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource.first")
    public DataSourceProperties firstDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource.first.configuration")
    public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) {
        return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Bean
    @ConfigurationProperties("app.datasource.second")
    public BasicDataSource secondDataSource() {
        return DataSourceBuilder.create().type(BasicDataSource.class).build();
    }

}
Kotlin
import com.zaxxer.hikari.HikariDataSource
import org.apache.commons.dbcp2.BasicDataSource
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary

@Configuration(proxyBeanMethods = false)
class MyDataSourcesConfiguration {

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource.first")
    fun firstDataSourceProperties(): DataSourceProperties {
        return DataSourceProperties()
    }

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource.first.configuration")
    fun firstDataSource(firstDataSourceProperties: DataSourceProperties): HikariDataSource {
        return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
    }

    @Bean
    @ConfigurationProperties("app.datasource.second")
    fun secondDataSource(): BasicDataSource {
        return DataSourceBuilder.create().type(BasicDataSource::class.java).build()
    }

}
firstDataSourceProperties должен быть помечен как @Primary, чтобы функция инициализатора базы данных использовала вашу копию (если вы используете инициализатор).

Оба источника данных также привязаны для расширенных настроек. Например, вы можете настроить их следующим образом:

Properties
app.datasource.first.url=jdbc:mysql://localhost/first
app.datasource.first.username=dbuser
app.datasource.first.password=dbpass
app.datasource.first.configuration.maximum-pool-size=30

app.datasource.second.url=jdbc:mysql://localhost/second
app.datasource.second.username=dbuser
app.datasource.second.password=dbpass
app.datasource.second.max-total=30
Yaml
app:
  datasource:
    first:
      url: "jdbc:mysql://localhost/first"
      username: "dbuser"
      password: "dbpass"
      configuration:
        maximum-pool-size: 30

    second:
      url: "jdbc:mysql://localhost/second"
      username: "dbuser"
      password: "dbpass"
      max-total: 30

Вы можете применить тот же принцип к второму DataSource, как показано в следующем примере:

Java
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration(proxyBeanMethods = false)
public class MyCompleteDataSourcesConfiguration {

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource.first")
    public DataSourceProperties firstDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource.first.configuration")
    public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) {
        return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Bean
    @ConfigurationProperties("app.datasource.second")
    public DataSourceProperties secondDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @ConfigurationProperties("app.datasource.second.configuration")
    public BasicDataSource secondDataSource(
            @Qualifier("secondDataSourceProperties") DataSourceProperties secondDataSourceProperties) {
        return secondDataSourceProperties.initializeDataSourceBuilder().type(BasicDataSource.class).build();
    }

}
Kotlin
import com.zaxxer.hikari.HikariDataSource
import org.apache.commons.dbcp2.BasicDataSource
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary

@Configuration(proxyBeanMethods = false)
class MyCompleteDataSourcesConfiguration {

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource.first")
    fun firstDataSourceProperties(): DataSourceProperties {
        return DataSourceProperties()
    }

    @Bean
    @Primary
    @ConfigurationProperties("app.datasource.first.configuration")
    fun firstDataSource(firstDataSourceProperties: DataSourceProperties): HikariDataSource {
        return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
    }

    @Bean
    @ConfigurationProperties("app.datasource.second")
    fun secondDataSourceProperties(): DataSourceProperties {
        return DataSourceProperties()
    }

    @Bean
    @ConfigurationProperties("app.datasource.second.configuration")
    fun secondDataSource(secondDataSourceProperties: DataSourceProperties): BasicDataSource {
        return secondDataSourceProperties.initializeDataSourceBuilder().type(BasicDataSource::class.java).build()
    }

}

В приведенном выше примере настраиваются два источника данных в пользовательских пространствах имён с той же логикой, что Spring Boot использует в автоконфигурации. Обратите внимание, что каждое подпространство configuration предоставляет расширенные настройки, основанные на выбранной реализации.

8.3. Использование Spring Data Repositories

Spring Data может создавать реализации @Repository интерфейсов различных типов. Spring Boot обрабатывает всё это за вас, если эти @Repositories находятся в одном пакете (или подпакете) с вашим классом @EnableAutoConfiguration.

Для многих приложений достаточно добавить необходимые зависимости Spring Data в ваш класс. Существуют стартеры для JPA, Mongodb и другие стартеры для поддерживаемых технологий. Для начала создайте интерфейсы репозитория для обработки ваших объектов @Entity.

Spring Boot пытается угадать расположение ваших @Repository определений, основываясь на @EnableAutoConfiguration, которые он находит. Для большей гибкости используйте аннотацию @EnableJpaRepositories (из Spring Data JPA).

Для получения дополнительной информации о Spring Data см. страницу проекта Spring Data.

8.4. Отделение определений сущностей от конфигурации Spring

Spring Boot пытается угадать расположение ваших @Entity определений, основываясь на @EnableAutoConfiguration, которые он находит. Для большего контроля можно использовать аннотацию @EntityScan, как показано в следующем примере:

Java
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EntityScan(basePackageClasses = City.class)
public class MyApplication {

    // ...

}
Kotlin
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EntityScan(basePackageClasses = [City::class])
class MyApplication {

    // ...

}

8.5. Настройка свойств JPA

Spring Data JPA уже предоставляет некоторые независимые от поставщика параметры конфигурации (например, для ведения журнала SQL), а Spring Boot экспонирует эти параметры и несколько дополнительных для Hibernate в качестве внешних свойств конфигурации. Некоторые из них автоматически обнаруживаются в соответствии с контекстом, поэтому вам не нужно их устанавливать.

Свойство spring.jpa.hibernate.ddl-auto является особым случаем, поскольку в зависимости от условий выполнения имеет разные значения по умолчанию. Если используется встроенная база данных и нет менеджера схемы (например, Liquibase или Flyway), который обрабатывает DataSource, он устанавливает значение по умолчанию create-drop. Во всех других случаях значение по умолчанию — none.

Диалект для использования определяется поставщиком JPA. Если вы предпочитаете указать диалект самостоятельно, задайте свойство spring.jpa.database-platform.

В следующем примере показаны наиболее распространенные параметры для установки:

Свойства
spring.jpa.hibernate.naming.physical-strategy=com.example.MyPhysicalNamingStrategy
spring.jpa.show-sql=true
Yaml
spring:
  jpa:
    hibernate:
      naming:
        physical-strategy: "com.example.MyPhysicalNamingStrategy"
    show-sql: true

Кроме того, все свойства в spring.jpa.properties.* передаются как обычные свойства JPA (с удалённым префиксом) при создании локальной EntityManagerFactory.

Необходимо убедиться, что имена, определённые в spring.jpa.properties.*, точно соответствуют ожидаемым вашим поставщиком JPA. Spring Boot не будет пытаться применить какое-либо ослабленное привязывание для этих записей.

Например, если вы хотите настроить размер пакетной обработки Hibernate, вы должны использовать spring.jpa.properties.hibernate.jdbc.batch_size. Если вы используете другие формы, такие как batchSize или batch-size, Hibernate не применит настройку.

Если вам нужно применить расширенную настройку к свойствам Hibernate, рассмотрите возможность регистрации компонента HibernatePropertiesCustomizer, который будет вызван перед созданием EntityManagerFactory. Это имеет приоритет перед любыми настройками, применяемыми автоматической конфигурацией.

8.6. Настройка стратегии именования Hibernate

Hibernate использует две разные стратегии именования для сопоставления имён из модели объектов с соответствующими именами в базе данных. Полное квалифицированное имя реализаций физической и неявной стратегий можно настроить, задав свойства spring.jpa.hibernate.naming.physical-strategy и spring.jpa.hibernate.naming.implicit-strategy соответственно. В качестве альтернативы, если компоненты ImplicitNamingStrategy или PhysicalNamingStrategy доступны в контексте приложения, Hibernate будет автоматически настроен на их использование.

По умолчанию Spring Boot настраивает физическую стратегию именования с помощью CamelCaseToUnderscoresNamingStrategy. Используя эту стратегию, все точки заменяются нижними подчёркиваниями, а верблюжьи обозначения — также. Кроме того, по умолчанию все имена таблиц генерируются в нижнем регистре. Например, сущность TelephoneNumber сопоставляется с таблицей telephone_number. Если ваша схема требует идентификаторов с разными регистрами, определите пользовательский компонент CamelCaseToUnderscoresNamingStrategy, как показано в следующем примере:

Java
import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyHibernateConfiguration {

    @Bean
    public CamelCaseToUnderscoresNamingStrategy caseSensitivePhysicalNamingStrategy() {
        return new CamelCaseToUnderscoresNamingStrategy() {

            @Override
            protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
                return false;
            }

        };
    }

}
Kotlin
import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyHibernateConfiguration {

    @Bean
    fun caseSensitivePhysicalNamingStrategy(): CamelCaseToUnderscoresNamingStrategy {
        return object : CamelCaseToUnderscoresNamingStrategy() {
            override fun isCaseInsensitive(jdbcEnvironment: JdbcEnvironment): Boolean {
                return false
            }
        }
    }

}

Если вы предпочитаете использовать по умолчанию Hibernate, задайте следующее свойство:

spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

В качестве альтернативы, можно настроить следующий компонент:

Java
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
class MyHibernateConfiguration {

    @Bean
    PhysicalNamingStrategyStandardImpl caseSensitivePhysicalNamingStrategy() {
        return new PhysicalNamingStrategyStandardImpl();
    }

}
Kotlin
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
internal class MyHibernateConfiguration {

    @Bean
    fun caseSensitivePhysicalNamingStrategy(): PhysicalNamingStrategyStandardImpl {
        return PhysicalNamingStrategyStandardImpl()
    }

}

См. HibernateJpaAutoConfiguration и JpaBaseConfiguration для получения более подробной информации.

8.7. Настройка кэширования второго уровня Hibernate

Hibernate кэш второго уровня может быть настроен для ряда поставщиков кэша. Вместо повторного поиска поставщика кэша Hibernate лучше предоставить его, если он доступен в контексте.

Для этого с JCache сначала убедитесь, что org.hibernate.orm:hibernate-jcache доступен в классе. Затем добавьте компонент HibernatePropertiesCustomizer, как показано в следующем примере:

Java
import org.hibernate.cache.jcache.ConfigSettings;

import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyHibernateSecondLevelCacheConfiguration {

    @Bean
    public HibernatePropertiesCustomizer hibernateSecondLevelCacheCustomizer(JCacheCacheManager cacheManager) {
        return (properties) -> properties.put(ConfigSettings.CACHE_MANAGER, cacheManager.getCacheManager());
    }

}
Kotlin
import org.hibernate.cache.jcache.ConfigSettings
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer
import org.springframework.cache.jcache.JCacheCacheManager
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyHibernateSecondLevelCacheConfiguration {

    @Bean
    fun hibernateSecondLevelCacheCustomizer(cacheManager: JCacheCacheManager): HibernatePropertiesCustomizer {
        return HibernatePropertiesCustomizer { properties ->
            properties[ConfigSettings.CACHE_MANAGER] = cacheManager.cacheManager
        }
    }

}

Этот кастомизатор настроит Hibernate на использование того же CacheManager, что и в приложении. Также возможно использовать отдельные CacheManager экземпляры. Подробности см. в руководстве пользователя Hibernate.

8.8. Использование инъекции зависимостей в компонентах Hibernate

По умолчанию Spring Boot регистрирует реализацию BeanContainer, которая использует BeanFactory, чтобы преобразователи и слушатели сущностей могли использовать обычную инъекцию зависимостей.

Вы можете отключить или настроить это поведение, зарегистрировав компонент HibernatePropertiesCustomizer, который удалит или изменит свойство hibernate.resource.beans.container.

8.9. Использование пользовательского EntityManagerFactory

Чтобы полностью контролировать конфигурацию EntityManagerFactory, необходимо добавить компонент @Bean с именем ‘entityManagerFactory’. Spring Boot автоматически отключает свой менеджер сущностей в присутствии компонента такого типа.

8.10. Использование нескольких EntityManagerFactory

Если вам нужно использовать JPA с несколькими источниками данных, вам, скорее всего, понадобится по одному EntityManagerFactory на каждый источник данных. LocalContainerEntityManagerFactoryBean из Spring ORM позволяет настроить EntityManagerFactory для ваших нужд. Также можно повторно использовать JpaProperties для привязки настроек к каждому EntityManagerFactory, как показано в следующем примере:

Java
import javax.sql.DataSource;

import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;

@Configuration(proxyBeanMethods = false)
public class MyEntityManagerFactoryConfiguration {

    @Bean
    @ConfigurationProperties("app.jpa.first")
    public JpaProperties firstJpaProperties() {
        return new JpaProperties();
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean firstEntityManagerFactory(DataSource firstDataSource,
            JpaProperties firstJpaProperties) {
        EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(firstJpaProperties);
        return builder.dataSource(firstDataSource).packages(Order.class).persistenceUnit("firstDs").build();
    }

    private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties jpaProperties) {
        JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(jpaProperties);
        return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.getProperties(), null);
    }

    private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
        // ... map JPA properties as needed
        return new HibernateJpaVendorAdapter();
    }

}
Kotlin
import javax.sql.DataSource

import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.orm.jpa.JpaVendorAdapter
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter

@Configuration(proxyBeanMethods = false)
class MyEntityManagerFactoryConfiguration {

    @Bean
    @ConfigurationProperties("app.jpa.first")
    fun firstJpaProperties(): JpaProperties {
        return JpaProperties()
    }

    @Bean
    fun firstEntityManagerFactory(
        firstDataSource: DataSource?,
        firstJpaProperties: JpaProperties
    ): LocalContainerEntityManagerFactoryBean {
        val builder = createEntityManagerFactoryBuilder(firstJpaProperties)
        return builder.dataSource(firstDataSource).packages(Order::class.java).persistenceUnit("firstDs").build()
    }

    private fun createEntityManagerFactoryBuilder(jpaProperties: JpaProperties): EntityManagerFactoryBuilder {
        val jpaVendorAdapter = createJpaVendorAdapter(jpaProperties)
        return EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.properties, null)
    }

    private fun createJpaVendorAdapter(jpaProperties: JpaProperties): JpaVendorAdapter {
        // ... map JPA properties as needed
        return HibernateJpaVendorAdapter()
    }

}

В примере выше создаётся EntityManagerFactory с использованием компонента DataSource с именем firstDataSource. Он сканирует сущности, расположенные в том же пакете, что и Order. Возможно сопоставление дополнительных свойств JPA с использованием пространства имён app.first.jpa.

Когда вы создаёте компонент для LocalContainerEntityManagerFactoryBean самостоятельно, любая настройка, применённая при создании автоматически настроенного LocalContainerEntityManagerFactoryBean, теряется. Например, в случае Hibernate, любые свойства с префиксом spring.jpa.hibernate не будут автоматически применены к вашему LocalContainerEntityManagerFactoryBean. Если вы полагались на эти свойства для настройки таких вещей, как стратегия именования или режим DDL, вам необходимо явно настроить их при создании компонента LocalContainerEntityManagerFactoryBean.

Вы должны предоставить аналогичную конфигурацию для любых дополнительных источников данных, для которых вам нужен доступ к JPA. Для завершения картины, необходимо настроить JpaTransactionManager для каждого EntityManagerFactory также. В качестве альтернативы, возможно использование JTA менеджера транзакций, охватывающего оба.

Если вы используете Spring Data, вам необходимо настроить @EnableJpaRepositories соответствующим образом, как показано в следующих примерах:

Java
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = Order.class, entityManagerFactoryRef = "firstEntityManagerFactory")
public class OrderConfiguration {

}
Kotlin
import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaRepositories

@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = [Order::class], entityManagerFactoryRef = "firstEntityManagerFactory")
class OrderConfiguration
Java
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = Customer.class, entityManagerFactoryRef = "secondEntityManagerFactory")
public class CustomerConfiguration {

}
Kotlin
import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaRepositories

@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = [Customer::class], entityManagerFactoryRef = "secondEntityManagerFactory")
class CustomerConfiguration

8.11. Использование традиционного файла persistence.xml

Spring Boot по умолчанию не будет искать или использовать файл META-INF/persistence.xml. Если вы предпочитаете использовать традиционный файл persistence.xml, вам нужно определить собственный @Bean типа LocalEntityManagerFactoryBean (с идентификатором ‘entityManagerFactory’) и установить там имя persistence unit.

См. JpaBaseConfiguration для параметров по умолчанию.

8.12. Использование Spring Data JPA и Mongo репозиториев

Spring Data JPA и Spring Data Mongo могут автоматически создавать реализации Repository. Если оба присутствуют в classpath, возможно, потребуется дополнительная настройка, чтобы указать Spring Boot, какие репозитории создавать. Наиболее явным способом это сделать является использование стандартных аннотаций Spring Data @EnableJpaRepositories и @EnableMongoRepositories и указание расположения ваших интерфейсов Repository.

Также существуют флаги (spring.data.*.repositories.enabled и spring.data.*.repositories.type), которые можно использовать для включения и отключения автоматически настроенных репозиториев во внешней конфигурации. Это полезно, например, в случае, если вы хотите отключить Mongo репозитории и по-прежнему использовать автоматически настроенные MongoTemplate.

Такая же проблема и те же возможности существуют для других автоматически настраиваемых типов репозиториев Spring Data (Elasticsearch, Redis и других). Для работы с ними следует соответственно изменить названия аннотаций и флагов.

8.13. Настройка поддержки Spring Data в веб-приложениях

Spring Data предоставляет поддержку веб-приложений, упрощающую использование репозиториев Spring Data в веб-приложении. Spring Boot предоставляет свойства в пространстве имён spring.data.web для настройки конфигурации. Обратите внимание, что если вы используете Spring Data REST, необходимо использовать свойства в пространстве имён spring.data.rest.

8.14. Предоставление репозиториев Spring Data как REST-эндпоинтов

Spring Data REST может предоставить реализации Repository как REST-эндпоинты, при условии, что Spring MVC включен для приложения.

Spring Boot предоставляет набор полезных свойств (из пространства имён spring.data.rest), которые настраивают RepositoryRestConfiguration. Если требуется дополнительная настройка, необходимо использовать bean RepositoryRestConfigurer.

Если вы не указываете порядок для вашего пользовательского RepositoryRestConfigurer, он выполняется после того, как Spring Boot выполняет внутренний. Если необходимо указать порядок, убедитесь, что он выше 0.

8.15. Настройка компонента, используемого JPA

Если вы хотите настроить компонент, используемый JPA, необходимо убедиться, что компонент инициализирован до JPA. Когда компонент настраивается автоматически, Spring Boot позаботится об этом за вас. Например, при автоматической настройке Flyway, Hibernate настраивается таким образом, чтобы зависеть от Flyway, что даёт Flyway возможность инициализировать базу данных перед тем, как Hibernate попытается её использовать.

Если вы настраиваете компонент самостоятельно, вы можете использовать подкласс EntityManagerFactoryDependsOnPostProcessor в качестве удобного способа настройки необходимых зависимостей. Например, если вы используете Hibernate Search с Elasticsearch в качестве менеджера индексов, все EntityManagerFactory бины должны быть настроены так, чтобы зависеть от elasticsearchClient бина, как показано в следующем примере:

Java
import jakarta.persistence.EntityManagerFactory;

import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryDependsOnPostProcessor;
import org.springframework.stereotype.Component;

/**
 * {@link EntityManagerFactoryDependsOnPostProcessor} that ensures that
 * {@link EntityManagerFactory} beans depend on the {@code elasticsearchClient} bean.
 */
@Component
public class ElasticsearchEntityManagerFactoryDependsOnPostProcessor
        extends EntityManagerFactoryDependsOnPostProcessor {

    public ElasticsearchEntityManagerFactoryDependsOnPostProcessor() {
        super("elasticsearchClient");
    }

}
Kotlin
import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryDependsOnPostProcessor
import org.springframework.stereotype.Component

@Component
class ElasticsearchEntityManagerFactoryDependsOnPostProcessor :
    EntityManagerFactoryDependsOnPostProcessor("elasticsearchClient")

8.16. Настройка jOOQ с несколькими источниками данных

Если вам нужно использовать jOOQ с несколькими источниками данных, следует создать собственный DSLContext для каждого. Подробнее см. в JooqAutoConfiguration.

В частности, JooqExceptionTranslator и SpringTransactionProvider могут быть повторно использованы для предоставления аналогичных возможностей, которые обеспечивает автоматическая настройка с одним DataSource.

9. Инициализация базы данных

Базу данных SQL можно инициализировать различными способами, в зависимости от используемой стека. Конечно, вы можете сделать это вручную, если база данных — отдельный процесс. Рекомендуется использовать единый механизм для генерации схемы.

9.1. Инициализация базы данных с помощью JPA

JPA предоставляет средства для генерации DDL, которые можно настроить на выполнение при запуске приложения на базе данных. Это контролируется двумя внешними свойствами:

  • spring.jpa.generate-ddl (boolean) включает и выключает эту функцию и является независимой от поставщика.

  • spring.jpa.hibernate.ddl-auto (перечисление) — функция Hibernate, которая более точно управляет поведением. Эта функция описывается более подробно в этом руководстве.

9.2. Инициализация базы данных с помощью Hibernate

Вы можете явно установить spring.jpa.hibernate.ddl-auto, и стандартные значения свойства Hibernate — none, validate, update, create и create-drop. Spring Boot выбирает для вас значение по умолчанию, исходя из того, считает ли он вашу базу данных встроенной. По умолчанию это значение create-drop, если не обнаружен менеджер схем, или none во всех остальных случаях. Встроенная база данных определяется по типу Connection и URL JDBC. hsqldb, h2 и derby — кандидаты, а другие — нет. Будьте внимательны, переключаясь с памяти на «реальную» базу данных, чтобы не делать предположений о существовании таблиц и данных на новой платформе. Вам нужно явно установить ddl-auto или использовать один из других механизмов для инициализации базы данных.

Вы можете вывести создание схемы, включив логгер org.hibernate.SQL. Это делается автоматически, если включен режим отладки debug mode.

Кроме того, файл с именем import.sql в корне classpath выполняется при запуске, если Hibernate создает схему с нуля (то есть, если свойство ddl-auto установлено в create или create-drop). Это может быть полезно для демонстраций и тестирования, но, скорее всего, не нужно оставлять его в classpath в рабочей среде. Это функция Hibernate (и не имеет ничего общего со Spring).

9.3. Инициализация базы данных с помощью простых SQL-скриптов

Spring Boot может автоматически создавать схему (скрипты DDL) вашей JDBC-базы данных DataSource или R2DBC-базы данных ConnectionFactory и инициализировать её данные (скрипты DML).

По умолчанию он загружает скрипты схемы из optional:classpath*:schema.sql, а скрипты данных — из optional:classpath*:data.sql. Местоположения этих скриптов схемы и данных могут быть настраиваемы с помощью spring.sql.init.schema-locations и spring.sql.init.data-locations соответственно. Префикс optional: означает, что приложение будет запускаться, даже если файлы отсутствуют. Чтобы приложение не запускалось, если файлы отсутствуют, удалите префикс optional:.

Кроме того, Spring Boot обрабатывает файлы optional:classpath*:schema-${platform}.sql и optional:classpath*:data-${platform}.sql (если они есть), где ${platform} — значение spring.sql.init.platform. Это позволяет переключаться на скрипты, специфичные для базы данных, при необходимости. Например, вы можете установить его в имя поставщика базы данных (hsqldb, h2, oracle, mysql, postgresql и так далее).

По умолчанию инициализация SQL-базы данных выполняется только при использовании встроенной базы данных памяти. Чтобы всегда инициализировать SQL-базу данных, независимо от её типа, установите spring.sql.init.mode в значение always. Аналогично, чтобы отключить инициализацию, установите spring.sql.init.mode в значение never. По умолчанию Spring Boot включает функцию быстрой ошибки (fail-fast) своего инициализатора базы данных на основе скриптов. Это означает, что если скрипты вызывают исключения, приложение не запускается. Вы можете настроить это поведение, установив spring.sql.init.continue-on-error.

Инициализация на основе скриптов DataSource выполняется по умолчанию до создания любых JPA-биндов EntityManagerFactory. schema.sql может быть использован для создания схемы для JPA-управляемых сущностей, а data.sql — для её заполнения. Хотя мы не рекомендуем использовать несколько технологий инициализации источников данных, если вы хотите, чтобы инициализация на основе скриптов DataSource строилась на основе создания схемы Hibernate, установите spring.jpa.defer-datasource-initialization в значение true. Это отложит инициализацию источника данных до создания и инициализации бинов EntityManagerFactory. schema.sql может быть использован для добавления к любому созданию схемы Hibernate, а data.sql — для заполнения.

Если вы используете инструмент миграции базы данных высокого уровня, такой как Flyway или Liquibase, используйте их для создания и инициализации схемы. Использование простых скриптов schema.sql и data.sql вместе с Flyway или Liquibase не рекомендуется, и поддержка будет удалена в будущих версиях.

9.4. Инициализация базы данных Spring Batch

Если вы используете Spring Batch, в нём уже есть SQL-скрипты инициализации для большинства популярных платформ баз данных. Spring Boot может определить тип вашей базы данных и выполнить эти скрипты при запуске. Если вы используете встроенную базу данных, это происходит по умолчанию. Вы также можете включить это для любого типа базы данных, как показано в следующем примере:

Свойства
spring.batch.jdbc.initialize-schema=always
Yaml
spring:
  batch:
    jdbc:
      initialize-schema: "always"

Вы также можете отключить инициализацию явно, установив spring.batch.jdbc.initialize-schema в значение never.

9.5. Использование инструмента миграции базы данных высокого уровня

Spring Boot поддерживает два инструмента миграции базы данных высокого уровня: Flyway и Liquibase.

9.5.1. Выполнение миграций базы данных Flyway при запуске

Для автоматического выполнения миграций базы данных Flyway при запуске добавьте org.flywaydb:flyway-core в ваш classpath.

Обычно, миграции представляют собой скрипты в формате V<VERSION>__<NAME>.sql (с <VERSION>, разделяемым дефисом, например, ‘1’ или ‘2_1’). По умолчанию, они находятся в директории под названием classpath:db/migration, но вы можете изменить это расположение, задав spring.flyway.locations. Это список, разделенный запятыми, одного или нескольких classpath: или filesystem: расположений. Например, следующая конфигурация будет искать скрипты как в стандартном расположении classpath, так и в директории /opt/migration:

Свойства
spring.flyway.locations=classpath:db/migration,filesystem:/opt/migration
Yaml
spring:
  flyway:
    locations: "classpath:db/migration,filesystem:/opt/migration"

Вы также можете добавить специальный {vendor} плейсхолдер для использования скриптов, специфичных для поставщика. Предположим следующее:

Свойства
spring.flyway.locations=classpath:db/migration/{vendor}
Yaml
spring:
  flyway:
    locations: "classpath:db/migration/{vendor}"

Вместо использования db/migration, предыдущая конфигурация устанавливает используемую директорию в соответствии с типом базы данных (например, db/migration/mysql для MySQL). Список поддерживаемых баз данных доступен по адресу DatabaseDriver.

Миграции также могут быть написаны на Java. Flyway будет автоматически настроен с любыми компонентами, реализующими JavaMigration.

FlywayProperties предоставляет большинство настроек Flyway и небольшой набор дополнительных свойств, которые могут быть использованы для отключения миграций или отключения проверки расположения. Если вам нужен больший контроль над конфигурацией, рассмотрите регистрацию компонента FlywayConfigurationCustomizer.

Spring Boot вызывает Flyway.migrate() для выполнения миграции базы данных. Если вам нужен больший контроль, предоставьте @Bean, который реализует FlywayMigrationStrategy.

Flyway поддерживает SQL и Java обработчики событий. Для использования обработчиков на основе SQL поместите скрипты обработчиков в директорию classpath:db/migration. Для использования обработчиков на основе Java создайте один или несколько компонентов, реализующих Callback. Все такие компоненты автоматически регистрируются в Flyway. Они могут быть упорядочены с помощью @Order или путем реализации интерфейса Ordered. Компоненты, реализующие устаревший интерфейс FlywayCallback, также могут быть обнаружены, однако они не могут использоваться вместе с компонентами Callback.

По умолчанию, Flyway автоматически подключает (@Primary) DataSource в вашем контексте и использует его для миграций. Если вы хотите использовать другой DataSource, вы можете создать его и отметить его @Bean как @FlywayDataSource. Если вы это сделаете и хотите два источника данных, помните, что нужно создать ещё один и отметить его как @Primary. В качестве альтернативы, вы можете использовать собственный DataSource Flyway, задав spring.flyway.[url,user,password] во внешних свойствах. Установка любого из spring.flyway.url или spring.flyway.user достаточно для того, чтобы Flyway использовал свой собственный DataSource. Если ни одно из трёх свойств не установлено, будет использоваться значение соответствующего свойства spring.datasource.

Вы также можете использовать Flyway для предоставления данных для определённых сценариев. Например, вы можете поместить миграции, специфичные для тестов, в src/test/resources, и они будут выполняться только при запуске вашего приложения для тестирования. Также вы можете использовать конфигурацию, специфичную для профилей, чтобы настроить spring.flyway.locations таким образом, чтобы определённые миграции выполнялись только при активации определённого профиля. Например, в application-dev.properties, вы можете указать следующее значение:

Свойства
spring.flyway.locations=classpath:/db/migration,classpath:/dev/db/migration
Yaml
spring:
  flyway:
    locations: "classpath:/db/migration,classpath:/dev/db/migration"

С этой настройкой миграции в dev/db/migration выполняются только при активации профиля dev.

9.5.2. Выполнение миграций базы данных Liquibase при запуске

Для автоматического выполнения миграций базы данных Liquibase при запуске добавьте org.liquibase:liquibase-core в ваш classpath.

Когда вы добавляете org.liquibase:liquibase-core в ваш classpath, миграции базы данных по умолчанию выполняются как во время запуска приложения, так и перед выполнением ваших тестов. Это поведение можно настроить, используя свойство spring.liquibase.enabled, установив разные значения в конфигурациях main и test. Невозможно использовать два разных способа инициализации базы данных (например, Liquibase для запуска приложения, JPA для запусков тестов).

По умолчанию, главный журнал изменений считывается из db/changelog/db.changelog-master.yaml, но вы можете изменить расположение, задав spring.liquibase.change-log. Помимо YAML, Liquibase также поддерживает форматы JSON, XML и SQL для журнала изменений.

По умолчанию, Liquibase автоматически подключает (@Primary) DataSource в ваш контекст и использует его для миграций. Если вам нужно использовать другой DataSource, вы можете создать его и отметить его @Bean как @LiquibaseDataSource. Если вы это сделаете и хотите два источника данных, помните, что нужно создать ещё один и отметить его как @Primary. В качестве альтернативы, вы можете использовать собственный DataSource Liquibase, задав spring.liquibase.[driver-class-name,url,user,password] во внешних свойствах. Установка либо spring.liquibase.url, либо spring.liquibase.user достаточно для того, чтобы Liquibase использовал свой собственный DataSource. Если ни одно из трёх свойств не установлено, будет использоваться значение соответствующего свойства spring.datasource.

Подробную информацию о доступных настройках, таких как контексты, схема по умолчанию и другие, см. в LiquibaseProperties.

9.6. Зависимость от инициализированной базы данных

Инициализация базы данных выполняется при запуске приложения как часть обновления контекста приложения. Для доступа к инициализированной базе данных во время запуска приложения автоматически обнаруживаются компоненты, действующие как инициализаторы базы данных, и компоненты, которым требуется, чтобы база данных была инициализирована. Компоненты, инициализация которых зависит от того, была ли инициализирована база данных, настраиваются так, чтобы зависеть от тех, которые её инициализируют. Если во время запуска ваше приложение пытается получить доступ к базе данных, а она не была инициализирована, вы можете настроить дополнительное обнаружение компонентов, которые инициализируют базу данных и требуют её инициализации.

9.6.1. Обнаружение инициализатора базы данных

Spring Boot автоматически обнаружит компоненты следующих типов, которые инициализируют базу данных SQL:

  • DataSourceScriptDatabaseInitializer

  • EntityManagerFactory

  • Flyway

  • FlywayMigrationInitializer

  • R2dbcScriptDatabaseInitializer

  • SpringLiquibase

Если вы используете сторонний стартер для библиотеки инициализации базы данных, он может предоставить детектор, который позволит автоматически обнаруживать компоненты и других типов. Чтобы обнаруживать другие компоненты, зарегистрируйте реализацию DatabaseInitializerDetector в META-INF/spring.factories.

9.6.2. Обнаружение компонента, зависящего от инициализации базы данных

Spring Boot автоматически обнаружит компоненты следующих типов, которые зависят от инициализации базы данных:

  • AbstractEntityManagerFactoryBean (если spring.jpa.defer-datasource-initialization не установлено в true)

  • DSLContext (jOOQ)

  • EntityManagerFactory (если spring.jpa.defer-datasource-initialization не установлено в true)

  • JdbcOperations

  • NamedParameterJdbcOperations

Если вы используете стороннюю библиотеку доступа к данным, она может предоставить детектор, который позволяет автоматически обнаруживать компоненты других типов. Чтобы обнаруживать другие компоненты, зарегистрируйте реализацию DependsOnDatabaseInitializationDetector в META-INF/spring.factories. В качестве альтернативы, отметьте класс компонента или его метод @Bean аннотацией @DependsOnDatabaseInitialization.

10. NoSQL

Spring Boot предлагает несколько стартеров, поддерживающих технологии NoSQL. В этом разделе рассматриваются вопросы, возникающие при использовании NoSQL с Spring Boot.

10.1. Использование Jedis вместо Lettuce

По умолчанию, стартер Spring Boot (spring-boot-starter-data-redis) использует Lettuce. Вам необходимо исключить эту зависимость и включить зависимость Jedis. Spring Boot управляет обеими этими зависимостями, поэтому вы можете переключиться на Jedis, не указывая версию.

Следующий пример демонстрирует, как это сделать в Maven:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

Следующий пример демонстрирует, как это сделать в Gradle:

dependencies {
    implementation('org.springframework.boot:spring-boot-starter-data-redis') {
        exclude group: 'io.lettuce', module: 'lettuce-core'
    }
    implementation 'redis.clients:jedis'
    // ...
}

11. Сообщения

Spring Boot предлагает несколько стартеров для поддержки обмена сообщениями. В этом разделе рассматриваются вопросы, возникающие при использовании обмена сообщениями с Spring Boot.

11.1. Отключение транзакционных сессий JMS

Если ваш брокер JMS не поддерживает транзакционные сессии, вы должны полностью отключить поддержку транзакций. Если вы создаёте свой собственный JmsListenerContainerFactory, ничего делать не нужно, так как по умолчанию он не может быть транзакционным. Если вы хотите использовать DefaultJmsListenerContainerFactoryConfigurer для повторного использования значения по умолчанию Spring Boot, вы можете отключить транзакционные сессии следующим образом:

Java
import jakarta.jms.ConnectionFactory;

import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;

@Configuration(proxyBeanMethods = false)
public class MyJmsConfiguration {

    @Bean
    public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory,
            DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory listenerFactory = new DefaultJmsListenerContainerFactory();
        configurer.configure(listenerFactory, connectionFactory);
        listenerFactory.setTransactionManager(null);
        listenerFactory.setSessionTransacted(false);
        return listenerFactory;
    }

}
Kotlin
import jakarta.jms.ConnectionFactory
import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.jms.config.DefaultJmsListenerContainerFactory

@Configuration(proxyBeanMethods = false)
class MyJmsConfiguration {

    @Bean
    fun jmsListenerContainerFactory(connectionFactory: ConnectionFactory?,
            configurer: DefaultJmsListenerContainerFactoryConfigurer): DefaultJmsListenerContainerFactory {
        val listenerFactory = DefaultJmsListenerContainerFactory()
        configurer.configure(listenerFactory, connectionFactory)
        listenerFactory.setTransactionManager(null)
        listenerFactory.setSessionTransacted(false)
        return listenerFactory
    }

}

В приведенном примере переопределяется фабрика по умолчанию, и он должен применяться ко всем другим фабрикам, определённым в вашем приложении.

12. Приложения пакетной обработки

При использовании Spring Batch в приложении Spring Boot часто возникают вопросы. В этом разделе рассматриваются эти вопросы.

12.1. Указание источника данных для пакетной обработки

По умолчанию, приложения пакетной обработки требуют DataSource для хранения деталей задач. Spring Batch по умолчанию ожидает единственный DataSource. Чтобы использовать DataSource, отличный от основного DataSource приложения, объявите бин DataSource, аннотировав его метод @Bean аннотацией @BatchDataSource. Если вы это сделаете и хотите использовать два источника данных, не забудьте отметить другой @Primary. Для большего контроля добавьте @EnableBatchProcessing в один из ваших @Configuration классов или расширьте DefaultBatchConfiguration. Более подробную информацию см. в документации @EnableBatchProcessing и DefaultBatchConfiguration.

Для получения дополнительной информации о Spring Batch посетите страницу проекта Spring Batch.

12.2. Запуск задач Spring Batch при запуске

Автоконфигурация Spring Batch включается путём добавления spring-boot-starter-batch в ваш classpath.

Если в контексте приложения найдена единственная Job, она выполняется при запуске (см. JobLauncherApplicationRunner для подробностей). Если найдено несколько бин Job, задача, которая должна быть выполнена, должна быть указана с помощью spring.batch.job.name.

Чтобы отключить выполнение задачи Job, найденной в контексте приложения, установите spring.batch.job.enabled в false.

Для получения дополнительной информации см. BatchAutoConfiguration.

12.3. Запуск из командной строки

Spring Boot преобразует любой аргумент командной строки, начинающийся с --, в свойство, которое добавляется в Environment, см. доступ к свойствам командной строки. Это не следует использовать для передачи аргументов задачам пакетной обработки. Для указания аргументов пакетной обработки в командной строке используйте обычный формат (то есть без --), как показано в следующем примере:

$ java -jar myapp.jar someParameter=someValue anotherParameter=anotherValue

Если вы укажете свойство Environment в командной строке, оно будет проигнорировано задачей. Рассмотрим следующую команду:

$ java -jar myapp.jar --server.port=7070 someParameter=someValue

Это предоставляет только один аргумент задаче пакетной обработки: someParameter=someValue.

12.4. Хранение хранилища задач

Spring Batch требует хранилище данных для Job хранилища. Если вы используете Spring Boot, вам необходимо использовать фактическую базу данных. Обратите внимание, что это может быть база данных в памяти, см. Настройка хранилища задач.

13. Активатор

Spring Boot включает Spring Boot Активатор. В этом разделе отвечают на вопросы, которые часто возникают при его использовании.

13.1. Изменение порта или адреса HTTP-точек входа активатора

В автономном приложении порт HTTP-активатора по умолчанию совпадает с основным портом HTTP. Чтобы приложение прослушивало другой порт, установите внешнее свойство: management.server.port. Чтобы прослушивать другой сетевой адрес (например, когда у вас есть внутренняя сеть для управления и внешняя для пользовательских приложений), вы также можете установить management.server.address на допустимый IP-адрес, к которому сервер может подключиться.

Более подробную информацию можно найти в ManagementServerProperties исходном коде и в разделе «Готовые к работе функции» в «actuator.html».

13.2. Настройка страницы ошибки «whitelabel»

Spring Boot устанавливает страницу ошибки «whitelabel», которую вы видите в клиенте браузера, если возникает ошибка сервера (клиенты машин, потребляющие JSON и другие типы данных, должны видеть осмысленный ответ с правильным кодом ошибки).

Установите server.error.whitelabel.enabled=false, чтобы отключить страницу ошибки по умолчанию. Это восстановит настройки по умолчанию для используемого контейнера сервлетов. Обратите внимание, что Spring Boot по-прежнему пытается разрешить представление ошибки, поэтому вам, вероятно, следует добавить собственную страницу ошибки, а не полностью ее отключать.

Переопределение страницы ошибки собственным шаблоном зависит от используемой вами технологии шаблонов. Например, если вы используете Thymeleaf, вы можете добавить шаблон error.html. Если вы используете FreeMarker, вы можете добавить шаблон error.ftlh. В общем случае вам нужен View, который разрешается с именем error или @Controller, который обрабатывает путь /error. Если вы не заменили некоторые настройки по умолчанию, вы должны найти BeanNameViewResolver в вашем ApplicationContext, поэтому @Bean с именем error будет одним из способов сделать это. Дополнительные параметры см. в ErrorMvcAutoConfiguration.

Также см. раздел «Обработка ошибок» для получения подробной информации о том, как регистрировать обработчики в контейнере сервлетов.

13.3. Санітизация конфіденційних значень

Информация, возвращаемая точками входа /env, /configprops и /quartz, может быть конфиденциальной. По умолчанию все значения санітизуются (заменяются на ******). Просмотр исходных значений в необработанном виде можно настроить для каждой точки входа с помощью свойства showValues для этой точки входа. Это свойство может быть настроено следующим образом:

  • ALWAYS - все значения отображаются в необработанном виде всем пользователям

  • NEVER - все значения всегда санітизуются (заменяются на ******)

  • WHEN_AUTHORIZED - все значения отображаются в необработанном виде авторизованным пользователям

Для HTTP-точек входа пользователь считается авторизованным, если он прошел аутентификацию и имеет роли, настроенные свойством роли точки входа. По умолчанию любой аутентифицированный пользователь считается авторизованным. Для JMX-точек входа все пользователи всегда авторизованы.

Свойства
management.endpoint.env.show-values=WHEN_AUTHORIZED
management.endpoint.env.roles=admin
Yaml
management:
  endpoint:
    env:
      show-values: WHEN_AUTHORIZED
      roles: "admin"

Вышеуказанная настройка позволяет всем пользователям с ролью admin просматривать все значения в их исходном виде из точки входа /env.

Когда show-values установлено в ALWAYS или WHEN_AUTHORIZED, любая санітизация, применяемая SanitizingFunction, всё равно будет применена.

13.3.1. Настройка санітизації

Чтобы получить контроль над санітизацией, определите бин SanitizingFunction. SanitizableData, с помощью которого вызывается функция, предоставляет доступ к ключу и значению, а также к PropertySource, откуда они пришли. Это позволяет, например, санітизировать каждое значение, которое поступает из определенного источника свойств. Каждый SanitizingFunction вызывается по порядку, пока функция не изменит значение санітизируемых данных.

13.4. Сопоставление индикаторов состояния с метриками Micrometer

Индикаторы состояния Spring Boot возвращают тип Status для обозначения общего состояния системы. Если вы хотите отслеживать или подавать сигналы тревоги по уровням состояния для конкретного приложения, вы можете экспортировать эти состояния как метрики с помощью Micrometer. По умолчанию Spring Boot использует коды состояния «UP», «DOWN», «OUT_OF_SERVICE» и «UNKNOWN». Чтобы их экспортировать, вам нужно преобразовать эти состояния в набор чисел, чтобы они могли использоваться с Micrometer Gauge.

Следующий пример демонстрирует один способ написания такого экспортера:

Java
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;

import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.Status;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyHealthMetricsExportConfiguration {

    public MyHealthMetricsExportConfiguration(MeterRegistry registry, HealthEndpoint healthEndpoint) {
        // This example presumes common tags (such as the app) are applied elsewhere
        Gauge.builder("health", healthEndpoint, this::getStatusCode).strongReference(true).register(registry);
    }

    private int getStatusCode(HealthEndpoint health) {
        Status status = health.health().getStatus();
        if (Status.UP.equals(status)) {
            return 3;
        }
        if (Status.OUT_OF_SERVICE.equals(status)) {
            return 2;
        }
        if (Status.DOWN.equals(status)) {
            return 1;
        }
        return 0;
    }

}
Kotlin
import io.micrometer.core.instrument.Gauge
import io.micrometer.core.instrument.MeterRegistry
import org.springframework.boot.actuate.health.HealthEndpoint
import org.springframework.boot.actuate.health.Status
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MyHealthMetricsExportConfiguration(registry: MeterRegistry, healthEndpoint: HealthEndpoint) {

    init {
        // This example presumes common tags (such as the app) are applied elsewhere
        Gauge.builder("health", healthEndpoint) { health ->
            getStatusCode(health).toDouble()
        }.strongReference(true).register(registry)
    }

    private fun getStatusCode(health: HealthEndpoint): Int {
        val status = health.health().status
        if (Status.UP == status) {
            return 3
        }
        if (Status.OUT_OF_SERVICE == status) {
            return 2
        }
        if (Status.DOWN == status) {
            return 1
        }
        return 0
    }

}

14. Безопасность

В этом разделе рассматриваются вопросы безопасности при работе с Spring Boot, включая вопросы, возникающие при использовании Spring Security с Spring Boot.

Для получения дополнительной информации о Spring Security см. страницу проекта Spring Security.

14.1. Выключение конфигурации Spring Boot безопасности

Если вы определите @Configuration с бином SecurityFilterChain в своем приложении, это выключит настройки безопасности веб-приложений по умолчанию в Spring Boot.

14.2. Изменение UserDetailsService и добавление учетных записей пользователей

Если вы предоставите @Bean типа AuthenticationManager, AuthenticationProvider или UserDetailsService, по умолчанию @Bean для InMemoryUserDetailsManager не создается. Это означает, что у вас есть полный функционал Spring Security (например, различные варианты аутентификации).

Самый простой способ добавить учетные записи пользователей — предоставить собственный бин UserDetailsService.

14.3. Включение HTTPS при работе через прокси-сервер

Обеспечение того, что все основные точки входа доступны только по протоколу HTTPS, является важной задачей для любого приложения. Если вы используете Tomcat в качестве контейнера сервлетов, то Spring Boot автоматически добавляет собственный RemoteIpValve Tomcat, если он обнаруживает некоторые настройки среды, и вы можете полагаться на HttpServletRequest, чтобы сообщить, защищен ли он или нет (даже в случае с прокси-сервером, который выполняет фактическое завершение SSL). Стандартное поведение определяется наличием или отсутствием определенных заголовков запросов (x-forwarded-for и x-forwarded-proto), имена которых являются стандартными, поэтому это должно работать с большинством прокси-серверов фронтального типа. Вы можете включить клапан, добавив некоторые записи в application.properties, как показано в следующем примере:

Свойства
server.tomcat.remoteip.remote-ip-header=x-forwarded-for
server.tomcat.remoteip.protocol-header=x-forwarded-proto
Yaml
server:
  tomcat:
    remoteip:
      remote-ip-header: "x-forwarded-for"
      protocol-header: "x-forwarded-proto"

(Наличие любого из этих свойств включает клапан. В качестве альтернативы, вы можете добавить RemoteIpValve, настроив TomcatServletWebServerFactory с помощью бинa WebServerFactoryCustomizer.)

Чтобы настроить Spring Security таким образом, чтобы потребовать защищенный канал для всех (или некоторых) запросов, рассмотрите возможность добавления собственного бинa SecurityFilterChain, который добавляет следующие настройки HttpSecurity:

Java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class MySecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // Customize the application security ...
        http.requiresChannel((channel) -> channel.anyRequest().requiresSecure());
        return http.build();
    }

}
Kotlin
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.web.SecurityFilterChain

@Configuration
class MySecurityConfig {

    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        // Customize the application security ...
        http.requiresChannel { requests -> requests.anyRequest().requiresSecure() }
        return http.build()
    }

}

15. Горячая замена

Spring Boot поддерживает горячую замену. В этом разделе рассматриваются вопросы о том, как она работает.

15.1. Перезагрузка статического контента

Существует несколько вариантов горячей перезагрузки. Рекомендуемый подход — использовать spring-boot-devtools, так как он предоставляет дополнительные функции для разработки, такие как поддержка быстрой перезагрузки приложения и LiveReload, а также разумная конфигурация для разработки (например, кэширование шаблонов). Devtools отслеживает изменения в classpath. Это означает, что изменения в статических ресурсах должны быть «собраны», чтобы изменения вступили в силу. По умолчанию это происходит автоматически в Eclipse при сохранении изменений. В IntelliJ IDEA команда «Собрать проект» запускает необходимую сборку. Из-за исключений перезагрузки по умолчанию изменения в статических ресурсах не приводят к перезагрузке приложения. Однако они запускают живую перезагрузку.

В качестве альтернативы, работа в IDE (особенно с отладкой) — хороший способ разработки (все современные IDE позволяют перезагружать статические ресурсы и обычно также позволяют выполнять горячую замену изменений в классах Java).

Наконец, плагины Maven и Gradle можно настроить (см. свойство addResources), чтобы поддерживать выполнение из командной строки с перезагрузкой статических файлов непосредственно из исходного кода. Вы можете использовать это с внешним процессом компиляции css/js, если вы пишете этот код с помощью инструментов более высокого уровня.

15.2. Перезагрузка шаблонов без перезапуска контейнера

Большинство технологий шаблонизации, поддерживаемых Spring Boot, включают возможность отключения кэширования (описано далее в этом документе). Если вы используете модуль spring-boot-devtools, эти свойства автоматически настраиваются для вас во время разработки.

15.2.1. Шаблоны Thymeleaf

Если вы используете Thymeleaf, установите spring.thymeleaf.cache в false. См. ThymeleafAutoConfiguration для других параметров настройки Thymeleaf.

15.2.2. Шаблоны FreeMarker

Если вы используете FreeMarker, установите spring.freemarker.cache в false. См. FreeMarkerAutoConfiguration для других параметров настройки FreeMarker.

15.2.3. Шаблоны Groovy

Если вы используете шаблоны Groovy, установите spring.groovy.template.cache в false. См. GroovyTemplateAutoConfiguration для других параметров настройки Groovy.

15.3. Быстрая перезагрузка приложения

Модуль spring-boot-devtools включает поддержку автоматической перезагрузки приложения. Хотя он не такой быстрый, как технологии, такие как JRebel, обычно он значительно быстрее, чем «холодный запуск». Попробуйте его перед изучением более сложных вариантов перезагрузки, обсуждаемых позже в этом документе.

Для получения более подробной информации см. раздел using.html.

15.4. Перезагрузка классов Java без перезапуска контейнера

Многие современные IDE (Eclipse, IDEA и другие) поддерживают горячую замену байткода. Следовательно, если вы внесли изменение, не повлиявшее на подписи классов или методов, оно должно перезагружаться без побочных эффектов.

16. Тестирование

Spring Boot включает в себя ряд инструментов и вспомогательных классов для тестирования, а также специальный стартер, который предоставляет общие зависимости для тестов. В этом разделе рассматриваются распространённые вопросы, связанные с тестированием.

16.1. Тестирование с Spring Security

Spring Security предоставляет поддержку запуска тестов как определённого пользователя. Например, тест в приведённом ниже фрагменте будет выполняться с аутентифицированным пользователем, имеющим роль ADMIN.

Java
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

@WebMvcTest(UserController.class)
class MySecurityTests {

    @Autowired
    private MockMvc mvc;

    @Test
    @WithMockUser(roles = "ADMIN")
    void requestProtectedUrlWithUser() throws Exception {
        this.mvc.perform(get("/"));
    }

}
Kotlin
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.security.test.context.support.WithMockUser
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders

@WebMvcTest(UserController::class)
class MySecurityTests(@Autowired val mvc: MockMvc) {

    @Test
    @WithMockUser(roles = ["ADMIN"])
    fun requestProtectedUrlWithUser() {
        mvc.perform(MockMvcRequestBuilders.get("/"))
    }

}

Spring Security обеспечивает полную интеграцию с Spring MVC Test, и это также можно использовать при тестировании контроллеров с использованием среза @WebMvcTest и MockMvc.

Дополнительные сведения о поддержке тестирования Spring Security см. в справочной документации Spring Security здесь.

16.2. Структура @Configuration классов для включения в срезы тестов

Срезы тестов работают путём ограничения сканирования компонентов Spring Framework до ограниченного набора компонентов на основе их типа. Для любых бинов, которые не создаются через сканирование компонентов, например, бинов, созданных с использованием аннотации @Bean, срезы тестов не смогут включать/исключать их из контекста приложения. Рассмотрим пример:

import org.apache.commons.dbcp2.BasicDataSource;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration(proxyBeanMethods = false)
public class MyConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
        return http.build();
    }

    @Bean
    @ConfigurationProperties("app.datasource.second")
    public BasicDataSource secondDataSource() {
        return DataSourceBuilder.create().type(BasicDataSource.class).build();
    }

}

Для @WebMvcTest приложения с указанным выше классом @Configuration, вы могли бы ожидать наличия бинa SecurityFilterChain в контексте приложения, чтобы вы могли проверить, защищены ли правильно ваши контроллерные конечные точки. Однако MyConfiguration не обнаруживается фильтром сканирования компонентов @WebMvcTest, поскольку он не соответствует ни одному из типов, указанных в фильтре. Вы можете явно включить конфигурацию, аннотировав класс теста @Import(MyConfiguration.class). Это загрузит все бины в MyConfiguration, включая бин BasicDataSource, который не требуется при тестировании веб-уровня. Разделение класса конфигурации на два позволит импортировать только конфигурацию безопасности.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration(proxyBeanMethods = false)
public class MySecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
        return http.build();
    }

}
import org.apache.commons.dbcp2.BasicDataSource;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyDatasourceConfiguration {

    @Bean
    @ConfigurationProperties("app.datasource.second")
    public BasicDataSource secondDataSource() {
        return DataSourceBuilder.create().type(BasicDataSource.class).build();
    }

}

Использование одного класса конфигурации может быть неэффективным, когда бины определённого домена должны быть включены в срезы тестов. Вместо этого структурирование конфигурации приложения как нескольких гранулированных классов с бинами для определённого домена может позволить импортировать их только для конкретных срезов тестов.

17. Сборка

Spring Boot включает плагины сборки для Maven и Gradle. В этом разделе отвечены на часто задаваемые вопросы об этих плагинах.

17.1. Генерация информации о сборке

Плагин Maven и плагин Gradle позволяют генерировать информацию о сборке, содержащую координаты, имя и версию проекта. Плагины также могут быть настроены для добавления дополнительных свойств через конфигурацию. При наличии такого файла Spring Boot автоматически настраивает bean BuildProperties.

Для генерации информации о сборке с помощью Maven добавьте выполнение для цели build-info, как показано в следующем примере:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>3.1.3</version>
            <executions>
                <execution>
                    <goals>
                        <goal>build-info</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
Подробнее см. в документации плагина Spring Boot Maven.

Следующий пример делает то же самое с Gradle:

springBoot {
    buildInfo()
}
Подробнее см. в документации плагина Spring Boot Gradle.

17.2. Генерация информации из Git

И Maven, и Gradle позволяют генерировать файл git.properties, содержащий информацию о состоянии вашего репозитория исходного кода git во время сборки проекта.

Для пользователей Maven, в файле spring-boot-starter-parent POM включен предварительно настроенный плагин для генерации файла git.properties. Для его использования добавьте следующее объявление для Git Commit Id Plugin в ваш POM:

<build>
    <plugins>
        <plugin>
            <groupId>io.github.git-commit-id</groupId>
            <artifactId>git-commit-id-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Пользователи Gradle могут достичь того же результата, используя плагин gradle-git-properties, как показано в следующем примере:

plugins {
    id "com.gorylenko.gradle-git-properties" version "2.4.1"
}

Оба плагина Maven и Gradle позволяют настраивать свойства, включенные в git.properties.

Время коммита в git.properties должно соответствовать следующему формату: yyyy-MM-dd’T’HH:mm:ssZ. Это стандартный формат для обоих плагинов, указанных выше. Использование этого формата позволяет парсить время в Date и его формат, при сериализации в JSON, управлять настройками сериализации дат Jackson.

17.3. Настройка версий зависимостей

Файл spring-boot-dependencies POM управляет версиями общих зависимостей. Плагины Spring Boot для Maven и Gradle позволяют настраивать эти управляемые версии зависимостей с помощью свойств сборки.

Каждый выпуск Spring Boot разработан и протестирован с этим конкретным набором сторонних зависимостей. Изменение версий может привести к проблемам совместимости.

Для переопределения версий зависимостей с помощью Maven, см. данный раздел в документации плагина Maven.

Для переопределения версий зависимостей в Gradle, см. данный раздел в документации плагина Gradle.

17.4. Создание исполняемого JAR-файла с помощью Maven

spring-boot-maven-plugin может использоваться для создания исполняемого "fat" JAR. Если вы используете spring-boot-starter-parent POM, вы можете объявить плагин, и ваши JAR-файлы будут переупакованы следующим образом:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Если вы не используете родительский POM, вы все равно можете использовать плагин. Однако вам необходимо дополнительно добавить раздел <executions>, как показано ниже:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>3.1.3</version>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Полные детали использования см. в документации плагина.

17.5. Использование приложения Spring Boot в качестве зависимости

Как и WAR-файл, приложение Spring Boot не предназначено для использования в качестве зависимости. Если ваше приложение содержит классы, которые вы хотите использовать в других проектах, рекомендуется переместить этот код в отдельный модуль. Затем отдельный модуль можно использовать в вашем приложении и других проектах.

Если вы не можете перегруппировать свой код, как рекомендовано выше, плагины Spring Boot для Maven и Gradle должны быть настроены для создания отдельного артефакта, подходящего для использования в качестве зависимости. Исполняемый архив не может быть использован в качестве зависимости, так как формат исполняемого JAR-файла упаковывает классы приложения в BOOT-INF/classes. Это означает, что их нельзя найти, когда исполняемый JAR используется как зависимость.

Для создания двух артефактов, одного для использования в качестве зависимости и одного для выполнения, необходимо указать классификатор. Этот классификатор применяется к имени исполняемого архива, оставляя по умолчанию архив для использования в качестве зависимости.

Для настройки классификатора exec в Maven вы можете использовать следующую конфигурацию:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <classifier>exec</classifier>
            </configuration>
        </plugin>
    </plugins>
</build>

17.6. Извлечение определенных библиотек при запуске исполняемого JAR

Большинство вложенных библиотек в исполняемом JAR не нуждаются в распаковке для работы. Однако у некоторых библиотек могут возникнуть проблемы. Например, JRuby включает свою собственную поддержку вложенных JAR, предполагая, что jruby-complete.jar всегда непосредственно доступен в виде файла сам по себе.

Чтобы справиться с проблемами библиотек, вы можете отметить, что определенные вложенные JAR должны быть автоматически распакованы при первом запуске исполняемого JAR. Такие вложенные JAR пишутся в временной директории, определяемой системной переменной java.io.tmpdir.

Следует позаботиться о том, чтобы ваша операционная система была настроена так, чтобы не удалять распакованные JAR-файлы в временную директорию, пока приложение работает.

Например, чтобы указать, что JRuby должен быть помечен для распаковки с использованием плагина Maven, вы добавите следующую конфигурацию:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <requiresUnpack>
                    <dependency>
                        <groupId>org.jruby</groupId>
                        <artifactId>jruby-complete</artifactId>
                    </dependency>
                </requiresUnpack>
            </configuration>
        </plugin>
    </plugins>
</build>

17.7. Создание неисполняемого JAR с исключениями

Часто, если у вас есть исполняемый и неисполняемый JAR в качестве двух отдельных продуктов сборки, исполняемая версия имеет дополнительные конфигурационные файлы, которые не нужны в библиотечном JAR. Например, конфигурационный файл application.yaml может быть исключен из неисполняемого JAR.

В Maven, исполняемый JAR должен быть основным артефактом, и вы можете добавить классифицированный JAR для библиотеки следующим образом:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <plugin>
            <artifactId>maven-jar-plugin</artifactId>
            <executions>
                <execution>
                    <id>lib</id>
                    <phase>package</phase>
                    <goals>
                        <goal>jar</goal>
                    </goals>
                    <configuration>
                        <classifier>lib</classifier>
                        <excludes>
                            <exclude>application.yaml</exclude>
                        </excludes>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

17.8. Дистанционная отладка приложения Spring Boot, запущенного с Maven

Для подключения удаленного отладчика к приложению Spring Boot, запущенному с Maven, вы можете использовать свойство jvmArguments плагина Maven.

См. этот пример для получения более подробной информации.

17.9. Сборка исполняемого архива с помощью Ant без использования spring-boot-antlib

Для сборки с помощью Ant, вам необходимо получить зависимости, скомпилировать и затем создать JAR- или WAR-архив. Для того, чтобы сделать его исполняемым, вы можете либо использовать модуль spring-boot-antlib, либо следовать этим инструкциям:

  1. Если вы собираете JAR, упакуйте классы и ресурсы приложения в вложенную директорию BOOT-INF/classes. Если вы собираете WAR, упакуйте классы приложения в вложенную директорию WEB-INF/classes, как обычно.

  2. Добавьте зависимости времени выполнения в вложенную директорию BOOT-INF/lib для JAR или WEB-INF/lib для WAR. Не забудьте не сжимать записи в архиве.

  3. Добавьте зависимости provided (встроенного контейнера) в вложенную директорию BOOT-INF/lib для JAR или WEB-INF/lib-provided для WAR. Не забудьте не сжимать записи в архиве.

  4. Добавьте классы spring-boot-loader в корень архива (чтобы Main-Class был доступен).

  5. Используйте соответствующий загрузчик (например, JarLauncher для JAR-файла) в качестве атрибута Main-Class в манифесте и укажите необходимые свойства, как записи в манифесте — в основном, установив свойство Start-Class.

Следующий пример показывает, как собрать исполняемый архив с помощью Ant:

<target name="build" depends="compile">
    <jar destfile="target/${ant.project.name}-${spring-boot.version}.jar" compress="false">
        <mappedresources>
            <fileset dir="target/classes" />
            <globmapper from="*" to="BOOT-INF/classes/*"/>
        </mappedresources>
        <mappedresources>
            <fileset dir="src/main/resources" erroronmissingdir="false"/>
            <globmapper from="*" to="BOOT-INF/classes/*"/>
        </mappedresources>
        <mappedresources>
            <fileset dir="${lib.dir}/runtime" />
            <globmapper from="*" to="BOOT-INF/lib/*"/>
        </mappedresources>
        <zipfileset src="${lib.dir}/loader/spring-boot-loader-jar-${spring-boot.version}.jar" />
        <manifest>
            <attribute name="Main-Class" value="org.springframework.boot.loader.JarLauncher" />
            <attribute name="Start-Class" value="${start-class}" />
        </manifest>
    </jar>
</target>

18. Предварительная обработка

Возникает ряд вопросов при использовании предварительной обработки приложений Spring Boot. В этом разделе рассматриваются эти вопросы.

18.1. Условия

Предварительная обработка оптимизирует приложение и оценивает условия на основе среды во время сборки. Профили реализованы через условия и также на них влияют.

Если вам нужны компоненты, созданные на основе условия в оптимизированном приложении предварительной обработки, необходимо настроить среду при сборке приложения. Компоненты, созданные во время предварительной обработки на этапе сборки, всегда создаются при запуске приложения и не могут быть отключены. Для этого можно установить профили, которые должны использоваться при сборке приложения.

Для Maven это делается путем настройки конфигурации profiles выполнения spring-boot-maven-plugin:process-aot:

<profile>
    <id>native</id>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>process-aot</id>
                            <configuration>
                                <profiles>profile-a,profile-b</profiles>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</profile>

Для Gradle необходимо настроить задачу ProcessAot:

tasks.withType(org.springframework.boot.gradle.tasks.aot.ProcessAot).configureEach {
    args('--spring.profiles.active=profile-a,profile-b')
}

Профили, которые изменяют только свойства конфигурации, не влияющие на условия, поддерживаются без ограничений при запуске оптимизированных приложений предварительной обработки.

19. Традиционная развертывание

Spring Boot поддерживает традиционную развертку, а также более современные формы развертывания. Этот раздел отвечает на часто задаваемые вопросы о традиционной развертке.

19.1. Создание развертываемого файла WAR

Поскольку Spring WebFlux не строго зависит от API сервлетов, а приложения по умолчанию развертываются на встроенном сервере Reactor Netty, развертывание WAR для приложений WebFlux не поддерживается.

Первый шаг в создании развертываемого файла WAR — предоставить подкласс SpringBootServletInitializer и переопределить его метод configure. Это позволяет использовать поддержку сервлетов 3.0 Spring Framework и настраивать приложение при запуске контейнером сервлетов. Обычно, вы должны обновить главный класс вашего приложения, чтобы он расширял класс SpringBootServletInitializer, как показано в следующем примере:

Java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(MyApplication.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

}
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.runApplication
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer

@SpringBootApplication
class MyApplication : SpringBootServletInitializer() {

    override fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder {
        return application.sources(MyApplication::class.java)
    }

}

fun main(args: Array<String>) {
    runApplication<MyApplication>(*args)
}

Следующий шаг — обновить конфигурацию сборки таким образом, чтобы ваш проект генерировал файл WAR, а не JAR. Если вы используете Maven и spring-boot-starter-parent (который настраивает плагин Maven для создания WAR), вам нужно только изменить pom.xml, чтобы изменить упаковку на war, как показано ниже:

<packaging>war</packaging>

Если вы используете Gradle, вам нужно изменить build.gradle, чтобы применить плагин war к проекту, как показано ниже:

apply plugin: 'war'

Последний шаг — убедиться, что встроенный контейнер сервлетов не конфликтует с контейнером сервлетов, в который развертывается файл WAR. Для этого вам нужно пометить зависимость встроенного контейнера сервлетов как provided.

Если вы используете Maven, следующий пример отмечает контейнер сервлетов (Tomcat, в данном случае) как provided:

<dependencies>
    <!-- ... -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
    <!-- ... -->
</dependencies>

Если вы используете Gradle, следующий пример отмечает контейнер сервлетов (Tomcat, в данном случае) как provided:

dependencies {
    // ...
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
    // ...
}
Предпочтительно использовать способ маркировки зависимостей встроенного контейнера сервлетов как provided с помощью providedRuntime, а не конфигурацию compileOnly в Gradle. Среди прочих ограничений, зависимости compileOnly не находятся в пути к тестовым классам, поэтому любые веб-интеграционные тесты завершаются сбоем.

Если вы используете инструменты сборки Spring Boot, маркировка зависимости встроенного контейнера сервлетов как provided создает исполняемый файл WAR с зависимостями provided, упакованными в каталог lib-provided. Это означает, что, помимо развертывания в контейнер сервлетов, вы также можете запустить ваше приложение, используя java -jar в командной строке.

19.2. Преобразование существующего приложения в Spring Boot

Для преобразования существующего не-веб-приложения Spring в приложение Spring Boot замените код, который создаёт ваш ApplicationContext, вызовами SpringApplication или SpringApplicationBuilder. Веб-приложения Spring MVC обычно можно сначала преобразовать в развертываемое приложение war, а затем позже переместить его в исполняемый war или jar. См. Руководство по началу работы по преобразованию jar в war.

Для создания развертываемого файла WAR, расширив класс SpringBootServletInitializer (например, в классе с именем Application) и добавив аннотацию Spring Boot @SpringBootApplication, используйте код, подобный следующему примеру:

Java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        // Customize the application or call application.sources(...) to add sources
        // Since our example is itself a @Configuration class (through
        // @SpringBootApplication)
        // we actually do not need to override this method.
        return application;
    }


}
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.runApplication
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer

@SpringBootApplication
class MyApplication : SpringBootServletInitializer() {

    override fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder {
        // Customize the application or call application.sources(...) to add sources
        // Since our example is itself a @Configuration class (through @SpringBootApplication)
        // we actually do not need to override this method.
        return application
    }

}

Помните, что всё, что вы помещаете в sources, представляет собой всего лишь Spring ApplicationContext. Как правило, всё, что уже работает, должно работать и здесь. Возможно, некоторые бинны можно удалить позже, и Spring Boot предоставит свои собственные значения по умолчанию, но, скорее всего, можно получить работающий результат, прежде чем нужно будет что-то менять.

Статические ресурсы можно перенести в /public (или /static или /resources или /META-INF/resources) в корне пути к классам. То же самое относится к messages.properties (который Spring Boot автоматически обнаруживает в корне пути к классам).

Простая интеграция Spring DispatcherServlet и Spring Security не потребует дополнительных изменений. Если в вашем приложении используются другие функции (например, другие сервлеты или фильтры), вам может потребоваться добавить некоторую конфигурацию в контекст Application, заменив соответствующие элементы из web.xml, как показано ниже:

  • @Bean типа Servlet или ServletRegistrationBean устанавливает этот бин в контейнер так, как если бы это был <servlet/> и <servlet-mapping/> в web.xml.

  • @Bean типа Filter или FilterRegistrationBean ведёт себя аналогично (как <filter/> и <filter-mapping/>).

  • ApplicationContext в файле XML можно добавить с помощью @ImportResource в вашем Application. Кроме того, случаи, где аннотациями используется уже большая часть конфигурации, можно переделать в несколько строк как определения @Bean.

После того, как файл WAR начнёт работать, вы можете сделать его исполняемым, добавив метод main в ваш класс Application, как показано в следующем примере:

Java
public static void main(String[] args) {
    SpringApplication.run(MyApplication.class, args);
}
Kotlin
fun main(args: Array<String>) {
    runApplication<MyApplication>(*args)
}

Если вы хотите запускать приложение как war, так и как исполняемое приложение, вам нужно разделить настройки билдера в методе, который доступен как в callback SpringBootServletInitializer, так и в методе main в классе, подобном следующему:

Java
import org.springframework.boot.Banner;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return customizerBuilder(builder);
    }

    public static void main(String[] args) {
        customizerBuilder(new SpringApplicationBuilder()).run(args);
    }

    private static SpringApplicationBuilder customizerBuilder(SpringApplicationBuilder builder) {
        return builder.sources(MyApplication.class).bannerMode(Banner.Mode.OFF);
    }

}
Kotlin
import org.springframework.boot.Banner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer

@SpringBootApplication
class MyApplication : SpringBootServletInitializer() {

    override fun configure(builder: SpringApplicationBuilder): SpringApplicationBuilder {
        return customizerBuilder(builder)
    }

    companion object {

        @JvmStatic
        fun main(args: Array<String>) {
            customizerBuilder(SpringApplicationBuilder()).run(*args)
        }

        private fun customizerBuilder(builder: SpringApplicationBuilder): SpringApplicationBuilder {
            return builder.sources(MyApplication::class.java).bannerMode(Banner.Mode.OFF)
        }

    }

}

Приложения могут относиться к нескольким категориям:

  • Приложения Servlet 3.0+ без web.xml.

  • Приложения с web.xml.

  • Приложения с иерархией контекстов.

  • Приложения без иерархии контекстов.

Все они должны поддаваться преобразованию, но для каждого из них могут потребоваться немного отличающиеся методы.

Приложения Servlet 3.0+ могут преобразовываться довольно легко, если они уже используют поддержку Spring Servlet 3.0+ инициализаторов классов. Обычно, весь код из существующего WebApplicationInitializer можно перенести в SpringBootServletInitializer. Если у вашего существующего приложения есть более одного ApplicationContext (например, если оно использует AbstractDispatcherServletInitializer), то, возможно, вы сможете объединить все источники контекста в один SpringApplication. Основная сложность может возникнуть, если объединение не работает, и вам нужно сохранить иерархию контекстов. См. запись о создании иерархии для примеров. Существующий родительский контекст, содержащий веб-специфические функции, обычно необходимо разбить таким образом, чтобы все компоненты ServletContextAware были в дочернем контексте.

Приложения, которые не являются приложениями Spring, могут быть преобразованы в приложения Spring Boot, и вышеупомянутые рекомендации могут помочь. Однако, могут возникнуть проблемы. В этом случае мы рекомендуем задать вопросы на Stack Overflow с тегом spring-boot.

19.3. Развертывание WAR в WebLogic

Для развертывания приложения Spring Boot в WebLogic необходимо убедиться, что ваш инициализатор сервлетов прямо реализует интерфейс WebApplicationInitializer (даже если вы наследуете от базового класса, который его уже реализует).

Типичный инициализатор для WebLogic должен быть похожим на следующий пример:

Java
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.web.WebApplicationInitializer;

@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer implements WebApplicationInitializer {

}
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer
import org.springframework.web.WebApplicationInitializer

@SpringBootApplication
class MyApplication : SpringBootServletInitializer(), WebApplicationInitializer

Если вы используете Logback, вам также нужно указать WebLogic, чтобы он предпочитал упакованную версию, а не предварительно установленную версию с сервера. Вы можете сделать это, добавив файл WEB-INF/weblogic.xml со следующим содержимым:

<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app
    xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        https://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd
        http://xmlns.oracle.com/weblogic/weblogic-web-app
        https://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd">
    <wls:container-descriptor>
        <wls:prefer-application-packages>
            <wls:package-name>org.slf4j</wls:package-name>
        </wls:prefer-application-packages>
    </wls:container-descriptor>
</wls:weblogic-web-app>

20. Docker Compose

Этот раздел включает темы, связанные с поддержкой Docker Compose в Spring Boot.

20.1. Настройка JDBC URL

При использовании JdbcConnectionDetails с Docker Compose, параметры JDBC URL можно настроить, применив метку org.springframework.boot.jdbc.parameters к сервису. Например:

services:
  postgres:
    image: 'postgres:15.3'
    environment:
      - 'POSTGRES_USER=myuser'
      - 'POSTGRES_PASSWORD=secret'
      - 'POSTGRES_DB=mydb'
    ports:
      - '5432:5432'
    labels:
      org.springframework.boot.jdbc.parameters: 'ssl=true&sslmode=require'

С этим файлом Docker Compose, используемый JDBC URL будет jdbc:postgresql://127.0.0.1:5432/mydb?ssl=true&sslmode=require.

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/howto.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API