Основные возможности
Этот раздел углубляется в детали Spring Boot. Здесь вы можете узнать о ключевых функциях, которые вы можете использовать и настраивать. Если вы ещё этого не сделали, вам следует прочитать разделы "Начало работы" и "Разработка с Spring Boot", чтобы иметь хорошее понимание основ.
1. SpringApplication
Класс SpringApplication предоставляет удобный способ запустить Spring-приложение, которое стартует из метода main(). Во многих ситуациях вы можете использовать статический метод SpringApplication.run, как показано в следующем примере:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
При запуске приложения вы должны увидеть вывод, похожий на следующий:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.1.3) 2023-08-24T09:33:22.083Z INFO 37822 --- [ main] o.s.b.d.f.logexample.MyApplication : Starting MyApplication using Java 17.0.8 with PID 37822 (/opt/apps/myapp.jar started by myuser in /opt/apps/) 2023-08-24T09:33:22.088Z INFO 37822 --- [ main] o.s.b.d.f.logexample.MyApplication : No active profile set, falling back to 1 default profile: "default" 2023-08-24T09:33:23.339Z INFO 37822 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2023-08-24T09:33:23.354Z INFO 37822 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2023-08-24T09:33:23.355Z INFO 37822 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.12] 2023-08-24T09:33:23.485Z INFO 37822 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2023-08-24T09:33:23.488Z INFO 37822 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1308 ms 2023-08-24T09:33:23.989Z INFO 37822 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2023-08-24T09:33:24.000Z INFO 37822 --- [ main] o.s.b.d.f.logexample.MyApplication : Started MyApplication in 2.424 seconds (process running for 2.804)
По умолчанию отображаются сообщения журнала INFO, включая некоторые важные детали запуска, такие как пользователь, который запустил приложение. Если вам нужен уровень журнала, отличный от INFO, вы можете его установить, как описано в Уровни журналов. Версия приложения определяется с помощью версии реализации из пакета основного класса приложения. Ведение журнала информации о запуске можно отключить, установив spring.main.log-startup-info в значение false. Это также отключит ведение журнала активных профилей приложения.
Чтобы добавить дополнительные журналы во время запуска, вы можете переопределить logStartupInfo(boolean) в подклассе SpringApplication. |
1.1. Ошибка запуска
Если ваше приложение не может запуститься, зарегистрированные FailureAnalyzers получают возможность предоставить специальное сообщение об ошибке и конкретное действие для устранения проблемы. Например, если вы запускаете веб-приложение на порту 8080, а этот порт уже используется, вы увидите сообщение, подобное следующему:
*************************** APPLICATION FAILED TO START *************************** Description: Embedded servlet container failed to start. Port 8080 was already in use. Action: Identify and stop the process that is listening on port 8080 or configure this application to listen on another port.
Spring Boot предоставляет множество реализаций FailureAnalyzer, и вы можете добавить свои собственные. |
Если ни один из анализаторов сбоев не может обработать исключение, вы все равно можете отобразить полный отчет об условиях, чтобы лучше понять, что пошло не так. Для этого необходимо включить свойство debug или включить ведение журнала DEBUG для org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener.
Например, если вы запускаете приложение с помощью java -jar, вы можете включить свойство debug следующим образом:
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug 1.2. Ленивая инициализация
SpringApplication позволяет лениво инициализировать приложение. При включенной ленивой инициализации бины создаются по мере необходимости, а не во время запуска приложения. В результате включение ленивой инициализации может сократить время запуска вашего приложения. В веб-приложении включение ленивой инициализации приведет к тому, что многие веб-бины не будут инициализированы до получения HTTP-запроса.
Недостатком ленивой инициализации является то, что она может отложить обнаружение проблемы с приложением. Если бину с неправильной настройкой будет проведена ленивая инициализация, ошибка больше не произойдет во время запуска, и проблема проявится только при инициализации бина. Также необходимо позаботиться о том, чтобы у JVM было достаточно памяти для размещения всех бинов приложения, а не только тех, которые инициализируются во время запуска. По этим причинам ленивая инициализация по умолчанию не включена, и рекомендуется произвести настройку размера кучи JVM перед включением ленивой инициализации.
Ленивую инициализацию можно включить программно, используя метод lazyInitialization для SpringApplicationBuilder или метод setLazyInitialization для SpringApplication. Кроме того, ее можно включить, используя свойство spring.main.lazy-initialization, как показано в следующем примере:
spring.main.lazy-initialization=true spring:
main:
lazy-initialization: true Если вы хотите отключить ленивую инициализацию для определенных бинов, используя ленивую инициализацию для остальной части приложения, вы можете явно установить их атрибут lazy в false, используя аннотацию @Lazy(false). |
1.3. Настройка баннера
Баннер, который печатается при запуске, может быть изменён путем добавления файла banner.txt в ваш класспатический путь или установкой свойства spring.banner.location для указания местоположения такого файла. Если файл имеет кодировку, отличную от UTF-8, вы можете установить spring.banner.charset.
Внутри вашего файла banner.txt вы можете использовать любой ключ, доступный в Environment, а также следующие плейсхолдеры:
| Переменная | Описание |
|---|---|
| Номер версии вашего приложения, как объявлен в |
| Номер версии вашего приложения, как объявлен в |
| Версия Spring Boot, которую вы используете. Например |
| Версия Spring Boot, которую вы используете, отформатированная для отображения (в скобках и с префиксом |
| Где |
| Название вашего приложения, как объявлено в |
Метод SpringApplication.setBanner(…) может быть использован, если вам нужно сгенерировать баннер программно. Используйте интерфейс org.springframework.boot.Banner и реализуйте свой собственный метод printBanner(). |
Вы также можете использовать свойство spring.main.banner-mode, чтобы определить, должен ли баннер печататься при запуске (console), отправляться в настроенный логгер (log) или вообще не отображаться (off).
Напечатанный баннер регистрируется как одиночный бин под следующим именем: springBootBanner.
| Свойства Вот почему мы рекомендуем всегда запускать распакованные jar-файлы с помощью |
1.4. Настройка SpringApplication
Если значения по умолчанию SpringApplication не подходят, вы можете создать локальный экземпляр и настроить его. Например, чтобы отключить баннер, вы можете написать:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args) {
setBannerMode(Banner.Mode.OFF)
}
}
Аргументы конструктора, передаваемые в SpringApplication, являются источниками конфигурации для Spring-бинов. В большинстве случаев это ссылки на классы @Configuration, но это также могут быть прямые ссылки на классы @Component. |
Также возможно настроить SpringApplication, используя файл application.properties. Подробности см. в разделе Внешняя конфигурация.
Полный список параметров конфигурации см. в SpringApplication Javadoc.
1.5. API гибкого создания
Если вам нужно создать иерархию (несколько контекстов с родительско-дочерними отношениями) или вы предпочитаете использовать API гибкого создания, вы можете использовать SpringApplicationBuilder.
SpringApplicationBuilder позволяет объединить несколько вызовов методов и включает методы parent и child, которые позволяют создавать иерархию, как показано в следующем примере:
new SpringApplicationBuilder().sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
SpringApplicationBuilder()
.sources(Parent::class.java)
.child(Application::class.java)
.bannerMode(Banner.Mode.OFF)
.run(*args)
Существуют некоторые ограничения при создании иерархии ApplicationContext. Например, веб-компоненты обязательно должны находиться в дочернем контексте, и один и тот же Environment используется для родительского и дочернего контекстов. Для получения полной информации см. SpringApplicationBuilder Javadoc. |
1.6. Доступность приложения
При развертывании на платформах приложения могут предоставлять информацию о своей доступности платформе, используя инфраструктуру, такую как Пробы Kubernetes. Spring Boot включает встроенную поддержку распространенных состояний доступности «жизнеспособности» и «готовности». Если вы используете поддержку «актуатора» Spring Boot, эти состояния экспонируются как группы конечных точек состояния.
Кроме того, вы также можете получить состояния доступности, внедрив интерфейс ApplicationAvailability в свои собственные бины.
1.6.1. Состояние жизнеспособности
Состояние «жизнеспособности» приложения указывает, позволяет ли его внутреннее состояние работать корректно или восстановиться само по себе, если оно в настоящее время не работает. Сломанное состояние «жизнеспособности» означает, что приложение находится в состоянии, из которого оно не может восстановиться, и инфраструктура должна перезапустить приложение.
| В целом, состояние «жизнеспособности» не должно основываться на внешних проверках, таких как Проверки состояния. В противном случае сбои внешней системы (базы данных, веб-API, внешнего кэша) приведут к массовым перезапускам и каскадным сбоям на всей платформе. |
Внутреннее состояние приложений Spring Boot в основном представлено контекстом Spring ApplicationContext. Если контекст приложения успешно запущен, Spring Boot предполагает, что приложение находится в валидном состоянии. Приложение считается живым, как только контекст был обновлён, см. Жизненный цикл приложения Spring Boot и связанные события приложения.
1.6.2. Состояние готовности
Состояние «готовности» приложения указывает, готово ли приложение обрабатывать трафик. Сбой состояния «готовности» сообщает платформе, что в настоящее время не следует направлять трафик в приложение. Это обычно происходит во время запуска, в то время как компоненты CommandLineRunner и ApplicationRunner обрабатываются, или в любое время, если приложение решает, что оно слишком занято для дополнительного трафика.
Приложение считается готовым, как только будут вызваны приложение и исполняемые файлы командной строки, см. Жизненный цикл приложения Spring Boot и связанные события приложения.
Задачи, ожидаемые во время запуска, должны выполняться компонентами CommandLineRunner и ApplicationRunner, а не с использованием обратных вызовов жизненного цикла компонента Spring, таких как @PostConstruct. |
1.6.3. Управление состоянием доступности приложения
Компоненты приложения могут извлекать текущее состояние доступности в любое время, внедрив интерфейс ApplicationAvailability и вызвав методы на нём. Чаще всего приложения будут следить за обновлениями состояния или обновлять состояние приложения.
Например, мы можем экспортировать состояние «готовности» приложения в файл, чтобы «exec Probe» Kubernetes мог просмотреть этот файл:
@Component
public class MyReadinessStateExporter {
@EventListener
public void onStateChange(AvailabilityChangeEvent<ReadinessState> event) {
switch (event.getState()) {
case ACCEPTING_TRAFFIC:
// create file /tmp/healthy
break;
case REFUSING_TRAFFIC:
// remove file /tmp/healthy
break;
}
}
}
@Component
class MyReadinessStateExporter {
@EventListener
fun onStateChange(event: AvailabilityChangeEvent<ReadinessState?>) {
when (event.state) {
ReadinessState.ACCEPTING_TRAFFIC -> {
// create file /tmp/healthy
}
ReadinessState.REFUSING_TRAFFIC -> {
// remove file /tmp/healthy
}
else -> {
// ...
}
}
}
}
Мы также можем обновить состояние приложения, когда приложение выходит из строя и не может восстановиться:
@Component
public class MyLocalCacheVerifier {
private final ApplicationEventPublisher eventPublisher;
public MyLocalCacheVerifier(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void checkLocalCache() {
try {
// ...
}
catch (CacheCompletelyBrokenException ex) {
AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN);
}
}
}
@Component
class MyLocalCacheVerifier(private val eventPublisher: ApplicationEventPublisher) {
fun checkLocalCache() {
try {
// ...
} catch (ex: CacheCompletelyBrokenException) {
AvailabilityChangeEvent.publish(eventPublisher, ex, LivenessState.BROKEN)
}
}
}
Spring Boot предоставляет Пробы HTTP Kubernetes для "жизнеспособности" и "готовности" с конечными точками состояния Актуатора. Более подробную информацию о развертывании приложений Spring Boot на Kubernetes можно найти в соответствующем разделе.
1.7. События и слушатели приложения
Помимо обычных событий Spring Framework, таких как ContextRefreshedEvent, приложение Spring Boot также генерирует дополнительные события приложения.
| Некоторые события фактически срабатывают до создания Если вы хотите, чтобы эти слушатели регистрировались автоматически независимо от того, как создается приложение, вы можете добавить файл org.springframework.context.ApplicationListener=com.example.project.MyListener |
События приложения отправляются в следующем порядке, поскольку ваше приложение выполняется:
-
Отправляется событие
ApplicationStartingEventв начале выполнения, но до обработки, за исключением регистрации слушателей и инициализаторов. -
Отправляется событие
ApplicationEnvironmentPreparedEvent, когда контекстEnvironment, который будет использоваться в контексте, известен, но до создания контекста. -
Отправляется событие
ApplicationContextInitializedEvent, когда контекстApplicationContextподготовлен, вызваны ApplicationContextInitializers, но до загрузки каких-либо определений бинов. -
Отправляется событие
ApplicationPreparedEventнепосредственно перед началом обновления, но после загрузки определений бинов. -
Отправляется событие
ApplicationStartedEventпосле обновления контекста, но до вызова приложения и исполняемых файлов командной строки. -
Отправляется событие
AvailabilityChangeEventсразу после сLivenessState.CORRECT, чтобы указать, что приложение считается живым. -
Отправляется событие
ApplicationReadyEventпосле вызова приложения и исполняемых файлов командной строки. -
Отправляется событие
AvailabilityChangeEventсразу после сReadinessState.ACCEPTING_TRAFFIC, чтобы указать, что приложение готово обслуживать запросы. -
Отправляется событие
ApplicationFailedEvent, если при запуске возникла ошибка.
В приведенном выше списке указаны только события SpringApplicationEvent, которые связаны с SpringApplication. Кроме этих событий, также публикуются следующие события после ApplicationPreparedEvent и до ApplicationStartedEvent:
-
Отправляется событие
WebServerInitializedEventпосле готовностиWebServer.ServletWebServerInitializedEventиReactiveWebServerInitializedEventявляются соответственно вариантами для сервлетной и реактивной моделей. -
Отправляется событие
ContextRefreshedEventпри обновленииApplicationContext.
| Часто использование событий приложения не требуется, но полезно знать, что они существуют. Внутренне Spring Boot использует события для обработки различных задач. |
| Слушатели событий не должны запускать потенциально длительные задачи, так как они по умолчанию выполняются в том же потоке. Вместо этого используйте приложение и исполняемые файлы командной строки. |
События приложения отправляются с использованием механизма публикации событий Spring Framework. Часть этого механизма гарантирует, что событие, опубликованное для слушателей в дочернем контексте, также публикуется для слушателей во всех родительских контекстах. В результате, если ваше приложение использует иерархию экземпляров SpringApplication, слушатель может получить несколько экземпляров одного типа события приложения.
Чтобы слушатель мог отличать событие для своего контекста от события для дочернего контекста, он должен запросить внедрение своего контекста приложения, а затем сравнить внедренный контекст с контекстом события. Контекст можно внедрить, реализовав интерфейс ApplicationContextAware или, если слушатель является бином, используя @Autowired.
1.8. Веб-среда
Spring Boot пытается создать правильный тип веб-сервлета от вашего имени. Алгоритм определения веб-сервлета следующий:
-
Если присутствует Spring MVC, используется Spring MVC веб-сервлет.
-
Если Spring MVC отсутствует, а Spring WebFlux присутствует, используется Spring WebFlux веб-сервлет.
-
В противном случае, используется встроенный веб-сервлет.
Это означает, что если вы используете Spring MVC и новый веб-сервлет Spring WebFlux в одном приложении, Spring MVC будет использоваться по умолчанию. Вы можете легко переопределить это, вызвав соответствующий метод.
Также возможно полностью контролировать тип веб-сервлета, вызвав нужный метод.
| Часто желательно вызвать соответствующий метод, когда Spring MVC используется в JUnit тесте. |
1.9. Доступ к аргументам приложения
Если вам нужно получить доступ к аргументам приложения, переданным в приложение, вы можете инжектировать бины. Интерфейс позволяет получить доступ к исходным аргументам, а также к обработанным аргументам и параметрам, как показано в следующем примере:
@Component
public class MyBean {
public MyBean(ApplicationArguments args) {
boolean debug = args.containsOption("debug");
List<String> files = args.getNonOptionArgs();
if (debug) {
System.out.println(files);
}
// if run with "--debug logfile.txt" prints ["logfile.txt"]
}
}
@Component
class MyBean(args: ApplicationArguments) {
init {
val debug = args.containsOption("debug")
val files = args.nonOptionArgs
if (debug) {
println(files)
}
// if run with "--debug logfile.txt" prints ["logfile.txt"]
}
}
| Spring Boot также регистрирует бины с аргументами приложения в Spring контексте. Это позволяет инжектировать отдельные аргументы приложения с помощью аннотации. |
1.10. Использование ApplicationRunner или CommandLineRunner
Если вам нужно выполнить определенный код после запуска приложения, вы можете реализовать интерфейсы или . Оба интерфейса работают одинаково и предлагают метод, который вызывается непосредственно перед завершением запуска приложения.
| Этот интерфейс хорошо подходит для задач, которые должны выполняться после запуска приложения, но перед началом обработки трафика. |
Интерфейс предоставляет доступ к аргументам приложения в виде массива строк, а интерфейс использует интерфейс, обсуждавшийся ранее. Следующий пример демонстрирует класс, реализующий метод:
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) {
// Do something...
}
}
@Component
class MyCommandLineRunner : CommandLineRunner {
override fun run(vararg args: String) {
// Do something...
}
}
Если определено несколько бинов или, которые должны вызываться в определенном порядке, вы можете дополнительно реализовать интерфейс или использовать аннотацию.
1.11. Выход из приложения
Каждое приложение регистрирует обработчик завершения работы с JVM, чтобы гарантировать, что приложение закрывается корректно при выходе. Все стандартные колбеки жизненного цикла Spring (такие как интерфейс или аннотация) могут быть использованы.
Кроме того, бины могут реализовать интерфейс, если они хотят возвратить определенный код выхода при вызове метода. Этот код выхода может быть передан методу для возврата в качестве кода состояния, как показано в следующем примере:
@SpringBootApplication
public class MyApplication {
@Bean
public ExitCodeGenerator exitCodeGenerator() {
return () -> 42;
}
public static void main(String[] args) {
System.exit(SpringApplication.exit(SpringApplication.run(MyApplication.class, args)));
}
}
@SpringBootApplication
class MyApplication {
@Bean
fun exitCodeGenerator() = ExitCodeGenerator { 42 }
}
fun main(args: Array<String>) {
exitProcess(SpringApplication.exit(
runApplication<MyApplication>(*args)))
}
Также интерфейс может быть реализован исключениями. Когда возникает такое исключение, Spring Boot возвращает код выхода, предоставленный реализованным методом.
Если существует более одного генератора кода выхода, используется первый ненулевой код выхода. Для управления порядком вызова генераторов дополнительно реализуйте интерфейс или используйте аннотацию.
1.12. Функции администратора
Возможно включить функции администрирования для приложения, задав свойство. Это экспонирует интерфейс на платформе. Вы можете использовать эту функцию для удаленного администрирования приложения Spring Boot. Эта функция может быть полезна для любой реализации обертки сервиса.
| Если вы хотите узнать, на каком HTTP порту запущено приложение, получите свойство с ключом. |
1.13. Отслеживание запуска приложения
Во время запуска приложения и выполняют множество задач, связанных с жизненным циклом приложения, жизненным циклом бинов или даже обработкой событий приложения. С Spring Framework позволяет отслеживать последовательность запуска приложения с помощью объектов. Эти данные могут собираться для профилирования или для лучшего понимания процесса запуска приложения.
Вы можете выбрать реализацию при настройке экземпляра. Например, для использования , вы можете написать:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args);
}
}
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args) {
applicationStartup = BufferingApplicationStartup(2048)
}
}
Первая доступная реализация предоставляется Spring Framework. Она добавляет события, специфичные для Spring, в сессию Java Flight Recorder и предназначена для профилирования приложений и сопоставления их жизненного цикла контекста Spring с событиями JVM (такими как выделение памяти, сборка мусора, загрузка классов…). После настройки вы можете записывать данные, запуская приложение с включенным Flight Recorder:
$ java -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar demo.jar Spring Boot поставляется с вариантом; эта реализация предназначена для буферизации этапов запуска и их перенаправления в внешнюю систему метрик. Приложения могут запросить бин типа в любом компоненте.
Spring Boot также можно настроить для экспонирования конечной точки, которая предоставляет эту информацию в формате JSON.
2. Внешняя конфигурация
Spring Boot позволяет вам внешне задавать конфигурацию, так что вы можете работать с одним и тем же кодом приложения в разных средах. Вы можете использовать различные внешние источники конфигурации, включая файлы свойств Java, файлы YAML, переменные окружения и аргументы командной строки.
Значения свойств можно напрямую внедрять в ваши компоненты с помощью аннотации @Value, получать доступ через абстракцию Spring’s Environment или привязывать к структурированным объектам через @ConfigurationProperties.
Spring Boot использует очень специфический порядок PropertySource, разработанный для разумного переопределения значений. Поздние источники свойств могут переопределять значения, определенные в более ранних. Источники рассматриваются в следующем порядке:
-
Значения по умолчанию (указанные путем установки
SpringApplication.setDefaultProperties). -
@PropertySourceаннотации на ваших@Configurationклассах. Обратите внимание, что такие источники свойств не добавляются вEnvironment, пока контекст приложения не будет обновлён. Это происходит слишком поздно для настройки определённых свойств, таких какlogging.*иspring.main.*, которые считываются до начала обновления. -
Данные конфигурации (например, файлы
application.properties). -
RandomValuePropertySource, содержащий свойства только вrandom.*. -
Переменные среды ОС.
-
Свойства системы Java (
System.getProperties()). -
Атрибуты JNDI из
java:comp/env. -
Параметры инициализации
ServletContext. -
Параметры инициализации
ServletConfig. -
Свойства из
SPRING_APPLICATION_JSON(встроенный JSON в переменной среды или свойстве системы). -
Аргументы командной строки.
-
propertiesатрибут в ваших тестах. Доступен в@SpringBootTestи аннотациях для тестирования конкретного фрагмента вашего приложения. -
@DynamicPropertySourceаннотации в ваших тестах. -
@TestPropertySourceаннотации в ваших тестах. -
Глобальные настройки Devtools в каталоге
$HOME/.config/spring-boot, когда активен devtools.
Файлы данных конфигурации рассматриваются в следующем порядке:
-
Свойства приложения упакованные внутри вашего jar (варианты
application.propertiesи YAML). -
Свойства приложения, специфичные для профиля упакованные внутри вашего jar (варианты
application-{profile}.propertiesи YAML). -
Свойства приложения вне вашего упакованного jar (варианты
application.propertiesи YAML). -
Свойства приложения, специфичные для профиля вне вашего упакованного jar (варианты
application-{profile}.propertiesи YAML).
Рекомендуется придерживаться одного формата для всего приложения. Если у вас есть файлы конфигурации с форматом .properties и YAML в одном месте, формат .properties имеет приоритет. |
Если вы используете переменные среды вместо свойств системы, большинство операционных систем не позволяют имена ключей с точками, но вы можете использовать подчёркивания вместо (например, SPRING_CONFIG_NAME вместо spring.config.name). Подробнее см. Привязка из переменных среды. |
Если ваше приложение работает в контейнере Servlet или сервере приложений, можно использовать свойства JNDI (в java:comp/env) или параметры инициализации контекста Servlet вместо или дополнительно к переменным среды или свойствам системы. |
В качестве конкретного примера, предположим, что вы разрабатываете приложение @Component, которое использует свойство name, как показано в следующем примере:
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}
@Component
class MyBean {
@Value("\${name}")
private val name: String? = null
// ...
}
В вашем классе приложения (например, внутри вашего jar) может быть файл application.properties, который предоставляет разумное значение свойства по умолчанию для name. При запуске в новой среде можно предоставить файл application.properties вне вашего jar, который переопределит name. Для разовых тестов вы можете запустить приложение с определенным ключом командной строки (например, java -jar app.jar --name="Spring").
Конечные точки env и configprops могут быть полезны для определения причины, по которой свойство имеет определённое значение. Вы можете использовать эти две конечные точки для диагностики неожиданных значений свойств. Подробнее см. раздел "Готовые к использованию в производстве функции". |
2.1. Доступ к свойствам командной строки
По умолчанию, SpringApplication преобразует любые аргументы командной строки (т.е., аргументы, начинающиеся с --, такие как --server.port=9000) в свойства и добавляет их к Spring Environment. Как упоминалось ранее, свойства командной строки всегда имеют приоритет над источниками свойств, основанными на файлах.
Если вы не хотите, чтобы свойства командной строки добавлялись в Environment, вы можете отключить их с помощью SpringApplication.setAddCommandLineProperties(false).
2.2. Свойства приложения в формате JSON
Переменные окружения и свойства системы часто имеют ограничения, из-за которых некоторые имена свойств нельзя использовать. Чтобы помочь с этим, Spring Boot позволяет вам закодировать блок свойств в одну JSON-структуру.
При запуске вашего приложения, все spring.application.json или SPRING_APPLICATION_JSON свойства будут обработаны и добавлены в Environment.
Например, свойство SPRING_APPLICATION_JSON можно передать в командной строке в оболочке UN*X как переменную окружения:
$ SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar В приведенном примере вы получите my.name=test в Spring Environment.
Тот же JSON также можно передать в качестве свойства системы:
$ java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar Или вы можете передать JSON с помощью аргумента командной строки:
$ java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}' Если вы развертываете приложение на классическом сервере приложений, вы также можете использовать переменную JNDI с именем java:comp/env/spring.application.json.
Хотя значения null из JSON будут добавлены в результирующий источник свойств, PropertySourcesPropertyResolver обрабатывает null свойства как отсутствующие значения. Это означает, что JSON не может переопределить свойства из источников свойств более низкого порядка с помощью значения null. |
2.3. Внешние свойства приложения
Spring Boot автоматически найдет и загрузит файлы application.properties и application.yaml из следующих расположений при запуске приложения:
-
Из класса
-
Корень класса
-
Пакет класса
/config
-
-
Из текущей директории
-
Текущая директория
-
Поддиректория
config/в текущей директории -
Непосредственные поддиректории поддиректории
config/
-
Список упорядочен по приоритету (значения из элементов ниже переопределяют предыдущие). Документы из загруженных файлов добавляются как PropertySources в Spring Environment.
Если вам не нравится имя файла конфигурации application, вы можете переключиться на другое имя, указав свойство среды spring.config.name. Например, чтобы найти файлы myproject.properties и myproject.yaml, вы можете запустить приложение следующим образом:
$ java -jar myproject.jar --spring.config.name=myproject Вы также можете указать явное расположение, используя свойство среды spring.config.location. Это свойство принимает список, разделенный запятыми, одного или нескольких расположений для проверки.
Следующий пример показывает, как указать два разных файла:
$ java -jar myproject.jar --spring.config.location=\
optional:classpath:/default.properties,\
optional:classpath:/override.properties Используйте префикс optional:, если расположения являются необязательными и вы не возражаете, если они не существуют. |
spring.config.name, spring.config.location, и spring.config.additional-location используются очень рано, чтобы определить, какие файлы нужно загрузить. Они должны быть определены как свойство среды (обычно переменная среды ОС, системная переменная или аргумент командной строки). |
Если spring.config.location содержит каталоги (в отличие от файлов), они должны заканчиваться на /. Во время выполнения к ним будут добавлены имена, сгенерированные из spring.config.name, прежде чем они будут загружены. Файлы, указанные в spring.config.location, импортируются напрямую.
Значения расположения каталогов и файлов также расширяются для проверки файлов, специфичных для профиля. Например, если у вас есть spring.config.location classpath:myconfig.properties, будут также загружены соответствующие файлы classpath:myconfig-<profile>.properties. |
В большинстве случаев каждый элемент spring.config.location, который вы добавляете, будет ссылаться на один файл или каталог. Расположения обрабатываются в том порядке, в котором они определены, и последующие могут переопределять значения предыдущих.
Если у вас сложная настройка расположений и вы используете файлы конфигурации, специфичные для профиля, вам может потребоваться предоставить дополнительные подсказки, чтобы Spring Boot знал, как их сгруппировать. Группа расположений — это набор расположений, которые рассматриваются на одном уровне. Например, вы можете сгруппировать все расположения класса, а затем все внешние расположения. Элементы в группе расположений должны быть разделены символом ;. Подробности см. в разделе «Файлы, специфичные для профиля».
Расположения, настроенные с помощью spring.config.location, заменяют стандартные расположения. Например, если spring.config.location настроено со значением optional:classpath:/custom-config/,optional:file:./custom-config/, полный набор рассматриваемых расположений:
-
optional:classpath:custom-config/ -
optional:file:./custom-config/
Если вы предпочитаете добавить дополнительные расположения вместо их замены, вы можете использовать spring.config.additional-location. Свойства, загруженные из дополнительных расположений, могут переопределять свойства в стандартных расположениях. Например, если spring.config.additional-location настроено со значением optional:classpath:/custom-config/,optional:file:./custom-config/, полный набор рассматриваемых расположений:
-
optional:classpath:/;optional:classpath:/config/ -
optional:file:./;optional:file:./config/;optional:file:./config/*/ -
optional:classpath:custom-config/ -
optional:file:./custom-config/
Такой порядок поиска позволяет указать значения по умолчанию в одном файле конфигурации, а затем выборочно переопределить их в другом. Вы можете предоставить значения по умолчанию для своего приложения в application.properties (или любом другом базовом имени, которое вы выберете с помощью spring.config.name) в одном из стандартных расположений. Эти значения по умолчанию можно переопределить во время выполнения с помощью другого файла, расположенного в одном из пользовательских расположений.
2.3.1. Необязательные расположения
По умолчанию, если указанное расположение данных конфигурации не существует, Spring Boot выбросит исключение ConfigDataLocationNotFoundException, и ваше приложение не запустится.
Если вы хотите указать расположение, но не возражаете, если оно не всегда существует, вы можете использовать префикс optional:. Вы можете использовать этот префикс со свойствами spring.config.location и spring.config.additional-location, а также с объявлениями spring.config.import.
Например, значение spring.config.import optional:file:./myconfig.properties позволяет вашему приложению запускаться, даже если файл myconfig.properties отсутствует.
Если вы хотите игнорировать все ConfigDataLocationNotFoundExceptions и всегда продолжать запуск приложения, вы можете использовать свойство spring.config.on-not-found. Установите значение на ignore с помощью SpringApplication.setDefaultProperties(…) или системной/средовой переменной.
2.3.2. Расположения с подстановочными знаками
Если расположение файла конфигурации включает символ * для последнего сегмента пути, оно считается расположением с подстановочными знаками. Подстановочные знаки расширяются при загрузке конфигурации, поэтому проверяются также непосредственные подкаталоги. Расположения с подстановочными знаками особенно полезны в среде, например, Kubernetes, когда есть несколько источников свойств конфигурации.
Например, если у вас есть конфигурация Redis и конфигурация MySQL, вы можете захотеть сохранить эти две части конфигурации раздельно, при этом потребовав, чтобы обе были присутствовать в файле application.properties. Это может привести к двум отдельным файлам application.properties, размещенным в разных расположениях, таких как /config/redis/application.properties и /config/mysql/application.properties. В таком случае, наличие расположения с подстановочным знаком config/*/ приведет к обработке обоих файлов.
По умолчанию Spring Boot включает config/*/ в стандартные расположения поиска. Это означает, что будут проверяться все подкаталоги каталога /config вне вашего JAR-файла.
Вы можете использовать расположения с подстановочными знаками самостоятельно с помощью свойств spring.config.location и spring.config.additional-location.
Расположение с подстановочными знаками должно содержать только один * и заканчиваться на */ для расположений поиска, которые являются каталогами, или */<filename> для расположений поиска, которые являются файлами. Расположения с подстановочными знаками сортируются по алфавиту на основе абсолютного пути к именам файлов. |
Расположения с подстановочными знаками работают только с внешними каталогами. Вы не можете использовать подстановочные знаки в расположениях classpath:. |
2.3.3. Файлы, специфичные для профиля
Помимо файлов свойств application, Spring Boot также попытается загрузить файлы, специфичные для профиля, используя соглашение об именовании application-{profile}. Например, если ваше приложение активирует профиль с именем prod и использует файлы YAML, то будут рассмотрены как application.yaml, так и application-prod.yaml.
Свойства, специфичные для профиля, загружаются из тех же мест, что и стандартные файлы application.properties, при этом файлы, специфичные для профиля, всегда переопределяют неспецифичные. Если указано несколько профилей, применяется стратегия «последнее значение выигрывает». Например, если профили prod,live указаны свойством spring.profiles.active, значения в application-prod.properties могут быть переопределены значениями из application-live.properties.
| Стратегия «последнее значение выигрывает» применяется на уровне группы расположений. Файл Например, продолжая наш пример с /cfg application-live.properties /ext application-live.properties application-prod.properties Когда у нас есть
Когда у нас есть
|
У Environment есть набор профилей по умолчанию (по умолчанию, [default]), которые используются, если активные профили не заданы. Другими словами, если профили не активированы явно, то учитываются свойства из application-default.
| Файлы свойств загружаются только один раз. Если вы уже напрямую импортировали файлы свойств, специфичные для профиля, то они не будут импортированы повторно. |
2.3.4. Импорт дополнительных данных
Свойства приложения могут импортировать дополнительные данные конфигурации из других мест, используя свойство spring.config.import. Импорты обрабатываются по мере обнаружения и рассматриваются как дополнительные документы, вставленные сразу под документом, который объявляет импорт.
Например, в вашем классе application.properties может быть следующий файл:
spring.application.name=myapp
spring.config.import=optional:file:./dev.properties spring:
application:
name: "myapp"
config:
import: "optional:file:./dev.properties" Это вызовет импорт файла dev.properties в текущей директории (если такой файл существует). Значения из импортированного dev.properties будут иметь приоритет над файлом, который инициировал импорт. В приведённом примере dev.properties может переопределить значение spring.application.name.
Импорт будет осуществлен только один раз, независимо от того, сколько раз он объявлен. Порядок объявления импорта внутри одного документа в файлах свойств/YAML не имеет значения. Например, два примера ниже дают один и тот же результат:
spring.config.import=my.properties
my.property=value spring:
config:
import: "my.properties"
my:
property: "value" my.property=value
spring.config.import=my.properties my:
property: "value"
spring:
config:
import: "my.properties" В обоих примерах значения из файла my.properties будут иметь приоритет над файлом, который инициировал импорт.
Несколько расположений могут быть указаны под одним ключом spring.config.import. Расположения будут обрабатываться в порядке их определения, а более поздние импорты будут иметь приоритет.
При необходимости, варианты, специфичные для профиля, также рассматриваются для импорта. Приведённый пример будет импортировать как my.properties, так и все my-<profile>.properties варианты. |
| Spring Boot включает подключаемый API, который позволяет поддерживать различные адреса расположения. По умолчанию поддерживаются импорт Java Properties, YAML и «деревья конфигурации». Библиотеки сторонних разработчиков могут предложить поддержку дополнительных технологий (файлы не обязаны быть локальными). Например, можно представить данные конфигурации из внешних хранилищ, таких как Consul, Apache ZooKeeper или Netflix Archaius. Если вы хотите поддержать свои собственные расположения, обратитесь к классам |
2.3.5. Импорт файлов без расширения
Некоторые облачные платформы не могут добавить расширение файла к объёмно смонтированным файлам. Чтобы импортировать эти файлы без расширения, необходимо дать Spring Boot подсказку о том, как их загружать. Это можно сделать, поместив подсказку расширения в квадратные скобки.
Например, предположим, что у вас есть файл /etc/config/myconfig, который вы хотите импортировать как YAML. Вы можете импортировать его из вашего application.properties, используя следующее:
spring.config.import=file:/etc/config/myconfig[.yaml] spring:
config:
import: "file:/etc/config/myconfig[.yaml]" 2.3.6. Использование деревьев конфигурации
При запуске приложений на облачной платформе (такой как Kubernetes) часто требуется читать значения конфигурации, предоставляемые платформой. Использование переменных окружения для таких целей распространено, но это может иметь недостатки, особенно если значение должно храниться в секрете.
В качестве альтернативы переменным окружения многие облачные платформы теперь позволяют отображать конфигурацию в смонтированных томах данных. Например, Kubernetes может монтировать тома как ConfigMaps, так и Secrets.
Существует два распространённых шаблона монтирования томов:
-
Один файл содержит полный набор свойств (обычно в формате YAML).
-
Несколько файлов записываются в дереве каталогов, при этом имя файла становится «ключом», а содержимое — «значением».
В первом случае вы можете импортировать YAML- или Properties-файл напрямую, используя spring.config.import, как описано выше. Во втором случае необходимо использовать префикс configtree:, чтобы Spring Boot знал, что ему нужно экспонировать все файлы как свойства.
В качестве примера, предположим, что Kubernetes смонтировал следующий том:
etc/
config/
myapp/
username
password Содержимое файла username будет значением конфигурации, а содержимое файла password — секретом.
Для импорта этих свойств вы можете добавить следующее в свой файл application.properties или application.yaml:
spring.config.import=optional:configtree:/etc/config/ spring:
config:
import: "optional:configtree:/etc/config/" Затем вы можете получить доступ к или внедрить свойства myapp.username и myapp.password из Environment обычным способом.
Имена папок и файлов в дереве конфигурации образуют имя свойства. В приведенном выше примере, чтобы получить доступ к свойствам как username и password, можно установить spring.config.import в optional:configtree:/etc/config/myapp. |
Имена файлов с использованием точечной нотации также корректно отображаются. Например, в примере выше, файл с именем myapp.username в каталоге /etc/config приведет к свойству myapp.username в Environment. |
Значения дерева конфигурации могут быть привязаны к типам строкового String и byte[] в зависимости от ожидаемого содержимого. |
Если у вас несколько деревьев конфигурации для импорта из одной родительской папки, вы можете использовать сокращение с подстановочным знаком. Любое расположение configtree:, которое заканчивается на /*/, импортирует всех непосредственных потомков как деревья конфигурации. Как и при импорте без подстановочного знака, имена папок и файлов в каждом дереве конфигурации образуют имя свойства.
Например, если данный том:
etc/
config/
dbconfig/
db/
username
password
mqconfig/
mq/
username
password Вы можете использовать configtree:/etc/config/*/ в качестве расположения импорта:
spring.config.import=optional:configtree:/etc/config/*/ spring:
config:
import: "optional:configtree:/etc/config/*/" Это добавит свойства db.username, db.password, mq.username и mq.password.
| Каталоги, загруженные с использованием подстановочного знака, сортируются в алфавитном порядке. Если вам нужен другой порядок, вы должны перечислить каждое расположение как отдельный импорт. |
Деревья конфигурации также могут использоваться для Docker-секретов. Когда служба Docker swarm получает доступ к секрету, секрет монтируется в контейнер. Например, если секрет с именем db.password смонтирован по пути /run/secrets/, вы можете сделать db.password доступным для среды Spring следующим образом:
spring.config.import=optional:configtree:/run/secrets/ spring:
config:
import: "optional:configtree:/run/secrets/" 2.3.7. Подстановки свойств
Значения в файлах application.properties и application.yaml фильтруются через существующие Environment при использовании, поэтому вы можете ссылаться на ранее определенные значения (например, из системных свойств или переменных окружения). Стандартный синтаксис подстановки свойств ${name} можно использовать где угодно в значении. Подстановки свойств также могут указывать значение по умолчанию, используя : для разделения значения по умолчанию и имени свойства, например ${name:default}.
Использование подстановок с и без значений по умолчанию показано в следующем примере:
app.name=MyApp
app.description=${app.name} is a Spring Boot application written by ${username:Unknown} app:
name: "MyApp"
description: "${app.name} is a Spring Boot application written by ${username:Unknown}" Предполагая, что свойство username не было установлено в другом месте, app.description будет иметь значение MyApp is a Spring Boot application written by Unknown.
| Вы всегда должны ссылаться на имена свойств в подстановке в канонической форме (кебаб-кейс, используя только строчные буквы). Это позволит Spring Boot использовать ту же логику, что и при расслабленной привязке Например, |
| Этот метод также можно использовать для создания «кратких» вариантов существующих свойств Spring Boot. Подробности см. в разделе howto.html. |
2.3.8. Работа с файлами, содержащими несколько документов
Spring Boot позволяет разделять один физический файл на несколько логических документов, каждый из которых добавляется независимо. Документы обрабатываются в порядке следования сверху вниз. Более поздние документы могут перезаписывать свойства, определённые в предыдущих.
Для файлов application.yaml используется стандартный синтаксис YAML для нескольких документов. Три последовательных дефиса обозначают конец одного документа и начало следующего.
Например, в следующем файле два логических документа:
spring:
application:
name: "MyApp"
---
spring:
application:
name: "MyCloudApp"
config:
activate:
on-cloud-platform: "kubernetes" Для файлов application.properties используется специальный комментарий #--- или !--- для обозначения разбиения документов:
spring.application.name=MyApp
#---
spring.application.name=MyCloudApp
spring.config.activate.on-cloud-platform=kubernetes | Разделители файлов свойств не должны содержать начальных пробелов и должны состоять ровно из трех дефисов. Строки непосредственно перед и после разделителя не должны содержать того же префикса комментария. |
Файлы свойств с несколькими документами часто используются вместе с свойствами активации, такими как spring.config.activate.on-profile. Подробности см. в следующем разделе. |
Файлы свойств с несколькими документами нельзя загрузить с помощью аннотаций @PropertySource или @TestPropertySource. |
2.3.9. Свойства активации
Иногда бывает полезно активировать заданный набор свойств только при выполнении определенных условий. Например, у вас могут быть свойства, относящиеся только к активному профилю.
Вы можете условно активировать документ свойств, используя spring.config.activate.*.
Доступны следующие свойства активации:
| Свойство | Примечание |
|---|---|
| Выражение профиля, которое должно соответствовать для активации документа. |
|
|
Например, следующее указывает, что второй документ активен только при работе в Kubernetes и только тогда, когда активны профили «prod» или «staging»:
myprop=always-set
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.config.activate.on-profile=prod | staging
myotherprop=sometimes-set myprop:
"always-set"
---
spring:
config:
activate:
on-cloud-platform: "kubernetes"
on-profile: "prod | staging"
myotherprop: "sometimes-set" 2.4. Шифрование свойств
Spring Boot не предоставляет встроенной поддержки шифрования значений свойств, однако он предоставляет необходимые точки подключения для изменения значений, содержащихся в Spring Environment. Интерфейс EnvironmentPostProcessor позволяет манипулировать Environment перед запуском приложения. Подробности см. в howto.html.
Если вам нужен безопасный способ хранения учетных данных и паролей, проект Spring Cloud Vault предоставляет поддержку хранения внешних настроек в HashiCorp Vault.
2.5. Работа с YAML
YAML — это надмножество JSON и, как таковое, является удобным форматом для задания иерархических данных конфигурации. Класс SpringApplication автоматически поддерживает YAML в качестве альтернативы свойствам, когда у вас есть библиотека SnakeYAML в вашем классе.
Если вы используете «Starters», SnakeYAML автоматически предоставляется spring-boot-starter. |
2.5.1. Сопоставление YAML со свойствами
Документы YAML необходимо преобразовать из иерархического формата в плоскую структуру, которую можно использовать с Spring Environment. Например, рассмотрим следующий документ YAML:
environments:
dev:
url: "https://dev.example.com"
name: "Developer Setup"
prod:
url: "https://another.example.com"
name: "My Cool App" Для доступа к этим свойствам из Environment они будут сглажены следующим образом:
environments.dev.url=https://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=https://another.example.com
environments.prod.name=My Cool App Аналогично, списки YAML также необходимо сглаживать. Они представлены в качестве ключей свойств с [index] дериференцировщиками. Например, рассмотрим следующий YAML:
my:
servers:
- "dev.example.com"
- "another.example.com" Представленный пример будет преобразован в следующие свойства:
my.servers[0]=dev.example.com
my.servers[1]=another.example.com Свойства, использующие [index] нотацию, могут быть связаны с Java List или Set объектами с помощью класса Spring Boot Binder. Подробнее см. раздел «Безопасные свойства конфигурации» ниже. |
Файлы YAML не могут быть загружены с помощью аннотаций @PropertySource или @TestPropertySource. Поэтому, в случае необходимости загрузки значений таким образом, вам нужно использовать файл свойств. |
2.5.2. Прямая загрузка YAML
Spring Framework предоставляет два удобных класса, которые могут использоваться для загрузки документов YAML. YamlPropertiesFactoryBean загружает YAML как Properties, а YamlMapFactoryBean загружает YAML как Map.
Вы также можете использовать класс YamlPropertySourceLoader, если хотите загрузить YAML как Spring PropertySource.
2.6. Настройка случайных значений
RandomValuePropertySource полезно для вставки случайных значений (например, в секреты или тестовые случаи). Он может генерировать целые числа, long, uuid или строки, как показано в следующем примере:
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number-less-than-ten=${random.int(10)}
my.number-in-range=${random.int[1024,65536]} my:
secret: "${random.value}"
number: "${random.int}"
bignumber: "${random.long}"
uuid: "${random.uuid}"
number-less-than-ten: "${random.int(10)}"
number-in-range: "${random.int[1024,65536]}" Синтаксис random.int* — OPEN value (,max) CLOSE, где OPEN,CLOSE — любые символы, а value,max — целые числа. Если max предоставлено, то value — минимальное значение, а max — максимальное значение (исключая его).
2.7. Настройка свойств системной среды
Spring Boot поддерживает установку префикса для свойств среды. Это полезно, если системная среда используется несколькими приложениями Spring Boot с различными требованиями к конфигурации. Префикс для свойств системной среды можно установить непосредственно в SpringApplication.
Например, если вы установите префикс на input, свойство, такое как remote.timeout, также будет разрешено как input.remote.timeout в системной среде.
2.8. Безопасные конфигурационные свойства
Использование аннотации @Value("${property}") для инъекции свойств конфигурации может быть неудобным, особенно при работе с несколькими свойствами или иерархическими данными. Spring Boot предоставляет альтернативный метод работы со свойствами, позволяющий сильно типизированным объектам управлять и проверять конфигурацию приложения.
2.8.1. Связывание свойств JavaBean
Можно связать объект, объявляющий стандартные свойства JavaBean, как показано в следующем примере:
@ConfigurationProperties("my.service")
public class MyProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
}
}
@ConfigurationProperties("my.service")
class MyProperties {
var isEnabled = false
var remoteAddress: InetAddress? = null
val security = Security()
class Security {
var username: String? = null
var password: String? = null
var roles: List<String> = ArrayList(setOf("USER"))
}
}
Предыдущий POJO определяет следующие свойства:
-
my.service.enabledсо значениемfalseпо умолчанию. -
my.service.remote-address, с типом, который может быть преобразован изString. -
my.service.security.username, со вложенным объектом "security", имя которого определяется именем свойства. В частности, тип там вообще не используется и мог бы бытьSecurityProperties. -
my.service.security.password. -
my.service.security.roles, с наборомString, который по умолчанию равенUSER.
Свойства, сопоставляемые с классами @ConfigurationProperties, доступными в Spring Boot, которые настраиваются через файлы свойств, файлы YAML, переменные среды и другие механизмы, являются частью API, но методы доступа (геттеры/сеттеры) самого класса не предназначены для прямого использования. |
| Такая организация полагается на конструктор по умолчанию, и геттеры и сеттеры обычно обязательны, так как связывание происходит через стандартные описатели свойств Java Beans, как и в Spring MVC. Сеттер может быть опущен в следующих случаях:
Некоторые люди используют Project Lombok для автоматического добавления геттеров и сеттеров. Убедитесь, что Lombok не генерирует какой-либо определенный конструктор для такого типа, так как он автоматически используется контейнером для создания объекта. Наконец, учитываются только стандартные свойства Java Bean, и привязка к статическим свойствам не поддерживается. |
2.8.2. Связывание по конструктору
Пример в предыдущем разделе можно переписать в неизменяемом виде, как показано в следующем примере:
@ConfigurationProperties("my.service")
public class MyProperties {
public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
public static class Security {
public Security(String username, String password, @DefaultValue("USER") List<String> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
}
}
@ConfigurationProperties("my.service")
class MyProperties(val enabled: Boolean, val remoteAddress: InetAddress,
val security: Security) {
class Security(val username: String, val password: String,
@param:DefaultValue("USER") val roles: List<String>)
}
В этой настройке наличие единственного параметризованного конструктора подразумевает использование связывания по конструктору. Это означает, что связующий элемент найдет конструктор с параметрами, которые вы хотите связать. Если у вашего класса несколько конструкторов, аннотация @ConstructorBinding может использоваться для указания конструктора, который нужно использовать для связывания по конструктору. Чтобы отключить связывание по конструктору для класса с единственным параметризованным конструктором, конструктор должен быть аннотирован с помощью @Autowired. Связывание по конструктору можно использовать с записями. Если ваша запись не имеет нескольких конструкторов, нет необходимости использовать @ConstructorBinding.
Вложенные члены класса, связанного по конструктору (например, Security в приведенном выше примере), также будут связаны через свой конструктор.
Значения по умолчанию можно задать с помощью @DefaultValue для параметров конструктора и компонентов записей. Сервис преобразования будет применен для преобразования аннотированного String значения в целевой тип отсутствующего свойства.
Если обратиться к предыдущему примеру, если ни одно свойство не привязано к Security, экземпляр MyProperties будет содержать значение null для security. Чтобы он содержал непустой экземпляр Security даже когда ни одно свойство не привязано к нему (при использовании Kotlin, это потребует параметров username и password Security быть объявлены как необязательные, поскольку у них нет значений по умолчанию), используйте пустую аннотацию @DefaultValue:
public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
class MyProperties(val enabled: Boolean, val remoteAddress: InetAddress,
@DefaultValue val security: Security) {
class Security(val username: String?, val password: String?,
@param:DefaultValue("USER") val roles: List<String>)
}
Для использования связывания по конструктору класс должен быть включен с помощью @EnableConfigurationProperties или сканирования свойств конфигурации. Вы не можете использовать связывание по конструктору с компонентами, которые создаются стандартными механизмами Spring (например, @Component компоненты, компоненты, созданные с помощью методов @Bean или загруженные с помощью @Import). |
Для использования связывания по конструктору в образце родного кода класс должен быть скомпилирован с -parameters. Это произойдет автоматически, если вы используете плагин Spring Boot Gradle или Maven и spring-boot-starter-parent. |
Использование java.util.Optional с @ConfigurationProperties не рекомендуется, так как оно предназначено в первую очередь для использования в качестве возвращаемого типа. Поэтому оно не подходит для инъекции свойств конфигурации. Для согласованности со свойствами других типов, если вы объявляете свойство Optional и оно не имеет значения, то будет привязано null, а не пустое Optional. |
2.8.3. Включение типов, аннотированных с помощью @ConfigurationProperties
Spring Boot предоставляет инфраструктуру для привязки типов @ConfigurationProperties и регистрации их как компонентов. Вы можете либо включить свойства конфигурации для каждого класса по отдельности, либо включить сканирование свойств конфигурации, которое работает аналогично сканированию компонентов.
Иногда классы, аннотированные с помощью @ConfigurationProperties, могут не подходить для сканирования, например, если вы разрабатываете собственные автоконфигурации или хотите включить их условно. В таких случаях укажите список типов для обработки с помощью аннотации @EnableConfigurationProperties. Это можно сделать в любом классе @Configuration, как показано в следующем примере:
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties.class)
public class MyConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SomeProperties::class)
class MyConfiguration
@ConfigurationProperties("some.properties")
public class SomeProperties {
}
@ConfigurationProperties("some.properties")
class SomeProperties
Чтобы использовать сканирование свойств конфигурации, добавьте аннотацию @ConfigurationPropertiesScan к своему приложению. Обычно она добавляется к главному классу приложения, аннотированному с помощью @SpringBootApplication, но ее можно добавить к любому классу @Configuration. По умолчанию сканирование будет выполняться из пакета класса, который объявляет аннотацию. Если вы хотите определить конкретные пакеты для сканирования, вы можете сделать это, как показано в следующем примере:
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" })
public class MyApplication {
}
@SpringBootApplication
@ConfigurationPropertiesScan("com.example.app", "com.example.another")
class MyApplication
| Когда компонент Предполагая, что он находится в пакете |
Рекомендуется, чтобы @ConfigurationProperties работал только со средой и, в частности, не инжектировал другие компоненты из контекста. Для особых случаев можно использовать инъекцию через сеттер или любой из интерфейсов *Aware, предоставляемых фреймворком (например, EnvironmentAware, если вам нужен доступ к Environment). Если вы все же хотите инжектировать другие компоненты с помощью конструктора, компонент конфигурации свойств должен быть аннотирован с помощью @Component и использовать привязку свойств на основе JavaBean.
2.8.4. Использование типов, аннотированных с помощью @ConfigurationProperties
Этот стиль конфигурации особенно хорошо работает с внешней YAML-конфигурацией, как показано в следующем примере:
my:
service:
remote-address: 192.168.1.1
security:
username: "admin"
roles:
- "USER"
- "ADMIN" Чтобы работать с @ConfigurationProperties бинсами, вы можете вводить их так же, как и любые другие бинсы, как показано в следующем примере:
@Service
public class MyService {
private final MyProperties properties;
public MyService(MyProperties properties) {
this.properties = properties;
}
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
server.start();
// ...
}
// ...
}
@Service
class MyService(val properties: MyProperties) {
fun openConnection() {
val server = Server(properties.remoteAddress)
server.start()
// ...
}
// ...
}
Использование @ConfigurationProperties также позволяет генерировать файлы метаданных, которые могут использоваться IDE для предложения автодополнения для ваших собственных ключей. Подробности см. в приложении. |
2.8.5. Конфигурация сторонних библиотек
Помимо использования @ConfigurationProperties для аннотирования класса, вы также можете использовать его для общедоступных @Bean методов. Это может быть особенно полезно, когда вы хотите привязать свойства к компонентам сторонних библиотек, которые находятся вне вашего контроля.
Для настройки бинса из Environment свойств добавьте @ConfigurationProperties к его регистрации бинсов, как показано в следующем примере:
@Configuration(proxyBeanMethods = false)
public class ThirdPartyConfiguration {
@Bean
@ConfigurationProperties(prefix = "another")
public AnotherComponent anotherComponent() {
return new AnotherComponent();
}
}
@Configuration(proxyBeanMethods = false)
class ThirdPartyConfiguration {
@Bean
@ConfigurationProperties(prefix = "another")
fun anotherComponent(): AnotherComponent = AnotherComponent()
}
Любое свойство JavaBean, определённое с префиксом another, сопоставляется с этим AnotherComponent бинсом аналогично предыдущему примеру SomeProperties.
2.8.6. Расслабленная привязка
Spring Boot использует некоторые правила гибкой привязки свойств Environment к объектам @ConfigurationProperties, поэтому точное совпадение имени свойства Environment и имени свойства объекта не требуется. Типичные примеры, где это полезно, включают свойства среды, разделенные дефисом (например, context-path привязывается к contextPath) и свойства среды с заглавными буквами (например, PORT привязывается к port).
В качестве примера рассмотрим следующий класс @ConfigurationProperties:
@ConfigurationProperties(prefix = "my.main-project.person")
public class MyPersonProperties {
private String firstName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
@ConfigurationProperties(prefix = "my.main-project.person")
class MyPersonProperties {
var firstName: String? = null
}
С указанным кодом могут использоваться следующие имена свойств:
| Свойство | Примечание |
|---|---|
| Кебаб-кейс, который рекомендуется использовать в файлах |
| Стандартный синтаксис camelCase. |
| Символы подчёркивания, это альтернативный формат для использования в файлах |
| Формат с заглавными буквами, который рекомендуется использовать при использовании переменных среды системы. |
Значение prefix для аннотации должно быть в формате kebab case (строчные буквы и разделены -, например, my.main-project.person). |
| Источник свойства | Простой | Список |
|---|---|---|
Файлы свойств | Camel case, kebab case или с символами подчёркивания | Стандартный синтаксис списка, использующий |
Файлы YAML | Camel case, kebab case или с символами подчёркивания | Стандартный синтаксис списка YAML или значения, разделённые запятыми |
Переменные среды | Формат с заглавными буквами с символом подчёркивания в качестве разделителя (см. Привязка из переменных среды). | Числовые значения, окружённые символами подчёркивания (см. Привязка из переменных среды) |
Свойства системы | Camel case, kebab case или с символами подчёркивания | Стандартный синтаксис списка, использующий |
Рекомендуется, если это возможно, хранить свойства в формате lower-case kebab, например, my.person.first-name=Rod. |
Привязка словарей
При привязке к Map свойствам может потребоваться использовать специальный синтаксис с квадратными скобками, чтобы сохранить исходное значение key. Если ключ не окружён [], любые символы, не являющиеся буквенно-цифровыми, - или ., будут удалены.
Например, рассмотрим привязку следующих свойств к Map<String,String>:
my.map.[/key1]=value1
my.map.[/key2]=value2
my.map./key3=value3 my:
map:
"[/key1]": "value1"
"[/key2]": "value2"
"/key3": "value3" | Для файлов YAML скобки должны быть заключены в кавычки, чтобы ключи были правильно обработаны. |
Вышеперечисленные свойства будут привязаны к Map со значениями /key1, /key2 и key3 в качестве ключей словаря. Слэш был удалён из key3, так как он не был заключён в квадратные скобки.
При привязке к скалярным значениям, ключи, содержащие ., не нуждаются в окружении []. Скалярные значения включают перечисления и все типы в пакете java.lang за исключением Object. Привязка a.b=c к Map<String, String> сохранит . в ключе и вернёт словарь со значением {"a.b"="c"}. Для других типов необходимо использовать синтаксис с квадратными скобками, если ваше key содержит .. Например, привязка a.b=c к Map<String, Object> вернёт словарь со значением {"a"={"b"="c"}}, тогда как [a.b]=c вернёт словарь со значением {"a.b"="c"}.
Привязка из переменных среды
Большинство операционных систем накладывают жёсткие ограничения на имена, которые могут использоваться для переменных среды. Например, переменные оболочки Linux могут содержать только буквы (a до z или A до Z), цифры (0 до 9) или символ подчёркивания (_). По соглашению, имена переменных оболочки Unix также будут в верхнем регистре.
Правила гибкой привязки Spring Boot, насколько это возможно, разработаны для совместимости с этими ограничениями на имена.
Чтобы преобразовать имя свойства в канонической форме в имя переменной среды, можно следовать этим правилам:
-
Замените точки (
.) символами подчёркивания (_). -
Удалите все дефисы (
-). -
Преобразуйте в верхний регистр.
Например, свойство конфигурации spring.main.log-startup-info будет переменной среды, названной SPRING_MAIN_LOGSTARTUPINFO.
Переменные среды также могут быть использованы при привязке к спискам объектов. Чтобы привязаться к элементу List, номер элемента должен быть заключен в символы подчёркивания в имени переменной.
Например, свойство конфигурации my.service[0].other будет использовать переменную среды, названную MY_SERVICE_0_OTHER.
2.8.7. Объединение сложных типов
При конфигурировании списков в нескольких местах перезапись происходит заменой всего списка.
Например, предположим объект MyPojo с атрибутами name и description, которые по умолчанию являются null. Следующий пример демонстрирует список объектов MyPojo из MyProperties:
@ConfigurationProperties("my")
public class MyProperties {
private final List<MyPojo> list = new ArrayList<>();
public List<MyPojo> getList() {
return this.list;
}
}
@ConfigurationProperties("my")
class MyProperties {
val list: List<MyPojo> = ArrayList()
}
Рассмотрим следующую конфигурацию:
my.list[0].name=my name
my.list[0].description=my description
#---
spring.config.activate.on-profile=dev
my.list[0].name=my another name my:
list:
- name: "my name"
description: "my description"
---
spring:
config:
activate:
on-profile: "dev"
my:
list:
- name: "my another name" Если профиль dev не активен, MyProperties.list содержит одну запись MyPojo, как определено ранее. Однако, если профиль dev активирован, list по-прежнему содержит только одну запись (с именем my another name и описанием null). Эта конфигурация не добавляет второй экземпляр MyPojo в список и не объединяет элементы.
Когда List указан в нескольких профилях, используется только тот, у которого приоритет выше. Рассмотрим следующий пример:
my.list[0].name=my name
my.list[0].description=my description
my.list[1].name=another name
my.list[1].description=another description
#---
spring.config.activate.on-profile=dev
my.list[0].name=my another name my:
list:
- name: "my name"
description: "my description"
- name: "another name"
description: "another description"
---
spring:
config:
activate:
on-profile: "dev"
my:
list:
- name: "my another name" В предыдущем примере, если профиль dev активен, MyProperties.list содержит одну запись MyPojo (с именем my another name и описанием null). Для YAML можно использовать как списки, разделенные запятыми, так и списки YAML для полной перезаписи содержимого списка.
Для свойств Map можно привязываться к значениям свойств из нескольких источников. Однако для одного и того же свойства в нескольких источниках используется тот, у которого приоритет выше. Следующий пример демонстрирует Map<String, MyPojo> из MyProperties:
@ConfigurationProperties("my")
public class MyProperties {
private final Map<String, MyPojo> map = new LinkedHashMap<>();
public Map<String, MyPojo> getMap() {
return this.map;
}
}
@ConfigurationProperties("my")
class MyProperties {
val map: Map<String, MyPojo> = LinkedHashMap()
}
Рассмотрим следующую конфигурацию:
my.map.key1.name=my name 1
my.map.key1.description=my description 1
#---
spring.config.activate.on-profile=dev
my.map.key1.name=dev name 1
my.map.key2.name=dev name 2
my.map.key2.description=dev description 2 my:
map:
key1:
name: "my name 1"
description: "my description 1"
---
spring:
config:
activate:
on-profile: "dev"
my:
map:
key1:
name: "dev name 1"
key2:
name: "dev name 2"
description: "dev description 2" Если профиль dev не активен, MyProperties.map содержит одну запись с ключом key1 (с именем my name 1 и описанием my description 1). Однако, если профиль dev включен, map содержит две записи с ключами key1 (с именем dev name 1 и описанием my description 1) и key2 (с именем dev name 2 и описанием dev description 2).
| Предыдущие правила объединения применяются к свойствам из всех источников свойств, а не только к файлам. |
2.8.8. Преобразование свойств
Spring Boot пытается привести внешние свойства приложения к нужному типу при привязке к @ConfigurationProperties биндам. Если вам нужна настройка преобразования типов, вы можете предоставить ConversionService бин (с именем бина conversionService) или пользовательские редакторы свойств (через CustomEditorConfigurer бин) или пользовательские Converters (с определениями бинов, помеченных как @ConfigurationPropertiesBinding).
Поскольку этот бин запрашивается очень рано на жизненном цикле приложения, убедитесь, что зависимости вашего ConversionService ограничены. Как правило, любая необходимая вам зависимость может быть не полностью инициализирована во время создания. Вы можете переименовать свой пользовательский ConversionService, если он не требуется для преобразования ключей конфигурации, и полагаться только на пользовательских преобразователях, квалифицированных как @ConfigurationPropertiesBinding. |
Преобразование продолжительностей
Spring Boot имеет специальную поддержку для выражения продолжительностей. Если вы экспонируете свойство java.time.Duration, доступны следующие форматы в свойствах приложения:
-
Обычное представление
long(с использованием миллисекунд в качестве единицы по умолчанию, если не указана другая единица@DurationUnit) -
Стандартный формат ISO-8601 используемый
java.time.Duration -
Более читаемый формат, где значение и единица объединены (
10sозначает 10 секунд)
Рассмотрим следующий пример:
@ConfigurationProperties("my")
public class MyProperties {
@DurationUnit(ChronoUnit.SECONDS)
private Duration sessionTimeout = Duration.ofSeconds(30);
private Duration readTimeout = Duration.ofMillis(1000);
}
@ConfigurationProperties("my")
class MyProperties {
@DurationUnit(ChronoUnit.SECONDS)
var sessionTimeout = Duration.ofSeconds(30)
var readTimeout = Duration.ofMillis(1000)
}
Для указания таймаута сессии в 30 секунд, 30, PT30S и 30s все эквивалентны. Таймаут чтения в 500 мс может быть задан в любом из следующих форматов: 500, PT0.5S и 500ms.
Вы также можете использовать любые поддерживаемые единицы. Вот они:
-
nsдля наносекунд -
usдля микросекунд -
msдля миллисекунд -
sдля секунд -
mдля минут -
hдля часов -
dдля дней
Единицей по умолчанию является миллисекунда, и её можно переопределить, используя @DurationUnit, как показано в примере выше.
Если вы предпочитаете использовать привязку к конструктору, те же свойства можно экспонировать, как показано в следующем примере:
@ConfigurationProperties("my")
public class MyProperties {
public MyProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout,
@DefaultValue("1000ms") Duration readTimeout) {
this.sessionTimeout = sessionTimeout;
this.readTimeout = readTimeout;
}
}
@ConfigurationProperties("my")
class MyProperties(@param:DurationUnit(ChronoUnit.SECONDS) @param:DefaultValue("30s") val sessionTimeout: Duration,
@param:DefaultValue("1000ms") val readTimeout: Duration)
Если вы обновляете свойство Long, убедитесь, что вы определили единицу (используя @DurationUnit), если это не миллисекунды. Это даёт прозрачный путь обновления, поддерживая более богатый формат. |
Преобразование периодов
Помимо продолжительностей, Spring Boot также может работать с типом java.time.Period. В свойствах приложения можно использовать следующие форматы:
-
Обычное представление
int(с использованием дней в качестве единицы по умолчанию, если не указана другая единица@PeriodUnit) -
Стандартный формат ISO-8601 используемый
java.time.Period -
Более простой формат, где значение и единица пар связаны (
1y3dозначает 1 год и 3 дня)
Следующие единицы поддерживаются в простом формате:
-
yдля лет -
mдля месяцев -
wдля недель -
dдля дней
Тип java.time.Period никогда не хранит количество недель, это сокращение, означающее «7 дней». |
Преобразование размеров данных
Spring Framework имеет тип значений DataSize, который выражает размер в байтах. Если вы экспонируете свойство DataSize, доступны следующие форматы в свойствах приложения:
-
Обычное представление
long(с использованием байтов в качестве единицы по умолчанию, если не указана другая единица@DataSizeUnit) -
Более читаемый формат, где значение и единица пар связаны (
10MBозначает 10 мегабайт)
Рассмотрим следующий пример:
@ConfigurationProperties("my")
public class MyProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize bufferSize = DataSize.ofMegabytes(2);
private DataSize sizeThreshold = DataSize.ofBytes(512);
}
@ConfigurationProperties("my")
class MyProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
var bufferSize = DataSize.ofMegabytes(2)
var sizeThreshold = DataSize.ofBytes(512)
}
Для указания размера буфера в 10 мегабайт, 10 и 10MB эквивалентны. Порог размера в 256 байт можно указать как 256 или 256B.
Вы также можете использовать любые поддерживаемые единицы. Вот они:
-
Bдля байт -
KBдля килобайт -
MBдля мегабайт -
GBдля гигабайт -
TBдля терабайт
Единицей по умолчанию является байт, и её можно переопределить, используя @DataSizeUnit, как показано в примере выше.
Если вы предпочитаете использовать привязку к конструктору, те же свойства можно экспонировать, как показано в следующем примере:
@ConfigurationProperties("my")
public class MyProperties {
public MyProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize,
@DefaultValue("512B") DataSize sizeThreshold) {
this.bufferSize = bufferSize;
this.sizeThreshold = sizeThreshold;
}
}
@ConfigurationProperties("my")
class MyProperties(@param:DataSizeUnit(DataUnit.MEGABYTES) @param:DefaultValue("2MB") val bufferSize: DataSize,
@param:DefaultValue("512B") val sizeThreshold: DataSize)
Если вы обновляете свойство Long, убедитесь, что определили единицу (используя @DataSizeUnit), если это не байты. Это даёт прозрачный путь обновления, поддерживая более богатый формат. |
2.8.9. Валидация @ConfigurationProperties
Spring Boot пытается валидировать @ConfigurationProperties классы всякий раз, когда они помечены аннотацией Spring @Validated. Вы можете напрямую использовать аннотации ограничений JSR-303 jakarta.validation на вашем классе конфигурации. Для этого убедитесь, что соответствующая реализация JSR-303 находится в вашем классе пути, а затем добавьте аннотации ограничений к вашим полям, как показано в следующем примере:
@ConfigurationProperties("my.service")
@Validated
public class MyProperties {
@NotNull
private InetAddress remoteAddress;
}
@ConfigurationProperties("my.service")
@Validated
class MyProperties {
var remoteAddress: @NotNull InetAddress? = null
}
Также можно инициировать валидацию, пометив метод @Bean, который создаёт свойства конфигурации, аннотацией @Validated. |
Чтобы гарантировать, что валидация всегда срабатывает для вложенных свойств, даже если свойства не найдены, соответствующее поле должно быть помечено аннотацией @Valid. Следующий пример расширяет предыдущий пример MyProperties:
@ConfigurationProperties("my.service")
@Validated
public class MyProperties {
@NotNull
private InetAddress remoteAddress;
@Valid
private final Security security = new Security();
public static class Security {
@NotEmpty
private String username;
}
}
@ConfigurationProperties("my.service")
@Validated
class MyProperties {
var remoteAddress: @NotNull InetAddress? = null
@Valid
val security = Security()
class Security {
@NotEmpty
var username: String? = null
}
}
Вы также можете добавить пользовательский Spring Validator, создав определение бина под названием configurationPropertiesValidator. Метод @Bean должен быть объявлен static. Валидатор свойств конфигурации создаётся очень рано на жизненном цикле приложения, а объявление метода @Bean как статического позволяет создать бин без необходимости инстанцирования класса @Configuration. Это предотвращает любые проблемы, которые могут возникнуть из-за ранней инстанциации.
Модуль spring-boot-actuator включает конечную точку, которая экспонирует все @ConfigurationProperties бины. Откройте в вашем веб-браузере /actuator/configprops или используйте эквивалентную конечную точку JMX. Подробности смотрите в разделе «Готовые к использованию в производственной среде функции». |
2.8.10. @ConfigurationProperties против @Value
Аннотация @Value — это основная функция контейнера, и она не предоставляет те же возможности, что и типизированные свойства конфигурации. В следующей таблице обобщены возможности, поддерживаемые @ConfigurationProperties и @Value:
| Функция | @ConfigurationProperties | @Value |
|---|---|---|
Да | Ограниченно (см. примечание ниже) | |
Да | Нет | |
| Нет | Да |
| Если вы хотите использовать Например, |
Если вы определяете набор ключей конфигурации для собственных компонентов, рекомендуется сгруппировать их в POJO, аннотированном с помощью @ConfigurationProperties. Это обеспечит вам структурированный, типизированный объект, который можно ввести в свои собственные бины.
SpEL выражения из файлов свойств приложения не обрабатываются во время разбора этих файлов и заполнения среды. Однако можно записать выражение SpEL в @Value. Если значение свойства из файла свойств приложения является выражением SpEL, оно будет вычислено при использовании через @Value.
3. Профили
Профили Spring позволяют разделить части конфигурации приложения и сделать их доступными только в определенных средах. Любой @Component, @Configuration или @ConfigurationProperties может быть помечен аннотацией @Profile, чтобы ограничить время загрузки, как показано в следующем примере:
@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {
// ...
}
@Configuration(proxyBeanMethods = false)
@Profile("production")
class ProductionConfiguration {
// ...
}
Если бины @ConfigurationProperties регистрируются через @EnableConfigurationProperties вместо автоматической проверки, аннотация @Profile должна быть указана в классе @Configuration, имеющем аннотацию @EnableConfigurationProperties. В случае, если @ConfigurationProperties проверяются, @Profile может быть указана на самом классе @ConfigurationProperties. |
Вы можете использовать свойство spring.profiles.active Environment для указания активных профилей. Вы можете указать свойство любым из способов, описанных ранее в этой главе. Например, вы можете включить его в свой application.properties, как показано в следующем примере:
spring.profiles.active=dev,hsqldb spring:
profiles:
active: "dev,hsqldb" Вы также можете указать его в командной строке, используя следующий переключатель: --spring.profiles.active=dev,hsqldb.
Если ни один профиль не активен, активируется по умолчанию. Имя профиля по умолчанию — default, и его можно настроить с помощью свойства spring.profiles.default Environment, как показано в следующем примере:
spring.profiles.default=none spring:
profiles:
default: "none" spring.profiles.active и spring.profiles.default могут использоваться только в документах, не связанных с профилями. Это означает, что их нельзя включать в файлы, специфичные для профилей, или в документы, активируемые spring.config.activate.on-profile.
Например, вторая конфигурация документа недопустима:
# this document is valid
spring.profiles.active=prod
#---
# this document is invalid
spring.config.activate.on-profile=prod
spring.profiles.active=metrics # this document is valid
spring:
profiles:
active: "prod"
---
# this document is invalid
spring:
config:
activate:
on-profile: "prod"
profiles:
active: "metrics" 3.1. Добавление активных профилей
Свойство spring.profiles.active подчиняется тем же правилам упорядочивания, что и другие свойства: побеждает самое высокое PropertySource. Это означает, что вы можете указать активные профили в application.properties, а затем заменить их, используя переключатель командной строки.
Иногда полезно иметь свойства, которые добавляют активные профили, а не заменяют их. Свойство spring.profiles.include можно использовать для добавления активных профилей поверх тех, которые активированы свойством spring.profiles.active. Точка входа SpringApplication также имеет API Java для установки дополнительных профилей. См. метод setAdditionalProfiles() в SpringApplication.
Например, при запуске приложения со следующими свойствами, профили common и local будут активированы, даже если приложение запускается с переключателем --spring.profiles.active:
spring.profiles.include[0]=common
spring.profiles.include[1]=local spring:
profiles:
include:
- "common"
- "local" Подобно spring.profiles.active, spring.profiles.include может использоваться только в документах, не связанных с профилями. Это означает, что его нельзя включать в файлы, специфичные для профилей, или в документы, активируемые spring.config.activate.on-profile. |
Группы профилей, которые описаны в следующем разделе следующем разделе, также могут быть использованы для добавления активных профилей, если определенный профиль активен.
3.2. Группы профилей
Иногда определенные вами и используемые в приложении профили слишком детализированы и их использование становится громоздким. Например, у вас могут быть профили proddb и prodmq, которые вы используете для включения функций базы данных и обмена сообщениями независимо.
Для решения этой проблемы Spring Boot позволяет определять группы профилей. Группа профилей позволяет определить логическое имя для связанной группы профилей.
Например, мы можем создать группу production, которая состоит из наших профилей proddb и prodmq.
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq spring:
profiles:
group:
production:
- "proddb"
- "prodmq" Теперь наше приложение можно запустить, используя --spring.profiles.active=production для активации профилей production, proddb и prodmq в один клик.
3.3. Программная установка профилей
Вы можете программно установить активные профили, вызвав SpringApplication.setAdditionalProfiles(…) перед запуском приложения. Также возможно активировать профили, используя интерфейс Spring ConfigurableEnvironment.
3.4. Файлы конфигурации, специфичные для профилей
Файлы, представляющие собой варианты, специфичные для профилей, как для application.properties (или application.yaml), так и для файлов, ссылающихся через @ConfigurationProperties, рассматриваются как файлы и загружаются. Подробнее см. "Файлы, специфичные для профилей".
4. Ведение журнала
Spring Boot использует Commons Logging для всего внутреннего ведения журнала, но оставляет реализацию основного журнала открытой. Предоставляются конфигурации по умолчанию для Java Util Logging, Log4j2 и Logback. В каждом случае логгеры предварительно настроены для использования вывода на консоль, а также доступен необязательный вывод в файл.
По умолчанию, если вы используете «Стартеры», для ведения журнала используется Logback. Также включено соответствующее маршрутизирование Logback, чтобы убедиться, что зависимые библиотеки, использующие Java Util Logging, Commons Logging, Log4J или SLF4J, работают корректно.
| Существует множество фреймворков ведения журнала для Java. Не беспокойтесь, если вышеприведенный список кажется запутанным. Как правило, вам не нужно изменять свои зависимости ведения журнала, и значения Spring Boot по умолчанию работают прекрасно. |
| При развертывании вашего приложения в контейнере Servlet или прикладном сервере, ведение журнала с помощью API Java Util Logging не маршрутизируется в журналы вашего приложения. Это предотвращает появление в журналах вашего приложения записей ведения журнала, выполненных контейнером или другими приложениями, развернутыми в нём. |
4.1. Формат журнала
Вывод журнала по умолчанию Spring Boot напоминает следующий пример:
2023-08-24T09:32:40.656Z INFO 34602 --- [ main] o.s.b.d.f.logexample.MyApplication : Starting MyApplication using Java 17.0.8 with PID 34602 (/opt/apps/myapp.jar started by myuser in /opt/apps/) 2023-08-24T09:32:40.661Z INFO 34602 --- [ main] o.s.b.d.f.logexample.MyApplication : No active profile set, falling back to 1 default profile: "default" 2023-08-24T09:32:42.277Z INFO 34602 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2023-08-24T09:32:42.292Z INFO 34602 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2023-08-24T09:32:42.292Z INFO 34602 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.12] 2023-08-24T09:32:42.423Z INFO 34602 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2023-08-24T09:32:42.425Z INFO 34602 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1698 ms 2023-08-24T09:32:42.940Z INFO 34602 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2023-08-24T09:32:42.952Z INFO 34602 --- [ main] o.s.b.d.f.logexample.MyApplication : Started MyApplication in 2.93 seconds (process running for 3.354)
Выводится следующее:
-
Дата и время: с точностью до миллисекунд и легко сортируемые.
-
Уровень журнала:
ERROR,WARN,INFO,DEBUGилиTRACE. -
Идентификатор процесса.
-
Разделитель
---, чтобы отличить начало фактических сообщений журнала. -
Имя потока: заключено в квадратные скобки (может быть усечено для вывода на консоль).
-
Имя логгера: обычно это имя класса источника (часто сокращённое).
-
Сообщение журнала.
Logback не имеет уровня FATAL. Он сопоставлен с ERROR. |
4.2. Вывод на консоль
Конфигурация журнала по умолчанию отражает сообщения на консоль по мере их написания. По умолчанию, сообщения уровня ERROR, WARN и INFO регистрируются. Вы также можете включить режим «отладки», запустив ваше приложение с флагом --debug.
$ java -jar myapp.jar --debug Вы также можете указать debug=true в вашем application.properties. |
Когда режим отладки включён, ряд ключевых логгеров (встроенный контейнер, Hibernate и Spring Boot) настроены на вывод дополнительной информации. Включение режима отладки не настраивает ваше приложение на запись всех сообщений с уровнем DEBUG.
В качестве альтернативы, вы можете включить режим «трассировки», запустив ваше приложение с флагом --trace (или trace=true в вашем application.properties). Это позволит включить трассировочный вывод журнала для ряда ключевых логгеров (встроенный контейнер, генерация схемы Hibernate и весь портфель Spring).
4.2.1. Цветной вывод
Если ваш терминал поддерживает ANSI, используется цветной вывод для повышения читаемости. Вы можете установить spring.output.ansi.enabled на поддерживаемое значение, чтобы переопределить автоматическое обнаружение.
Цветное кодирование настраивается с помощью ключевого слова преобразования %clr. В самом простом виде преобразователь окрашивает вывод в соответствии с уровнем журнала, как показано в следующем примере:
%clr(%5p) В следующей таблице описывается сопоставление уровней журнала с цветами:
| Уровень | Цвет |
|---|---|
| Красный |
| Красный |
| Жёлтый |
| Зелёный |
| Зелёный |
| Зелёный |
В качестве альтернативы, вы можете указать цвет или стиль, которые должны быть использованы, указав их как опцию для преобразования. Например, чтобы сделать текст жёлтым, используйте следующее значение:
%clr(%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}){yellow} Следующие цвета и стили поддерживаются:
-
blue -
cyan -
faint -
green -
magenta -
red -
yellow
4.3. Вывод в файл
По умолчанию Spring Boot записывает журналы только в консоль и не создаёт файлы журналов. Если вы хотите записывать файлы журналов помимо вывода на консоль, вам нужно установить свойство logging.file.name или logging.file.path (например, в вашем application.properties).
В следующей таблице показано, как свойства logging.* могут использоваться вместе:
logging.file.name | logging.file.path | Пример | Описание |
|---|---|---|---|
(нет) | (нет) | Только вывод на консоль. | |
Конкретный файл | (нет) |
| Запись в указанный файл журнала. Имена могут быть полными путями или относительными по отношению к текущей директории. |
(нет) | Конкретная директория |
| Запись |
Файлы журналов вращаются, когда достигают 10 МБ и, как и в случае с выводом на консоль, сообщения уровня ERROR, WARN и INFO регистрируются по умолчанию.
Свойства ведения журнала независимы от фактической инфраструктуры ведения журнала. В результате, конкретные ключи конфигурации (такие как logback.configurationFile для Logback) не управляются Spring Boot. |
4.4. Вращение файлов журнала
Если вы используете Logback, можно точно настроить параметры вращения журнала, используя свой файл application.properties или application.yaml. Для всех других систем логгирования вам нужно будет настроить параметры вращения самостоятельно (например, если вы используете Log4j2, то вы можете добавить файл log4j2.xml или log4j2-spring.xml).
Поддерживаются следующие свойства политики вращения:
| Имя | Описание |
|---|---|
| Шаблон имени файла, используемый для создания архивов журналов. |
| Производится ли очистка архивов журналов при запуске приложения. |
| Максимальный размер файла журнала перед его архивацией. |
| Максимальный размер, который могут занимать архивные файлы журналов перед их удалением. |
| Максимальное количество сохраняемых архивных файлов журналов (по умолчанию 7). |
4.5. Уровни журналов
Все поддерживаемые системы логгирования могут иметь уровни логгеров, установленные в Spring Environment (например, в application.properties), используя logging.level.<logger-name>=<level>, где level — это TRACE, DEBUG, INFO, WARN, ERROR, FATAL или OFF. Логгер root можно настроить, используя logging.level.root.
Следующий пример показывает потенциальные параметры логгирования в application.properties:
logging.level.root=warn
logging.level.org.springframework.web=debug
logging.level.org.hibernate=error logging:
level:
root: "warn"
org.springframework.web: "debug"
org.hibernate: "error" Также можно установить уровни логгирования, используя переменные окружения. Например, LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_WEB=DEBUG установит org.springframework.web в DEBUG.
Данный подход будет работать только для логгирования на уровне пакета. Поскольку релаксированная привязка всегда преобразует переменные окружения в нижний регистр, настроить логгирование для отдельного класса таким образом не получится. Если вам нужно настроить логгирование для класса, вы можете использовать переменную SPRING_APPLICATION_JSON. |
4.6. Группы журналов
Часто бывает полезно объединять связанные логгеры, чтобы их можно было настраивать одновременно. Например, вы можете часто изменять уровни логгирования для всех логгеров, связанных с Tomcat, но вам сложно запомнить верхние пакеты.
Чтобы помочь в этом, Spring Boot позволяет определять группы логгирования в вашем Spring Environment. Например, вот как вы можете определить группу «tomcat», добавив ее в ваш файл application.properties:
logging.group.tomcat=org.apache.catalina,org.apache.coyote,org.apache.tomcat logging:
group:
tomcat: "org.apache.catalina,org.apache.coyote,org.apache.tomcat" После определения вы можете изменить уровень для всех логгеров в группе одной строкой:
logging.level.tomcat=trace logging:
level:
tomcat: "trace" Spring Boot включает следующие предопределённые группы логгирования, которые можно использовать «из коробки»:
| Имя | Логгеры |
|---|---|
web |
|
sql |
|
4.7. Использование хука завершения работы
Для освобождения ресурсов логгирования при завершении работы приложения предоставляется хук завершения работы, который будет запускать очистку системы логгирования при выходе JVM. Этот хук регистрируется автоматически, если ваше приложение не развернуто как файл war. Если у вашего приложения сложная иерархия контекстов, хук завершения работы может оказаться не достаточным. Если это так, отключите хук завершения работы и изучите варианты, предоставляемые непосредственно используемой системой логгирования. Например, Logback предлагает селекторы контекста, которые позволяют создавать каждый логгер в собственном контексте. Вы можете использовать свойство logging.register-shutdown-hook для отключения хука завершения работы. Установив значение в false, вы отключите регистрацию. Вы можете установить свойство в файле application.properties или application.yaml:
logging.register-shutdown-hook=false logging:
register-shutdown-hook: false 4.8. Настройка собственного ведения журнала
Различные системы ведения журналов могут быть активированы путем включения соответствующих библиотек в classpath, и их можно дополнительно настроить, предоставив соответствующий файл конфигурации в корне classpath или в местоположении, указанном следующим свойством Spring Environment: logging.config.
Вы можете принудительно заставить Spring Boot использовать определенную систему ведения журналов, используя системное свойство org.springframework.boot.logging.LoggingSystem. Значение должно быть полностью квалифицированным именем класса реализации LoggingSystem. Вы также можете полностью отключить конфигурацию ведения журналов Spring Boot, используя значение none.
Поскольку ведение журнала инициализируется до создания ApplicationContext, невозможно управлять ведением журнала из @PropertySources в файлах Spring @Configuration. Единственный способ изменить систему ведения журнала или полностью отключить ее — это использовать системные свойства. |
В зависимости от вашей системы ведения журналов загружаются следующие файлы:
| Система ведения журнала | Настройка |
|---|---|
Logback |
|
Log4j2 |
|
JDK (Java Util Logging) |
|
Если возможно, мы рекомендуем использовать варианты -spring для конфигурации ведения журнала (например, logback-spring.xml вместо logback.xml). Если вы используете стандартные местоположения конфигурации, Spring не может полностью контролировать инициализацию журнала. |
| Известны проблемы с загрузкой классов в Java Util Logging, которые вызывают проблемы при запуске из «выполняемого jar». Мы рекомендуем избегать его при запуске из «выполняемого jar», если это вообще возможно. |
Для упрощения настройки некоторые другие свойства передаются из Spring Environment в системные свойства. Это позволяет свойствам потребляться конфигурацией системы ведения журналов. Например, установка logging.file.name в application.properties или LOGGING_FILE_NAME в качестве переменной среды приведет к установке системного свойства LOG_FILE. Передаваемые свойства описаны в следующей таблице:
| Среда Spring | Системное свойство | Комментарии |
|---|---|---|
|
| Слово преобразования, используемое при ведении журнала исключений. |
|
| Если определено, используется в конфигурации журнала по умолчанию. |
|
| Если определено, используется в конфигурации журнала по умолчанию. |
|
| Шаблон журнала для использования на консоли (stdout). |
|
| Шаблон приложения для формата даты журнала. |
|
| Кодировка для использования при ведении журнала на консоль. |
|
| Пороговый уровень журнала для использования при ведении журнала на консоль. |
|
| Шаблон журнала для использования в файле (если включен |
|
| Кодировка для использования при ведении журнала в файл (если включен |
|
| Пороговый уровень журнала для использования при ведении журнала в файл. |
|
| Формат для отображения уровня журнала (по умолчанию |
|
| Текущий идентификатор процесса (обнаруженный, если возможно, и если он еще не определен как переменная среды ОС). |
Если вы используете Logback, то также передаются следующие свойства:
| Среда Spring | Системное свойство | Комментарии |
|---|---|---|
|
| Шаблон для имен файлов журналов с переадресацией (по умолчанию |
|
| Очищать ли архивные файлы журналов при запуске. |
|
| Максимальный размер файла журнала. |
|
| Общий размер резервных копий журналов, которые необходимо сохранить. |
|
| Максимальное количество архивных файлов журналов для хранения. |
Все поддерживаемые системы ведения журналов могут обращаться к системным свойствам при анализе своих файлов конфигурации. См. примеры конфигураций по умолчанию в spring-boot.jar:
| Если вы хотите использовать заполнитель в свойстве регистрации, вы должны использовать синтаксис Spring Boot, а не синтаксис базового фреймворка. Обратите внимание, что если вы используете Logback, вы должны использовать |
| Вы можете добавить MDC и другие произвольные данные к строкам логов, переопределив только 2019-08-30 12:30:04.031 user:someone INFO 22174 --- [ nio-8080-exec-0] demo.Controller Handling authenticated request |
4.9. Расширения Logback
Spring Boot включает в себя ряд расширений Logback, которые могут помочь с расширенной настройкой. Вы можете использовать эти расширения в вашем файле конфигурации logback-spring.xml.
Поскольку стандартный файл конфигурации logback.xml загружается слишком рано, вы не можете использовать расширения в нём. Вам необходимо либо использовать logback-spring.xml, либо определить свойство logging.config. |
| Расширения не могут использоваться с функцией сканирования конфигурации Logback configuration scanning. Если вы попытаетесь это сделать, изменения в файле конфигурации приведут к ошибке, подобной одной из следующих, которая будет записана в лог: |
ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProperty], current ElementPath is [[configuration][springProperty]] ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProfile], current ElementPath is [[configuration][springProfile]]
4.9.1. Конфигурация, специфичная для профилей
Тэг <springProfile> позволяет по желанию включать или исключать разделы конфигурации в зависимости от активных профилей Spring. Разделы профилей поддерживаются в любом месте внутри элемента <configuration>. Используйте атрибут name для указания профиля, который принимает конфигурацию. Тэг <springProfile> может содержать имя профиля (например, staging) или выражение профиля. Выражение профиля позволяет выражать более сложную логику профилей, например production & (eu-central | eu-west). Более подробную информацию см. в руководстве по Spring Framework.
В следующем списке показаны три образца профилей:
<springProfile name="staging">
<!-- configuration to be enabled when the "staging" profile is active -->
</springProfile>
<springProfile name="dev | staging">
<!-- configuration to be enabled when the "dev" or "staging" profiles are active -->
</springProfile>
<springProfile name="!production">
<!-- configuration to be enabled when the "production" profile is not active -->
</springProfile> 4.9.2. Свойства среды
Тэг <springProperty> позволяет экспонировать свойства из Spring Environment для использования в Logback. Это может быть полезно, если вы хотите получить доступ к значениям из вашего файла application.properties в вашей конфигурации Logback. Тэг работает аналогично стандартному тегу Logback <property>. Однако вместо указания непосредственного value, вы указываете source свойства (из Environment). Если вам нужно сохранить свойство в другом месте, кроме области local, вы можете использовать атрибут scope. Если вам нужно значение по умолчанию (на случай, если свойство не задано в Environment), вы можете использовать атрибут defaultValue. Следующий пример показывает, как экспонировать свойства для использования в Logback:
<springProperty scope="context" name="fluentHost" source="myapp.fluentd.host"
defaultValue="localhost"/>
<appender name="FLUENT" class="ch.qos.logback.more.appenders.DataFluentAppender">
<remoteHost>${fluentHost}</remoteHost>
...
</appender> Имя свойства source должно быть указано в формате kebab-case (например, my.property-name). Однако свойства могут быть добавлены в Environment, используя более гибкие правила. |
4.10. Расширения Log4j2
Spring Boot включает в себя ряд расширений Log4j2, которые могут помочь с расширенной настройкой. Вы можете использовать эти расширения в любом файле конфигурации log4j2-spring.xml.
Поскольку стандартный файл конфигурации log4j2.xml загружается слишком рано, вы не можете использовать расширения в нём. Вам необходимо либо использовать log4j2-spring.xml, либо определить свойство logging.config. |
Расширения заменяют поддержку Spring Boot, предоставляемую Log4j. Вам следует убедиться, что вы не включаете модуль org.apache.logging.log4j:log4j-spring-boot в свой проект сборки. |
4.10.1. Конфигурация, специфичная для профилей
Тэг <SpringProfile> позволяет по желанию включать или исключать разделы конфигурации в зависимости от активных профилей Spring. Разделы профилей поддерживаются в любом месте внутри элемента <Configuration>. Используйте атрибут name для указания профиля, который принимает конфигурацию. Тэг <SpringProfile> может содержать имя профиля (например, staging) или выражение профиля. Выражение профиля позволяет выражать более сложную логику профилей, например production & (eu-central | eu-west). Более подробную информацию см. в руководстве по Spring Framework.
В следующем списке показаны три образца профилей:
<SpringProfile name="staging">
<!-- configuration to be enabled when the "staging" profile is active -->
</SpringProfile>
<SpringProfile name="dev | staging">
<!-- configuration to be enabled when the "dev" or "staging" profiles are active -->
</SpringProfile>
<SpringProfile name="!production">
<!-- configuration to be enabled when the "production" profile is not active -->
</SpringProfile> 4.10.2. Поиск свойств среды
Если вы хотите использовать свойства из вашего Spring Environment в конфигурации Log4j2, вы можете использовать префикс spring: для поиска свойств. Это может быть полезно, если вы хотите получить доступ к значениям из вашего файла application.properties в вашей конфигурации Log4j2.
Следующий пример показывает, как установить свойство Log4j2 под названием applicationName, которое считывает значение spring.application.name из Spring Environment:
<Properties>
<Property name="applicationName">${spring:spring.application.name}</Property>
</Properties> Ключ для поиска должен быть указан в формате kebab-case (например, my.property-name). |
4.10.3. Системные свойства Log4j2
Log4j2 поддерживает ряд системных свойств, которые можно использовать для настройки различных элементов. Например, системное свойство log4j2.skipJansi можно использовать для настройки, будет ли ConsoleAppender пытаться использовать поток вывода Jansi в Windows.
Все системные свойства, загруженные после инициализации Log4j2, можно получить из Spring Environment. Например, вы можете добавить log4j2.skipJansi=false в свой файл application.properties, чтобы ConsoleAppender использовал Jansi в Windows.
Spring Environment рассматривается только тогда, когда системные свойства и переменные среды ОС не содержат загружаемое значение. |
Системные свойства, загруженные во время ранней инициализации Log4j2, не могут ссылаться на Spring Environment. Например, свойство, которое Log4j2 использует для выбора реализации по умолчанию, используется до того, как Spring Environment становится доступным. |
5. Межкультурный обмен
Spring Boot поддерживает локализованные сообщения, чтобы ваше приложение могло обслуживать пользователей с разными языковыми предпочтениями. По умолчанию Spring Boot ищет наличие ресурсного пакета messages в корне пути к классам.
Автоконфигурация применяется, когда файл свойств по умолчанию для настроенного ресурсного пакета доступен (messages.properties по умолчанию). Если ваш ресурсный пакет содержит только языковые файлы свойств, вам необходимо добавить файл по умолчанию. Если не будет найдено ни одного файла свойств, соответствующего любому из настроенных базовых имён, MessageSource не будет сконфигурирован автоматически. |
Базовое имя ресурсного пакета, а также несколько других атрибутов можно настроить, используя пространство имён spring.messages, как показано в следующем примере:
spring.messages.basename=messages,config.i18n.messages
spring.messages.fallback-to-system-locale=false spring:
messages:
basename: "messages,config.i18n.messages"
fallback-to-system-locale: false spring.messages.basename поддерживает список местоположений, разделённых запятыми, либо квалификатор пакета, либо ресурс, разрешённый из корневого пути к классам. |
См. MessageSourceProperties для получения дополнительной информации о поддерживаемых опциях.
6. JSON
Spring Boot предоставляет интеграцию с тремя библиотеками для работы с JSON:
-
Gson
-
Jackson
-
JSON-B
Jackson является предпочтительной и по умолчанию используемой библиотекой.
6.1. Jackson
Автоконфигурирование для Jackson предоставлено, и Jackson входит в состав spring-boot-starter-json. При наличии Jackson в классе путей, автоматически настраивается бин ObjectMapper. Для настройки конфигурации ObjectMapper предоставляются несколько свойств конфигурации .
6.1.1. Пользовательские сериализаторы и десериализаторы
Если вы используете Jackson для сериализации и десериализации данных JSON, вам может потребоваться написать собственные классы JsonSerializer и JsonDeserializer. Пользовательские сериализаторы обычно регистрируются в Jackson через модуль, но Spring Boot предоставляет альтернативную аннотацию @JsonComponent, которая упрощает непосредственную регистрацию Spring Beans.
Вы можете использовать аннотацию @JsonComponent непосредственно на реализациях JsonSerializer, JsonDeserializer или KeyDeserializer. Вы также можете использовать её для классов, содержащих сериализаторы/десериализаторы, как внутренние классы, как показано в следующем примере:
@JsonComponent
public class MyJsonComponent {
public static class Serializer extends JsonSerializer<MyObject> {
@Override
public void serialize(MyObject value, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
jgen.writeStartObject();
jgen.writeStringField("name", value.getName());
jgen.writeNumberField("age", value.getAge());
jgen.writeEndObject();
}
}
public static class Deserializer extends JsonDeserializer<MyObject> {
@Override
public MyObject deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
ObjectCodec codec = jsonParser.getCodec();
JsonNode tree = codec.readTree(jsonParser);
String name = tree.get("name").textValue();
int age = tree.get("age").intValue();
return new MyObject(name, age);
}
}
}
@JsonComponent
class MyJsonComponent {
class Serializer : JsonSerializer<MyObject>() {
@Throws(IOException::class)
override fun serialize(value: MyObject, jgen: JsonGenerator, serializers: SerializerProvider) {
jgen.writeStartObject()
jgen.writeStringField("name", value.name)
jgen.writeNumberField("age", value.age)
jgen.writeEndObject()
}
}
class Deserializer : JsonDeserializer<MyObject>() {
@Throws(IOException::class, JsonProcessingException::class)
override fun deserialize(jsonParser: JsonParser, ctxt: DeserializationContext): MyObject {
val codec = jsonParser.codec
val tree = codec.readTree<JsonNode>(jsonParser)
val name = tree["name"].textValue()
val age = tree["age"].intValue()
return MyObject(name, age)
}
}
}
Все @JsonComponent бины в ApplicationContext автоматически регистрируются в Jackson. Поскольку @JsonComponent мета-аннотирована @Component, применяются обычные правила сканирования компонентов.
Spring Boot также предоставляет JsonObjectSerializer и JsonObjectDeserializer базовые классы, которые предоставляют полезные альтернативы стандартным версиям Jackson при сериализации объектов. Подробнее см. JsonObjectSerializer и JsonObjectDeserializer в Javadoc.
Приведённый выше пример можно переписать, используя JsonObjectSerializer/JsonObjectDeserializer следующим образом:
@JsonComponent
public class MyJsonComponent {
public static class Serializer extends JsonObjectSerializer<MyObject> {
@Override
protected void serializeObject(MyObject value, JsonGenerator jgen, SerializerProvider provider)
throws IOException {
jgen.writeStringField("name", value.getName());
jgen.writeNumberField("age", value.getAge());
}
}
public static class Deserializer extends JsonObjectDeserializer<MyObject> {
@Override
protected MyObject deserializeObject(JsonParser jsonParser, DeserializationContext context, ObjectCodec codec,
JsonNode tree) throws IOException {
String name = nullSafeValue(tree.get("name"), String.class);
int age = nullSafeValue(tree.get("age"), Integer.class);
return new MyObject(name, age);
}
}
}
`object`
@JsonComponent
class MyJsonComponent {
class Serializer : JsonObjectSerializer<MyObject>() {
@Throws(IOException::class)
override fun serializeObject(value: MyObject, jgen: JsonGenerator, provider: SerializerProvider) {
jgen.writeStringField("name", value.name)
jgen.writeNumberField("age", value.age)
}
}
class Deserializer : JsonObjectDeserializer<MyObject>() {
@Throws(IOException::class)
override fun deserializeObject(jsonParser: JsonParser, context: DeserializationContext,
codec: ObjectCodec, tree: JsonNode): MyObject {
val name = nullSafeValue(tree["name"], String::class.java)
val age = nullSafeValue(tree["age"], Int::class.java)
return MyObject(name, age)
}
}
}
6.1.2. Mixins
Jackson поддерживает mixins, которые могут использоваться для добавления дополнительных аннотаций в уже объявленные на целевом классе. Автоконфигурация Jackson Spring Boot будет сканировать пакеты вашего приложения на наличие классов, аннотированных с помощью @JsonMixin, и регистрировать их с помощью автоматически настроенного ObjectMapper. Регистрация выполняется механизмом JsonMixinModule Spring Boot.
6.2. Gson
Автоконфигурация для Gson предоставляется. При наличии Gson в классе путей, автоматически настраивается бин Gson. Предоставляются несколько свойств конфигурации spring.gson.* для настройки конфигурации. Для большего контроля можно использовать один или несколько бинов GsonBuilderCustomizer.
6.3. JSON-B
Автоконфигурация для JSON-B предоставляется. При наличии API JSON-B и реализации в классе путей, бин Jsonb будет настроен автоматически. Предпочитаемой реализацией JSON-B является Eclipse Yasson, для которого обеспечивается управление зависимостями.
7. Выполнение и планирование задач
В отсутствии бинa Executor в контексте, Spring Boot автоматически настраивает ThreadPoolTaskExecutor с разумными значениями по умолчанию, которые могут быть автоматически связаны с асинхронным выполнением задач (@EnableAsync) и асинхронной обработкой запросов Spring MVC.
| Если вы определили пользовательский Автоматически настроенный |
Потоковый пул использует 8 потоков ядра, которые могут увеличиваться и уменьшаться в соответствии с нагрузкой. Эти значения по умолчанию могут быть настроены с помощью пространства имён spring.task.execution, как показано в следующем примере:
spring.task.execution.pool.max-size=16
spring.task.execution.pool.queue-capacity=100
spring.task.execution.pool.keep-alive=10s spring:
task:
execution:
pool:
max-size: 16
queue-capacity: 100
keep-alive: "10s" Это изменяет пул потоков на использование ограниченной очереди, так что когда очередь заполняется (100 задач), пул потоков увеличивается до максимальных 16 потоков. Сжатие пула происходит более агрессивно, поскольку потоки изымаются, когда они простаивают в течение 10 секунд (вместо 60 секунд по умолчанию).
Также может быть автоматически настроен ThreadPoolTaskScheduler, если нужно его связать с выполнением запланированных задач (например, с помощью @EnableScheduling). Потоковый пул использует один поток по умолчанию, и его параметры можно настроить с помощью пространства имён spring.task.scheduling, как показано в следующем примере:
spring.task.scheduling.thread-name-prefix=scheduling-
spring.task.scheduling.pool.size=2 spring:
task:
scheduling:
thread-name-prefix: "scheduling-"
pool:
size: 2 Если необходимо создать пользовательский исполняющий агент или планировщик, в контексте становятся доступными как бин TaskExecutorBuilder, так и бин TaskSchedulerBuilder.
8. Тестирование
Spring Boot предоставляет ряд утилит и аннотаций, которые помогут при тестировании вашего приложения. Поддержка тестирования обеспечивается двумя модулями: spring-boot-test содержит основные элементы, а spring-boot-test-autoconfigure поддерживает автоматическую настройку для тестов.
Большинство разработчиков используют spring-boot-starter-test «Starter», который импортирует как модули тестирования Spring Boot, так и JUnit Jupiter, AssertJ, Hamcrest и ряд других полезных библиотек.
| Если у вас есть тесты, использующие JUnit 4, можно использовать винтажный движок JUnit 5 для их выполнения. Для использования винтажного движка добавьте зависимость от |
hamcrest-core исключен в пользу org.hamcrest:hamcrest, который является частью spring-boot-starter-test.
8.1. Зависимости области тестирования
spring-boot-starter-test «Starter» (в test scope) содержит следующие предоставляемые библиотеки:
-
JUnit 5: Фактический стандарт для модульного тестирования Java-приложений.
-
Spring Test и Spring Boot Test: Утилиты и поддержка интеграционных тестов для Spring Boot-приложений.
-
AssertJ: Библиотека флюидных утверждений.
-
Hamcrest: Библиотека объектов-матчеров (также известных как ограничения или предикаты).
-
Mockito: Java-фреймворк для создания моков.
-
JSONassert: Библиотека утверждений для JSON.
-
JsonPath: XPath для JSON.
Мы обычно считаем эти общие библиотеки полезными при написании тестов. Если эти библиотеки не подходят для ваших нужд, вы можете добавить дополнительные зависимости для тестов.
8.2. Тестирование Spring-приложений
Одним из основных преимуществ инъекции зависимостей является то, что это должно упростить тестирование вашего кода. Вы можете создавать экземпляры объектов, используя оператор new, даже не вовлекая Spring. Вы также можете использовать объекты-моки вместо реальных зависимостей.
Часто вам нужно выйти за рамки модульного тестирования и перейти к интеграционному тестированию (с Spring ApplicationContext). Полезно иметь возможность выполнять интеграционное тестирование без необходимости развертывания вашего приложения или подключения к другой инфраструктуре.
В Spring Framework есть специальный модуль для таких интеграционных тестов. Вы можете объявить зависимость непосредственно от org.springframework:spring-test или использовать spring-boot-starter-test «Starter», чтобы подключить его транзитивно.
Если вы раньше не использовали модуль spring-test, начните с прочтения соответствующего раздела документации по Spring Framework.
8.3. Тестирование приложений Spring Boot
Приложение Spring Boot является приложением Spring ApplicationContext, поэтому для его тестирования не требуется ничего особенного, кроме того, что обычно делается с обычным контекстом Spring.
Внешние свойства, логирование и другие функции Spring Boot устанавливаются в контексте по умолчанию только в том случае, если вы используете SpringApplication для его создания. |
Spring Boot предоставляет аннотацию @SpringBootTest, которая может быть использована в качестве альтернативы стандартной аннотации spring-test @ContextConfiguration, когда требуются функции Spring Boot. Аннотация работает, создавая ApplicationContext, используемый в ваших тестах, через SpringApplication. В дополнение к @SpringBootTest предоставляется ряд других аннотаций для тестирования более специфических фрагментов приложения.
Если вы используете JUnit 4, не забудьте также добавить @RunWith(SpringRunner.class) в свой тест, иначе аннотации будут проигнорированы. Если вы используете JUnit 5, нет необходимости добавлять эквивалентную @ExtendWith(SpringExtension.class), поскольку @SpringBootTest и другие @…Test аннотации уже аннотированы ею. |
По умолчанию @SpringBootTest не будет запускать сервер. Вы можете использовать атрибут webEnvironment аннотации @SpringBootTest для дальнейшей настройки выполнения ваших тестов:
-
MOCK(По умолчанию): Загружает веб-ApplicationContextи предоставляет имитационную веб-среду. Встроенные серверы не запускаются при использовании этой аннотации. Если веб-среда недоступна в вашем классе, этот режим прозрачно переходит к созданию обычного не-веб-ApplicationContext. Его можно использовать совместно с@AutoConfigureMockMvcили@AutoConfigureWebTestClientдля имитационного тестирования вашего веб-приложения. -
RANDOM_PORT: ЗагружаетWebServerApplicationContextи предоставляет реальную веб-среду. Встроенные серверы запускаются и слушают случайный порт. -
DEFINED_PORT: ЗагружаетWebServerApplicationContextи предоставляет реальную веб-среду. Встроенные серверы запускаются и слушают определенный порт (из вашегоapplication.properties) или порт по умолчанию8080. -
NONE: ЗагружаетApplicationContext, используяSpringApplication, но не предоставляет никакую веб-среду (имитационную или иную).
Если ваш тест @Transactional, он отменяет транзакцию в конце каждого тестового метода по умолчанию. Однако, поскольку использование данного метода с RANDOM_PORT или DEFINED_PORT неявно предоставляет реальную среду сервлета, HTTP-клиент и сервер работают в отдельных потоках и, следовательно, в отдельных транзакциях. Любая транзакция, инициированная на сервере, в этом случае не отменяется. |
@SpringBootTest с webEnvironment = WebEnvironment.RANDOM_PORT также запустит сервер управления на отдельном случайном порту, если ваше приложение использует другой порт для сервера управления. |
8.3.1. Определение типа веб-приложения
Если Spring MVC доступен, настраивается обычный контекст приложения на основе MVC. Если у вас только Spring WebFlux, мы определим это и настроим контекст приложения на основе WebFlux вместо этого.
Если оба присутствуют, Spring MVC имеет приоритет. Если вы хотите протестировать реактивное веб-приложение в этом сценарии, необходимо установить свойство spring.main.web-application-type:
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {
// ...
}
@SpringBootTest(properties = ["spring.main.web-application-type=reactive"])
class MyWebFluxTests {
// ...
}
8.3.2. Определение конфигурации теста
Если вы знакомы с Spring Test Framework, вы, возможно, привыкли использовать @ContextConfiguration(classes=…) для указания, какой Spring @Configuration загружать. Или, возможно, вы часто использовали вложенные @Configuration классы в своих тестах.
При тестировании приложений Spring Boot это часто не требуется. Аннотации @*Test Spring Boot автоматически ищут основную конфигурацию, когда вы не определяете её явно.
Алгоритм поиска работает вверх с пакета, содержащего тест, до тех пор, пока не найдет класс, помеченный аннотациями @SpringBootApplication или @SpringBootConfiguration. При условии разумной структуры вашего кода, ваша основная конфигурация обычно находится.
| Если вы используете аннотацию теста для тестирования более специфического фрагмента вашего приложения, избегайте добавления параметров конфигурации, специфичных для конкретной области в главном методе класса приложения. Основная конфигурация сканирования компонентов |
Если вы хотите настроить основную конфигурацию, вы можете использовать вложенный класс @TestConfiguration. В отличие от вложенного класса @Configuration, который бы использовался вместо основной конфигурации вашего приложения, вложенный класс @TestConfiguration используется дополнительно к основной конфигурации вашего приложения.
| Тестовый фреймворк Spring кэширует контексты приложений между тестами. Поэтому, пока ваши тесты используют одну и ту же конфигурацию (неважно, как она обнаружена), потенциально ресурсоёмкий процесс загрузки контекста происходит только один раз. |
8.3.3. Использование главного метода конфигурации теста
Обычно конфигурация теста, найденная @SpringBootTest, будет вашим основным @SpringBootApplication. В большинстве хорошо структурированных приложений этот класс конфигурации также будет включать метод main, используемый для запуска приложения.
Например, ниже представлен распространённый шаблон кода для типичного приложения Spring Boot:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
В примере выше метод main не делает ничего, кроме делегирования вызова в SpringApplication.run. Однако возможно иметь более сложный метод main, который применяет настройки перед вызовом SpringApplication.run.
Например, вот приложение, которое изменяет режим баннера и задаёт дополнительные профили:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.setAdditionalProfiles("myprofile");
application.run(args);
}
}
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args) {
setBannerMode(Banner.Mode.OFF)
setAdditionalProfiles("myprofile");
}
}
Поскольку настройки в методе main могут влиять на полученный ApplicationContext, возможно, вам также захочется использовать метод main для создания ApplicationContext, используемого в ваших тестах. По умолчанию, @SpringBootTest не будет вызывать ваш метод main, а вместо этого напрямую использует класс для создания ApplicationContext.
Если вы хотите изменить это поведение, вы можете изменить атрибут useMainMethod аннотации @SpringBootTest на UseMainMethod.ALWAYS или UseMainMethod.WHEN_AVAILABLE. При установке на ALWAYS тест завершится ошибкой, если не найден метод main. При установке на WHEN_AVAILABLE метод main будет использован, если он доступен, в противном случае будет использован стандартный механизм загрузки.
Например, следующий тест вызовет метод main класса MyApplication, чтобы создать ApplicationContext. Если главный метод устанавливает дополнительные профили, они будут активны при запуске ApplicationContext.
@SpringBootTest(useMainMethod = UseMainMethod.ALWAYS)
class MyApplicationTests {
@Test
void exampleTest() {
// ...
}
}
@SpringBootTest(useMainMethod = UseMainMethod.ALWAYS)
class MyApplicationTests {
@Test
fun exampleTest() {
// ...
}
}
8.3.4. Исключение тестовой конфигурации
Если ваше приложение использует сканирование компонентов (например, если вы используете @SpringBootApplication или @ComponentScan), вы можете случайно обнаружить классы конфигурации верхнего уровня, созданные только для определённых тестов, которые подхватываются везде.
Как мы уже видели, @TestConfiguration может быть использован для внутреннего класса теста для настройки основной конфигурации. При размещении на классе верхнего уровня, @TestConfiguration указывает, что классы в src/test/java не должны подхватываться сканированием. Тогда вы можете импортировать этот класс явно, где он необходим, как показано в следующем примере:
@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {
@Test
void exampleTest() {
// ...
}
}
@SpringBootTest
@Import(MyTestsConfiguration::class)
class MyTests {
@Test
fun exampleTest() {
// ...
}
}
Если вы напрямую используете @ComponentScan (то есть, не через @SpringBootApplication), вам необходимо зарегистрировать TypeExcludeFilter с ним. Подробнее см. Javadoc. |
8.3.5. Использование аргументов приложения
Если ваше приложение ожидает аргументы, вы можете использовать @SpringBootTest для их инъекции с помощью атрибута args.
@SpringBootTest(args = "--app.test=one")
class MyApplicationArgumentTests {
@Test
void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
assertThat(args.getOptionNames()).containsOnly("app.test");
assertThat(args.getOptionValues("app.test")).containsOnly("one");
}
}
@SpringBootTest(args = ["--app.test=one"])
class MyApplicationArgumentTests {
@Test
fun applicationArgumentsPopulated(@Autowired args: ApplicationArguments) {
assertThat(args.optionNames).containsOnly("app.test")
assertThat(args.getOptionValues("app.test")).containsOnly("one")
}
}
8.3.6. Тестирование с эмуляцией окружения
По умолчанию @SpringBootTest не запускает сервер, а вместо этого настраивает эмулированную среду для тестирования веб-точек входа.
С помощью Spring MVC, мы можем запросить наши веб-точки входа, используя MockMvc или WebTestClient, как показано в следующем примере:
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
void testWithMockMvc(@Autowired MockMvc mvc) throws Exception {
mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
}
// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
@Test
void testWithWebTestClient(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
fun testWithMockMvc(@Autowired mvc: MockMvc) {
mvc.perform(MockMvcRequestBuilders.get("/")).andExpect(MockMvcResultMatchers.status().isOk)
.andExpect(MockMvcResultMatchers.content().string("Hello World"))
}
// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
@Test
fun testWithWebTestClient(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
Если вы хотите сфокусироваться только на веб-слое и не запускать полный ApplicationContext, рассмотрите использование @WebMvcTest вместо этого. |
С Spring WebFlux точками входа, вы можете использовать WebTestClient, как показано в следующем примере:
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
@Test
fun exampleTest(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
| Тестирование в эмулированной среде обычно быстрее, чем работа с полным контейнером сервлетов. Однако, поскольку эмуляция происходит на уровне Spring MVC, код, зависящий от поведения контейнера сервлетов на более низком уровне, не может быть напрямую протестирован с помощью MockMvc. Например, обработка ошибок Spring Boot основана на поддержке «страницы ошибки», предоставляемой контейнером сервлетов. Это означает, что, хотя вы можете протестировать, что ваш слой MVC выбрасывает и обрабатывает исключения как ожидается, вы не можете напрямую проверить, что определённая собственная страница ошибки отображается. Если вам нужно протестировать эти проблемы более низкого уровня, вы можете запустить полностью работающий сервер, как описано в следующем разделе. |
8.3.7. Тестирование с запущенным сервером
Если вам нужно запустить полностью работающий сервер, рекомендуется использовать случайные порты. Если вы используете @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT), доступный порт выбирается случайным образом каждый раз, когда запускается ваш тест.
Аннотация @LocalServerPort может быть использована для инъекции фактического используемого порта в ваш тест. Для удобства, тесты, которые нуждаются в выполнении REST-запросов к запущенному серверу, могут дополнительно @Autowire WebTestClient, который разрешает относительные ссылки к запущенному серверу и поставляется с отдельным API для проверки ответов, как показано в следующем примере:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {
@Test
fun exampleTest(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
WebTestClient может быть использован как для живых серверов, так и для эмулированных сред. |
Эта настройка требует spring-webflux в классе. Если вы не можете или не хотите добавлять webflux, Spring Boot также предоставляет TestRestTemplate возможность:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {
@Test
void exampleTest(@Autowired TestRestTemplate restTemplate) {
String body = restTemplate.getForObject("/", String.class);
assertThat(body).isEqualTo("Hello World");
}
}
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {
@Test
fun exampleTest(@Autowired restTemplate: TestRestTemplate) {
val body = restTemplate.getForObject("/", String::class.java)
assertThat(body).isEqualTo("Hello World")
}
}
8.3.8. Настройка WebTestClient
Для настройки компонента WebTestClient, настройте компонент WebTestClientBuilderCustomizer. Любые такие компоненты вызываются с помощью WebTestClient.Builder, используемого для создания WebTestClient.
8.3.9. Использование JMX
Поскольку тестовая рамка кеширует контекст, JMX по умолчанию отключен, чтобы предотвратить регистрацию идентичных компонентов в одной и той же области. Если такому тесту нужен доступ к MBeanServer, рассмотрите возможность пометить его как изменённый:
@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class MyJmxTests {
@Autowired
private MBeanServer mBeanServer;
@Test
void exampleTest() {
assertThat(this.mBeanServer.getDomains()).contains("java.lang");
// ...
}
}
@SpringBootTest(properties = ["spring.jmx.enabled=true"])
@DirtiesContext
class MyJmxTests(@Autowired val mBeanServer: MBeanServer) {
@Test
fun exampleTest() {
assertThat(mBeanServer.domains).contains("java.lang")
// ...
}
}
8.3.10. Использование метрик
Независимо от вашего classpath, реестры счетчиков, за исключением реестров в памяти, не настраиваются автоматически при использовании @SpringBootTest.
Если вам нужно экспортировать метрики в другой бэкенд в рамках интеграционного теста, отметьте его аннотацией @AutoConfigureObservability.
8.3.11. Использование отслеживания
Независимо от вашего classpath, отслеживание не настраивается автоматически при использовании @SpringBootTest.
Если вам нужно отслеживание в рамках интеграционного теста, отметьте его аннотацией @AutoConfigureObservability.
8.3.12. Моделирование и подглядывание за биндами
При выполнении тестов иногда необходимо смоделировать определённые компоненты в контексте приложения. Например, у вас может быть фасад над какой-либо удалённой службой, которая недоступна во время разработки. Моделирование также может быть полезным, когда вы хотите смоделировать ошибки, которые могут быть трудно воспроизвести в реальной среде.
Spring Boot включает аннотацию @MockBean, которая может быть использована для определения модели Mockito для бинда внутри вашего ApplicationContext. Вы можете использовать аннотацию для добавления новых бинов или замены одного существующего определения бинда. Аннотация может быть использована напрямую на тестовых классах, на полях в вашем тесте или на @Configuration классах и полях. При использовании на поле экземпляр созданной модели также инжектируется. Модели бинов автоматически сбрасываются после каждого тестового метода.
| Если ваш тест использует одну из тестовых аннотаций Spring Boot (таких как Java Kotlin |
Следующий пример заменяет существующий RemoteService бинд моделью:
@SpringBootTest
class MyTests {
@Autowired
private Reverser reverser;
@MockBean
private RemoteService remoteService;
@Test
void exampleTest() {
given(this.remoteService.getValue()).willReturn("spring");
String reverse = this.reverser.getReverseValue(); // Calls injected RemoteService
assertThat(reverse).isEqualTo("gnirps");
}
}
@SpringBootTest
class MyTests(@Autowired val reverser: Reverser, @MockBean val remoteService: RemoteService) {
@Test
fun exampleTest() {
given(remoteService.value).willReturn("spring")
val reverse = reverser.reverseValue // Calls injected RemoteService
assertThat(reverse).isEqualTo("gnirps")
}
}
@MockBean не может быть использована для моделирования поведения бинда, который выполняется во время обновления контекста приложения. К моменту выполнения теста обновление контекста приложения уже завершено, и слишком поздно настраивать смоделированное поведение. В этой ситуации рекомендуется использовать метод @Bean для создания и настройки модели. |
Кроме того, вы можете использовать @SpyBean для обертывания любого существующего бинда с помощью модели Mockito spy. Смотрите Javadoc для получения полной информации.
Прокси CGLib, такие как те, что созданы для бинов со scoped, объявляют проксированные методы как final. Это предотвращает правильную работу Mockito, так как оно не может смоделировать или подглядывать за final методами в своей конфигурации по умолчанию. Если вы хотите смоделировать или подглядывать за таким бином, настройте Mockito, чтобы использовать его встроенный создатель модели, добавив org.mockito:mockito-inline в зависимости ваших тестов. Это позволит Mockito моделировать и подглядывать за final методами. |
Хотя тестовая среда Spring кэширует контексты приложения между тестами и повторно использует контекст для тестов, которые используют одну и ту же конфигурацию, использование @MockBean или @SpyBean влияет на ключ кэша, что, скорее всего, увеличит количество контекстов. |
Если вы используете @SpyBean для подглядывания за бином с @Cacheable методами, которые ссылаются на параметры по имени, ваше приложение должно быть скомпилировано с -parameters. Это гарантирует, что имена параметров доступны инфраструктуре кэширования после подглядывания за бином. |
Когда вы используете @SpyBean для подглядывания за бином, проксированным Spring, вам может потребоваться удалить прокси Spring в некоторых ситуациях, например, при настройке ожиданий с помощью given или when. Используйте AopTestUtils.getTargetObject(yourProxiedSpy) для этого. |
8.3.13. Автоконфигурируемые тесты
Система автоконфигурации Spring Boot хорошо работает для приложений, но иногда может быть слишком сложной для тестов. Зачастую полезно загрузить только те части конфигурации, которые необходимы для тестирования "фрагмента" вашего приложения. Например, вы можете захотеть проверить, что контроллеры Spring MVC правильно отображают URL-адреса, и не хотите вовлекать вызовы к базе данных в эти тесты, или вы можете захотеть протестировать сущности JPA, и вас не интересует веб-слой при запуске этих тестов.
Модуль spring-boot-test-autoconfigure включает ряд аннотаций, которые можно использовать для автоматической конфигурации таких "фрагментов". Каждая из них работает аналогичным образом, предоставляя аннотацию @…Test, которая загружает ApplicationContext, и одну или несколько аннотаций @AutoConfigure…, которые можно использовать для настройки параметров автоконфигурации.
Каждый фрагмент ограничивает сканирование компонентов соответствующими компонентами и загружает очень ограниченный набор классов автоконфигурации. Если вам нужно исключить один из них, большинство аннотаций @…Test предоставляют атрибут excludeAutoConfiguration. В качестве альтернативы, вы можете использовать @ImportAutoConfiguration#exclude. |
Включение нескольких "фрагментов" с использованием нескольких аннотаций @…Test в одном тесте не поддерживается. Если вам нужны несколько "фрагментов", выберите одну из аннотаций @…Test и вручную включите аннотации @AutoConfigure… других "фрагментов". |
Также возможно использовать аннотации @AutoConfigure… со стандартной аннотацией @SpringBootTest. Вы можете использовать это сочетание, если вас не интересует "фрагментация" вашего приложения, но вы хотите некоторые из автоконфигурируемых тестовых бинов. |
8.3.14. Автоконфигурируемые тесты JSON
Для проверки корректности сериализации и десериализации объектов JSON вы можете использовать аннотацию @JsonTest. @JsonTest автоматически настраивает доступный поддерживаемый маппер JSON, который может быть одной из следующих библиотек:
-
Jackson
ObjectMapper, любые@JsonComponentбинды и любые JacksonModule -
Gson -
Jsonb
Список автоконфигураций, которые включены аннотацией @JsonTest, можно найти в приложении. |
Если вам нужно настроить элементы автоконфигурации, вы можете использовать аннотацию @AutoConfigureJsonTesters.
Spring Boot включает помощники на основе AssertJ, которые работают с библиотеками JSONAssert и JsonPath для проверки того, что JSON отображается как ожидается. Классы JacksonTester, GsonTester, JsonbTester и BasicJsonTester могут быть использованы для Jackson, Gson, Jsonb и строк соответственно. Любые вспомогательные поля в тестовом классе могут быть @Autowired при использовании @JsonTest. Следующий пример демонстрирует тестовый класс для Jackson:
@JsonTest
class MyJsonTests {
@Autowired
private JacksonTester<VehicleDetails> json;
@Test
void serialize() throws Exception {
VehicleDetails details = new VehicleDetails("Honda", "Civic");
// Assert against a `.json` file in the same package as the test
assertThat(this.json.write(details)).isEqualToJson("expected.json");
// Or use JSON path based assertions
assertThat(this.json.write(details)).hasJsonPathStringValue("@.make");
assertThat(this.json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda");
}
@Test
void deserialize() throws Exception {
String content = "{\"make\":\"Ford\",\"model\":\"Focus\"}";
assertThat(this.json.parse(content)).isEqualTo(new VehicleDetails("Ford", "Focus"));
assertThat(this.json.parseObject(content).getMake()).isEqualTo("Ford");
}
}
@JsonTest
class MyJsonTests(@Autowired val json: JacksonTester<VehicleDetails>) {
@Test
fun serialize() {
val details = VehicleDetails("Honda", "Civic")
// Assert against a `.json` file in the same package as the test
assertThat(json.write(details)).isEqualToJson("expected.json")
// Or use JSON path based assertions
assertThat(json.write(details)).hasJsonPathStringValue("@.make")
assertThat(json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda")
}
@Test
fun deserialize() {
val content = "{\"make\":\"Ford\",\"model\":\"Focus\"}"
assertThat(json.parse(content)).isEqualTo(VehicleDetails("Ford", "Focus"))
assertThat(json.parseObject(content).make).isEqualTo("Ford")
}
}
Классы помощников JSON также могут быть использованы непосредственно в стандартных юнит-тестах. Для этого вызовите метод initFields помощника в вашем методе @Before, если вы не используете @JsonTest. |
Если вы используете помощники Spring Boot на основе AssertJ для проверки числового значения по заданному пути JSON, вы, возможно, не сможете использовать isEqualTo в зависимости от типа. Вместо этого вы можете использовать satisfies AssertJ для проверки того, что значение соответствует заданному условию. Например, следующий пример проверяет, что фактическое число является значением типа float, близким к 0.15 с отклонением в 0.01.
@Test
void someTest() throws Exception {
SomeObject value = new SomeObject(0.152f);
assertThat(this.json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
.satisfies((number) -> assertThat(number.floatValue()).isCloseTo(0.15f, within(0.01f)));
}
@Test
fun someTest() {
val value = SomeObject(0.152f)
assertThat(json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
.satisfies(ThrowingConsumer { number ->
assertThat(number.toFloat()).isCloseTo(0.15f, within(0.01f))
})
}
8.3.15. Автоконфигурируемые тесты Spring MVC
Для проверки корректной работы контроллеров Spring MVC используйте аннотацию @WebMvcTest. @WebMvcTest автоматически настраивает инфраструктуру Spring MVC и ограничивает сканирование бинов до @Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, HandlerInterceptor, WebMvcConfigurer, WebMvcRegistrations и HandlerMethodArgumentResolver. Регулярные бины @Component и @ConfigurationProperties не будут сканироваться при использовании аннотации @WebMvcTest. Аннотация @EnableConfigurationProperties может быть использована для включения бинов @ConfigurationProperties.
Список настроек автоконфигурации, активируемых @WebMvcTest, можно найти в приложении. |
Если вам необходимо зарегистрировать дополнительные компоненты, такие как Jackson Module, вы можете импортировать дополнительные конфигурационные классы, используя @Import в вашем тесте. |
Часто @WebMvcTest ограничивается одним контроллером и используется в сочетании с @MockBean для предоставления имитирующих реализаций необходимых коллабораторов.
@WebMvcTest также автоматически настраивает MockMvc. Мок MVC предоставляет мощный способ быстрого тестирования контроллеров MVC без необходимости запуска полного HTTP-сервера.
Вы также можете автоматически настроить MockMvc в не-@WebMvcTest (таком как @SpringBootTest) путем аннотирования его @AutoConfigureMockMvc. Следующий пример использует MockMvc: |
@WebMvcTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private MockMvc mvc;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andExpect(content().string("Honda Civic"));
}
}
@WebMvcTest(UserVehicleController::class)
class MyControllerTests(@Autowired val mvc: MockMvc) {
@MockBean
lateinit var userVehicleService: UserVehicleService
@Test
fun testExample() {
given(userVehicleService.getVehicleDetails("sboot"))
.willReturn(VehicleDetails("Honda", "Civic"))
mvc.perform(MockMvcRequestBuilders.get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(MockMvcResultMatchers.status().isOk)
.andExpect(MockMvcResultMatchers.content().string("Honda Civic"))
}
}
Если вам нужно настроить элементы автоконфигурации (например, когда должны применяться фильтры сервлетов), вы можете использовать атрибуты в аннотации @AutoConfigureMockMvc. |
Если вы используете HtmlUnit и Selenium, автоконфигурация также предоставляет бин HtmlUnit WebClient и/или бин Selenium WebDriver. Следующий пример использует HtmlUnit:
@WebMvcTest(UserVehicleController.class)
class MyHtmlUnitTests {
@Autowired
private WebClient webClient;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot")).willReturn(new VehicleDetails("Honda", "Civic"));
HtmlPage page = this.webClient.getPage("/sboot/vehicle.html");
assertThat(page.getBody().getTextContent()).isEqualTo("Honda Civic");
}
}
@WebMvcTest(UserVehicleController::class)
class MyHtmlUnitTests(@Autowired val webClient: WebClient) {
@MockBean
lateinit var userVehicleService: UserVehicleService
@Test
fun testExample() {
given(userVehicleService.getVehicleDetails("sboot")).willReturn(VehicleDetails("Honda", "Civic"))
val page = webClient.getPage<HtmlPage>("/sboot/vehicle.html")
assertThat(page.body.textContent).isEqualTo("Honda Civic")
}
}
По умолчанию Spring Boot помещает бины WebDriver в специальный «scope», чтобы обеспечить выход драйвера после каждого теста и инъекцию нового экземпляра. Если вам не нужно такое поведение, добавьте @Scope("singleton") в ваше определение WebDriver @Bean. |
Scope webDriver, созданный Spring Boot, заменит любой определенный вами scope с таким же именем. Если вы определите свой собственный scope webDriver, он может перестать работать при использовании @WebMvcTest. |
Если у вас Spring Security в classpath, @WebMvcTest также будет сканировать бины WebSecurityConfigurer. Вместо того, чтобы полностью отключать безопасность для таких тестов, вы можете использовать поддержку Spring Security. Подробнее о том, как использовать поддержку Spring Security MockMvc, можно найти в разделе howto.html.
| Иногда написания тестов Spring MVC недостаточно; Spring Boot может помочь вам выполнить полные тесты «конец-конец» с реальным сервером. |
8.3.16. Автоконфигурируемые тесты Spring WebFlux
Для проверки корректной работы контроллеров Spring WebFlux используйте аннотацию @WebFluxTest. @WebFluxTest автоматически настраивает инфраструктуру Spring WebFlux и ограничивает сканирование бинов до @Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, WebFilter и WebFluxConfigurer. Регулярные бины @Component и @ConfigurationProperties не будут сканироваться при использовании аннотации @WebFluxTest. @EnableConfigurationProperties может быть использован для включения бинов @ConfigurationProperties.
Список автоконфигураций, активируемых @WebFluxTest, можно найти в приложении. |
Если вам нужно зарегистрировать дополнительные компоненты, такие как Jackson Module, вы можете импортировать дополнительные конфигурационные классы, используя @Import в вашем тесте. |
Часто @WebFluxTest ограничивается одним контроллером и используется в сочетании с аннотацией @MockBean для предоставления имитационных реализаций необходимых коллабораторов.
@WebFluxTest также автоматически настраивает WebTestClient, что предоставляет мощный способ быстрого тестирования контроллеров WebFlux без необходимости запуска полного HTTP-сервера.
Вы также можете настроить WebTestClient в не-@WebFluxTest (таком как @SpringBootTest) путем аннотирования его @AutoConfigureWebTestClient. Следующий пример показывает класс, использующий как @WebFluxTest, так и WebTestClient: |
@WebFluxTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private WebTestClient webClient;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Honda Civic");
}
}
@WebFluxTest(UserVehicleController::class)
class MyControllerTests(@Autowired val webClient: WebTestClient) {
@MockBean
lateinit var userVehicleService: UserVehicleService
@Test
fun testExample() {
given(userVehicleService.getVehicleDetails("sboot"))
.willReturn(VehicleDetails("Honda", "Civic"))
webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Honda Civic")
}
}
Данная настройка поддерживается только приложениями WebFlux, так как использование WebTestClient в имитируемом веб-приложении в настоящее время работает только с WebFlux. |
@WebFluxTest не может обнаружить маршруты, зарегистрированные через функциональную веб-фреймворк. Для тестирования бинов RouterFunction в контексте рассмотрите импорт вашего RouterFunction самостоятельно, используя @Import или @SpringBootTest. |
@WebFluxTest не может обнаружить пользовательскую конфигурацию безопасности, зарегистрированную как @Bean типа SecurityWebFilterChain. Чтобы включить её в тест, необходимо импортировать конфигурацию, регистрирующую бин, используя @Import или @SpringBootTest. |
| Иногда написания тестов Spring WebFlux недостаточно; Spring Boot может помочь выполнить полные тесты «конец-конец» с реальным сервером. |
8.3.17. Автоконфигурируемые тесты Spring GraphQL
Spring GraphQL предлагает специализированный модуль поддержки тестирования; вам нужно добавить его в свой проект:
<dependencies>
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Unless already present in the compile scope -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<scope>test</scope>
</dependency>
</dependencies> dependencies {
testImplementation("org.springframework.graphql:spring-graphql-test")
// Unless already present in the implementation configuration
testImplementation("org.springframework.boot:spring-boot-starter-webflux")
} Этот модуль тестирования поставляет GraphQlTester. Тестер широко используется в тестах, поэтому обязательно ознакомьтесь с его использованием. Существует GraphQlTester варианты, и Spring Boot будет автоматически настраивать их в зависимости от типа тестов:
-
ExecutionGraphQlServiceTesterвыполняет тесты на стороне сервера без клиента и транспорта -
HttpGraphQlTesterвыполняет тесты с клиентом, который подключается к серверу, с живым сервером или без него
Spring Boot помогает вам тестировать ваши Spring GraphQL контроллеры с помощью аннотации @GraphQlTest. @GraphQlTest автоматически настраивает инфраструктуру Spring GraphQL без участия транспорта или сервера. Это ограничивает сканируемые бин к @Controller, RuntimeWiringConfigurer, JsonComponent, Converter, GenericConverter, DataFetcherExceptionResolver, Instrumentation и GraphQlSourceBuilderCustomizer. Регулярные бин @Component и @ConfigurationProperties не сканируются при использовании аннотации @GraphQlTest. @EnableConfigurationProperties можно использовать для включения бинов @ConfigurationProperties.
Список автоконфигураций, которые активируются @GraphQlTest, можно найти в приложении. |
Часто @GraphQlTest ограничивается набором контроллеров и используется в сочетании с аннотацией @MockBean для предоставления моковых реализаций необходимых коллабораторов.
@GraphQlTest(GreetingController.class)
class GreetingControllerTests {
@Autowired
private GraphQlTester graphQlTester;
@Test
void shouldGreetWithSpecificName() {
this.graphQlTester.document("{ greeting(name: \"Alice\") } ")
.execute()
.path("greeting")
.entity(String.class)
.isEqualTo("Hello, Alice!");
}
@Test
void shouldGreetWithDefaultName() {
this.graphQlTester.document("{ greeting } ")
.execute()
.path("greeting")
.entity(String.class)
.isEqualTo("Hello, Spring!");
}
}
@GraphQlTest(GreetingController::class)
internal class GreetingControllerTests {
@Autowired
lateinit var graphQlTester: GraphQlTester
@Test
fun shouldGreetWithSpecificName() {
graphQlTester.document("{ greeting(name: \"Alice\") } ").execute().path("greeting").entity(String::class.java)
.isEqualTo("Hello, Alice!")
}
@Test
fun shouldGreetWithDefaultName() {
graphQlTester.document("{ greeting } ").execute().path("greeting").entity(String::class.java)
.isEqualTo("Hello, Spring!")
}
}
@SpringBootTest тесты являются полными интеграционными тестами и включают весь проект. При использовании случайного или заданного порта настраивается живой сервер, и бин HttpGraphQlTester автоматически добавляется, так что вы можете использовать его для тестирования вашего сервера. При конфигурации среды MOCK вы также можете запросить бин HttpGraphQlTester, добавив аннотацию @AutoConfigureHttpGraphQlTester к вашему тестовому классу:
@AutoConfigureHttpGraphQlTester
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class GraphQlIntegrationTests {
@Test
void shouldGreetWithSpecificName(@Autowired HttpGraphQlTester graphQlTester) {
HttpGraphQlTester authenticatedTester = graphQlTester.mutate()
.webTestClient((client) -> client.defaultHeaders((headers) -> headers.setBasicAuth("admin", "ilovespring")))
.build();
authenticatedTester.document("{ greeting(name: \"Alice\") } ")
.execute()
.path("greeting")
.entity(String.class)
.isEqualTo("Hello, Alice!");
}
}
@AutoConfigureHttpGraphQlTester
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class GraphQlIntegrationTests {
@Test
fun shouldGreetWithSpecificName(@Autowired graphQlTester: HttpGraphQlTester) {
val authenticatedTester = graphQlTester.mutate()
.webTestClient { client: WebTestClient.Builder ->
client.defaultHeaders { headers: HttpHeaders ->
headers.setBasicAuth("admin", "ilovespring")
}
}.build()
authenticatedTester.document("{ greeting(name: \"Alice\") } ").execute()
.path("greeting").entity(String::class.java).isEqualTo("Hello, Alice!")
}
}
8.3.18. Автоконфигурируемые тесты Data Cassandra
Вы можете использовать @DataCassandraTest для тестирования приложений Cassandra. По умолчанию он настраивает CassandraTemplate, сканирует классы @Table и настраивает репозитории Spring Data Cassandra. Регулярные бин @Component и @ConfigurationProperties не сканируются при использовании аннотации @DataCassandraTest. @EnableConfigurationProperties можно использовать для включения бинов @ConfigurationProperties. (Подробнее о работе с Cassandra в Spring Boot см. "data.html".)
Список настроек автоконфигурации, которые активирует @DataCassandraTest, можно найти в приложении. |
Следующий пример демонстрирует типичную настройку для использования тестов Cassandra в Spring Boot:
@DataCassandraTest
class MyDataCassandraTests {
@Autowired
private SomeRepository repository;
}
@DataCassandraTest
class MyDataCassandraTests(@Autowired val repository: SomeRepository)
8.3.19. Автоконфигурируемые тесты Data Couchbase
Вы можете использовать @DataCouchbaseTest для тестирования приложений Couchbase. По умолчанию он настраивает CouchbaseTemplate или ReactiveCouchbaseTemplate, сканирует классы @Document и настраивает репозитории Spring Data Couchbase. Регулярные бин @Component и @ConfigurationProperties не сканируются при использовании аннотации @DataCouchbaseTest. @EnableConfigurationProperties можно использовать для включения бинов @ConfigurationProperties. (Подробнее о работе с Couchbase в Spring Boot см. "data.html" в этой главе.)
Список настроек автоконфигурации, которые активирует @DataCouchbaseTest, можно найти в приложении. |
Следующий пример демонстрирует типичную настройку для использования тестов Couchbase в Spring Boot:
@DataCouchbaseTest
class MyDataCouchbaseTests {
@Autowired
private SomeRepository repository;
// ...
}
@DataCouchbaseTest
class MyDataCouchbaseTests(@Autowired val repository: SomeRepository) {
// ...
}
8.3.20. Автоконфигурируемые тесты Data Elasticsearch
Вы можете использовать @DataElasticsearchTest для тестирования приложений Elasticsearch. По умолчанию он настраивает ElasticsearchRestTemplate, сканирует классы @Document и настраивает репозитории Spring Data Elasticsearch. Регулярные бин @Component и @ConfigurationProperties не сканируются при использовании аннотации @DataElasticsearchTest. @EnableConfigurationProperties можно использовать для включения бинов @ConfigurationProperties. (Подробнее о работе с Elasticsearch в Spring Boot см. "data.html" в этой главе.)
Список настроек автоконфигурации, которые активирует @DataElasticsearchTest, можно найти в приложении. |
Следующий пример демонстрирует типичную настройку для использования тестов Elasticsearch в Spring Boot:
@DataElasticsearchTest
class MyDataElasticsearchTests {
@Autowired
private SomeRepository repository;
// ...
}
@DataElasticsearchTest
class MyDataElasticsearchTests(@Autowired val repository: SomeRepository) {
// ...
}
8.3.21. Автонастроенные тесты Data JPA
Вы можете использовать аннотацию @DataJpaTest для тестирования приложений JPA. По умолчанию она сканирует классы @Entity и настраивает репозитории Spring Data JPA. Если на пути к классу доступна встроенная база данных, она также настраивается. Запросы SQL по умолчанию регистрируются, если свойство spring.jpa.show-sql установлено в значение true. Это можно отключить, используя атрибут showSql аннотации.
Обычные @Component и @ConfigurationProperties бины не сканируются при использовании аннотации @DataJpaTest. @EnableConfigurationProperties можно использовать для включения @ConfigurationProperties бинов.
Список настроек автоконфигурации, включенных аннотацией @DataJpaTest, можно найти в приложении. |
По умолчанию тесты Data JPA транзакционные и откатываются в конце каждого теста. Подробности см. в соответствующем разделе документации Spring Framework. Если этого не требуется, вы можете отключить управление транзакциями для теста или для всего класса следующим образом:
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {
// ...
}
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {
// ...
}
Тесты Data JPA также могут инжектировать бин TestEntityManager, что предоставляет альтернативу стандартному JPA EntityManager, специально разработанному для тестов.
TestEntityManager также может быть автоматически сконфигурирован для любого вашего тестового класса на основе Spring путем добавления @AutoConfigureTestEntityManager. При этом убедитесь, что ваш тест выполняется в транзакции, например, добавив @Transactional к вашему тестовому классу или методу. |
Также доступен JdbcTemplate, если вам это нужно. Следующий пример демонстрирует использование аннотации @DataJpaTest:
@DataJpaTest
class MyRepositoryTests {
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository repository;
@Test
void testExample() {
this.entityManager.persist(new User("sboot", "1234"));
User user = this.repository.findByUsername("sboot");
assertThat(user.getUsername()).isEqualTo("sboot");
assertThat(user.getEmployeeNumber()).isEqualTo("1234");
}
}
@DataJpaTest
class MyRepositoryTests(@Autowired val entityManager: TestEntityManager, @Autowired val repository: UserRepository) {
@Test
fun testExample() {
entityManager.persist(User("sboot", "1234"))
val user = repository.findByUsername("sboot")
assertThat(user?.username).isEqualTo("sboot")
assertThat(user?.employeeNumber).isEqualTo("1234")
}
}
Встроенные базы данных в памяти обычно хорошо подходят для тестов, так как они быстры и не требуют установки. Однако, если вы предпочитаете запускать тесты против реальной базы данных, вы можете использовать аннотацию @AutoConfigureTestDatabase, как показано в следующем примере:
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class MyRepositoryTests {
// ...
}
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class MyRepositoryTests {
// ...
}
8.3.22. Автонастроенные тесты JDBC
@JdbcTest аналогичен @DataJpaTest, но предназначен для тестов, которые требуют только DataSource и не используют Spring Data JDBC. По умолчанию он настраивает встроенную базу данных в памяти и JdbcTemplate. Обычные @Component и @ConfigurationProperties бины не сканируются при использовании аннотации @JdbcTest. @EnableConfigurationProperties можно использовать для включения @ConfigurationProperties бинов.
Список автоконфигураций, включённых аннотацией @JdbcTest, можно найти в приложении. |
По умолчанию JDBC-тесты транзакционные и откатываются в конце каждого теста. Подробности см. в соответствующем разделе документации Spring Framework. Если это не требуется, можно отключить управление транзакциями для теста или для всего класса следующим образом:
@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests {
}
@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests
Если вы предпочитаете запускать тест против реальной базы данных, вы можете использовать аннотацию @AutoConfigureTestDatabase так же, как и для DataJpaTest. (См. "Автонастроенные тесты Data JPA".)
8.3.23. Автонастроенные тесты Data JDBC
@DataJdbcTest аналогичен @JdbcTest, но предназначен для тестов, использующих репозитории Spring Data JDBC. По умолчанию он настраивает встроенную базу данных в памяти, JdbcTemplate и репозитории Spring Data JDBC. Только подклассы AbstractJdbcConfiguration сканируются при использовании аннотации @DataJdbcTest; обычные @Component и @ConfigurationProperties бины не сканируются. @EnableConfigurationProperties можно использовать для включения @ConfigurationProperties бинов.
Список автоконфигураций, включённых аннотацией @DataJdbcTest, можно найти в приложении. |
По умолчанию тесты Data JDBC транзакционные и откатываются в конце каждого теста. Подробности см. в соответствующем разделе документации Spring Framework. Если этого не требуется, можно отключить управление транзакциями для теста или для всего тестового класса, как показано в примере JDBC.
Если вы предпочитаете запускать тест против реальной базы данных, вы можете использовать аннотацию @AutoConfigureTestDatabase аналогично DataJpaTest. (См. "Автонастроенные тесты Data JPA".)
8.3.24. Автонастроенные тесты Data R2DBC
@DataR2dbcTest аналогичен @DataJdbcTest, но предназначен для тестов, использующих репозитории Spring Data R2DBC. По умолчанию он настраивает встроенную базу данных в памяти, R2dbcEntityTemplate и репозитории Spring Data R2DBC. Обычные @Component и @ConfigurationProperties бины не сканируются при использовании аннотации @DataR2dbcTest. @EnableConfigurationProperties можно использовать для включения @ConfigurationProperties бинов.
Список автоконфигураций, включённых аннотацией @DataR2dbcTest, можно найти в приложении. |
По умолчанию тесты Data R2DBC не транзакционные.
Если вы предпочитаете запускать тест против реальной базы данных, вы можете использовать аннотацию @AutoConfigureTestDatabase аналогично DataJpaTest. (См. "Автонастроенные тесты Data JPA".)
8.3.25. Автонастроенные тесты jOOQ
Вы можете использовать @JooqTest аналогично @JdbcTest, но для тестов, связанных с jOOQ. Поскольку jOOQ сильно зависит от схемы на Java, соответствующей схеме базы данных, используется существующая DataSource. Если вы хотите заменить её встроенной базой данных, вы можете использовать @AutoConfigureTestDatabase для переопределения этих настроек. (Подробнее о работе с jOOQ в Spring Boot см. "data.html".) Обычные @Component и @ConfigurationProperties бины не сканируются при использовании аннотации @JooqTest. @EnableConfigurationProperties можно использовать для включения @ConfigurationProperties бинов.
Список автоконфигураций, включённых аннотацией @JooqTest, можно найти в приложении. |
@JooqTest настраивает DSLContext. Следующий пример демонстрирует использование аннотации @JooqTest:
@JooqTest
class MyJooqTests {
@Autowired
private DSLContext dslContext;
// ...
}
@JooqTest
class MyJooqTests(@Autowired val dslContext: DSLContext) {
// ...
}
Тесты jOOQ по умолчанию транзакционные и откатываются в конце каждого теста. Если это не требуется, можно отключить управление транзакциями для теста или для всего тестового класса, как показано в примере JDBC.
8.3.26. Автоконфигурированные тесты данных MongoDB
Вы можете использовать @DataMongoTest для тестирования приложений MongoDB. По умолчанию он настраивает MongoTemplate, сканирует классы @Document и настраивает репозитории Spring Data MongoDB. Регулярные @Component и @ConfigurationProperties бины не сканируются, когда используется аннотация @DataMongoTest. @EnableConfigurationProperties можно использовать для включения @ConfigurationProperties бинов. (Дополнительную информацию об использовании MongoDB с Spring Boot см. в "data.html".)
Список параметров автоконфигурации, включенных @DataMongoTest, можно найти в приложении. |
Следующий класс демонстрирует использование аннотации @DataMongoTest:
@DataMongoTest
class MyDataMongoDbTests {
@Autowired
private MongoTemplate mongoTemplate;
// ...
}
@DataMongoTest
class MyDataMongoDbTests(@Autowired val mongoTemplate: MongoTemplate) {
// ...
}
8.3.27. Автоконфигурированные тесты данных Neo4j
Вы можете использовать @DataNeo4jTest для тестирования приложений Neo4j. По умолчанию он сканирует классы @Node и настраивает репозитории Spring Data Neo4j. Регулярные @Component и @ConfigurationProperties бины не сканируются, когда используется аннотация @DataNeo4jTest. @EnableConfigurationProperties можно использовать для включения @ConfigurationProperties бинов. (Дополнительную информацию об использовании Neo4J с Spring Boot см. в "data.html".)
Список параметров автоконфигурации, включенных @DataNeo4jTest, можно найти в приложении. |
Следующий пример показывает типичную настройку для использования тестов Neo4J в Spring Boot:
@DataNeo4jTest
class MyDataNeo4jTests {
@Autowired
private SomeRepository repository;
// ...
}
@DataNeo4jTest
class MyDataNeo4jTests(@Autowired val repository: SomeRepository) {
// ...
}
По умолчанию тесты данных Neo4j транзакционные и откатываются в конце каждого теста. Более подробную информацию см. в соответствующем разделе документации по Spring Framework. Если это не то, что вам нужно, вы можете отключить управление транзакциями для теста или для всего класса следующим образом:
@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests {
}
@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests
Транзакционные тесты не поддерживаются с реактивным доступом. Если вы используете этот стиль, вы должны настроить @DataNeo4jTest тесты, как описано выше. |
8.3.28. Автоконфигурированные тесты данных Redis
Вы можете использовать @DataRedisTest для тестирования приложений Redis. По умолчанию он сканирует классы @RedisHash и настраивает репозитории Spring Data Redis. Регулярные @Component и @ConfigurationProperties бины не сканируются, когда используется аннотация @DataRedisTest. @EnableConfigurationProperties можно использовать для включения @ConfigurationProperties бинов. (Дополнительную информацию об использовании Redis с Spring Boot см. в "data.html".)
Список параметров автоконфигурации, включенных @DataRedisTest, можно найти в приложении. |
Следующий пример показывает использование аннотации @DataRedisTest:
@DataRedisTest
class MyDataRedisTests {
@Autowired
private SomeRepository repository;
// ...
}
@DataRedisTest
class MyDataRedisTests(@Autowired val repository: SomeRepository) {
// ...
}
8.3.29. Автоконфигурированные тесты данных LDAP
Вы можете использовать @DataLdapTest для тестирования приложений LDAP. По умолчанию он настраивает встроенный LDAP в памяти (если доступен), настраивает LdapTemplate, сканирует классы @Entry и настраивает репозитории Spring Data LDAP. Регулярные @Component и @ConfigurationProperties бины не сканируются, когда используется аннотация @DataLdapTest. @EnableConfigurationProperties можно использовать для включения @ConfigurationProperties бинов. (Дополнительную информацию об использовании LDAP с Spring Boot см. в "data.html".)
Список параметров автоконфигурации, включенных @DataLdapTest, можно найти в приложении. |
Следующий пример показывает использование аннотации @DataLdapTest:
@DataLdapTest
class MyDataLdapTests {
@Autowired
private LdapTemplate ldapTemplate;
// ...
}
@DataLdapTest
class MyDataLdapTests(@Autowired val ldapTemplate: LdapTemplate) {
// ...
}
Встроенный LDAP в памяти обычно хорошо работает для тестов, так как он быстрый и не требует установки разработчиком. Однако, если вы предпочитаете выполнять тесты на реальном сервере LDAP, вы должны исключить встроенную автоконфигурацию LDAP, как показано в следующем примере:
@DataLdapTest(excludeAutoConfiguration = EmbeddedLdapAutoConfiguration.class)
class MyDataLdapTests {
// ...
}
@DataLdapTest(excludeAutoConfiguration = [EmbeddedLdapAutoConfiguration::class])
class MyDataLdapTests {
// ...
}
8.3.30. Автоконфигурированные REST-клиенты
Вы можете использовать аннотацию @RestClientTest для тестирования REST-клиентов. По умолчанию он автоматически настраивает поддержку Jackson, GSON и Jsonb, настраивает RestTemplateBuilder и добавляет поддержку MockRestServiceServer. Регулярные @Component и @ConfigurationProperties бины не сканируются при использовании аннотации @RestClientTest. @EnableConfigurationProperties можно использовать для включения @ConfigurationProperties бинов.
Список параметров автоконфигурации, включенных @RestClientTest, можно найти в приложении. |
Конкретные бины, которые вы хотите протестировать, должны быть указаны с помощью атрибута value или components аннотации @RestClientTest, как показано в следующем примере:
@RestClientTest(RemoteVehicleDetailsService.class)
class MyRestClientTests {
@Autowired
private RemoteVehicleDetailsService service;
@Autowired
private MockRestServiceServer server;
@Test
void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
this.server.expect(requestTo("/greet/details")).andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
String greeting = this.service.callRestService();
assertThat(greeting).isEqualTo("hello");
}
}
@RestClientTest(RemoteVehicleDetailsService::class)
class MyRestClientTests(
@Autowired val service: RemoteVehicleDetailsService,
@Autowired val server: MockRestServiceServer) {
@Test
fun getVehicleDetailsWhenResultIsSuccessShouldReturnDetails(): Unit {
server.expect(MockRestRequestMatchers.requestTo("/greet/details"))
.andRespond(MockRestResponseCreators.withSuccess("hello", MediaType.TEXT_PLAIN))
val greeting = service.callRestService()
assertThat(greeting).isEqualTo("hello")
}
}
8.3.31. Автонастроенные тесты Spring REST Docs
Вы можете использовать аннотацию @AutoConfigureRestDocs, чтобы использовать Spring REST Docs в своих тестах с Mock MVC, REST Assured или WebTestClient. Она избавляет от необходимости использования JUnit-расширения в Spring REST Docs.
@AutoConfigureRestDocs можно использовать для переопределения стандартной директории вывода (target/generated-snippets если вы используете Maven или build/generated-snippets если используете Gradle). Также можно настроить хост, схему и порт, которые будут отображаться в любых документированных URI.
Автонастроенные тесты Spring REST Docs с Mock MVC
@AutoConfigureRestDocs настраивает бин MockMvc для использования Spring REST Docs при тестировании веб-приложений на основе сервлетов. Вы можете инжектировать его, используя @Autowired, и использовать его в своих тестах так же, как и при использовании Mock MVC и Spring REST Docs, как показано в следующем примере:
@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
class MyUserDocumentationTests {
@Autowired
private MockMvc mvc;
@Test
void listUsers() throws Exception {
this.mvc.perform(get("/users").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andDo(document("list-users"));
}
}
@WebMvcTest(UserController::class)
@AutoConfigureRestDocs
class MyUserDocumentationTests(@Autowired val mvc: MockMvc) {
@Test
fun listUsers() {
mvc.perform(MockMvcRequestBuilders.get("/users").accept(MediaType.TEXT_PLAIN))
.andExpect(MockMvcResultMatchers.status().isOk)
.andDo(MockMvcRestDocumentation.document("list-users"))
}
}
Если вам нужен больший контроль над конфигурацией Spring REST Docs, чем предоставляют атрибуты @AutoConfigureRestDocs, вы можете использовать бин RestDocsMockMvcConfigurationCustomizer, как показано в следующем примере:
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsMockMvcConfigurationCustomizer {
@Override
public void customize(MockMvcRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsMockMvcConfigurationCustomizer {
override fun customize(configurer: MockMvcRestDocumentationConfigurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown())
}
}
Если вам нужно использовать поддержку Spring REST Docs для параметризованной директории вывода, вы можете создать бин RestDocumentationResultHandler. Автоконфигурация вызывает alwaysDo с этим обработчиком результатов, тем самым заставляя каждый вызов MockMvc автоматически генерировать стандартные фрагменты. Следующий пример демонстрирует определение RestDocumentationResultHandler:
@TestConfiguration(proxyBeanMethods = false)
public class MyResultHandlerConfiguration {
@Bean
public RestDocumentationResultHandler restDocumentation() {
return MockMvcRestDocumentation.document("{method-name}");
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyResultHandlerConfiguration {
@Bean
fun restDocumentation(): RestDocumentationResultHandler {
return MockMvcRestDocumentation.document("{method-name}")
}
}
Автонастроенные тесты Spring REST Docs с WebTestClient
@AutoConfigureRestDocs также можно использовать с WebTestClient при тестировании реактивных веб-приложений. Вы можете инжектировать его, используя @Autowired, и использовать его в тестах, как обычно при использовании @WebFluxTest и Spring REST Docs, как показано в следующем примере:
@WebFluxTest
@AutoConfigureRestDocs
class MyUsersDocumentationTests {
@Autowired
private WebTestClient webTestClient;
@Test
void listUsers() {
this.webTestClient
.get().uri("/")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.consumeWith(document("list-users"));
}
}
@WebFluxTest
@AutoConfigureRestDocs
class MyUsersDocumentationTests(@Autowired val webTestClient: WebTestClient) {
@Test
fun listUsers() {
webTestClient
.get().uri("/")
.exchange()
.expectStatus()
.isOk
.expectBody()
.consumeWith(WebTestClientRestDocumentation.document("list-users"))
}
}
Если вам нужен больший контроль над конфигурацией Spring REST Docs, чем предоставляют атрибуты @AutoConfigureRestDocs, вы можете использовать бин RestDocsWebTestClientConfigurationCustomizer, как показано в следующем примере:
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsWebTestClientConfigurationCustomizer {
@Override
public void customize(WebTestClientRestDocumentationConfigurer configurer) {
configurer.snippets().withEncoding("UTF-8");
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsWebTestClientConfigurationCustomizer {
override fun customize(configurer: WebTestClientRestDocumentationConfigurer) {
configurer.snippets().withEncoding("UTF-8")
}
}
Если вы хотите использовать поддержку Spring REST Docs для параметризованной директории вывода, вы можете использовать WebTestClientBuilderCustomizer для настройки обработчика для каждого результата обмена данными с сущностью. Следующий пример демонстрирует такое определение WebTestClientBuilderCustomizer:
@TestConfiguration(proxyBeanMethods = false)
public class MyWebTestClientBuilderCustomizerConfiguration {
@Bean
public WebTestClientBuilderCustomizer restDocumentation() {
return (builder) -> builder.entityExchangeResultConsumer(document("{method-name}"));
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyWebTestClientBuilderCustomizerConfiguration {
@Bean
fun restDocumentation(): WebTestClientBuilderCustomizer {
return WebTestClientBuilderCustomizer { builder: WebTestClient.Builder ->
builder.entityExchangeResultConsumer(
WebTestClientRestDocumentation.document("{method-name}")
)
}
}
}
Автонастроенные тесты Spring REST Docs с REST Assured
@AutoConfigureRestDocs делает бин RequestSpecification, предварительно настроенный для использования Spring REST Docs, доступным для ваших тестов. Вы можете инжектировать его, используя @Autowired, и использовать его в тестах, как обычно при использовании REST Assured и Spring REST Docs, как показано в следующем примере:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class MyUserDocumentationTests {
@Test
void listUsers(@Autowired RequestSpecification documentationSpec, @LocalServerPort int port) {
given(documentationSpec)
.filter(document("list-users"))
.when()
.port(port)
.get("/")
.then().assertThat()
.statusCode(is(200));
}
}
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class MyUserDocumentationTests {
@Test
fun listUsers(@Autowired documentationSpec: RequestSpecification?, @LocalServerPort port: Int) {
RestAssured.given(documentationSpec)
.filter(RestAssuredRestDocumentation.document("list-users"))
.`when`()
.port(port)["/"]
.then().assertThat()
.statusCode(Matchers.`is`(200))
}
}
Если вам нужен больший контроль над конфигурацией Spring REST Docs, чем предоставляют атрибуты @AutoConfigureRestDocs, можно использовать бин RestDocsRestAssuredConfigurationCustomizer, как показано в следующем примере:
@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsRestAssuredConfigurationCustomizer {
@Override
public void customize(RestAssuredRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyRestDocsConfiguration : RestDocsRestAssuredConfigurationCustomizer {
override fun customize(configurer: RestAssuredRestDocumentationConfigurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown())
}
}
8.3.32. Автонастроенные тесты Spring Web Services
Автонастроенные тесты клиента Spring Web Services
Вы можете использовать @WebServiceClientTest для тестирования приложений, которые вызывают веб-сервисы с помощью проекта Spring Web Services. По умолчанию он настраивает моковый бин WebServiceServer и автоматически настраивает ваш WebServiceTemplateBuilder. (Дополнительную информацию об использовании веб-сервисов с Spring Boot см. в "io.html".)
Список настроек автоконфигурации, включённых @WebServiceClientTest, можно найти в приложении. |
Следующий пример демонстрирует использование аннотации @WebServiceClientTest:
@WebServiceClientTest(SomeWebService.class)
class MyWebServiceClientTests {
@Autowired
private MockWebServiceServer server;
@Autowired
private SomeWebService someWebService;
@Test
void mockServerCall() {
this.server
.expect(payload(new StringSource("<request/>")))
.andRespond(withPayload(new StringSource("<response><status>200</status></response>")));
assertThat(this.someWebService.test())
.extracting(Response::getStatus)
.isEqualTo(200);
}
}
@WebServiceClientTest(SomeWebService::class)
class MyWebServiceClientTests(@Autowired val server: MockWebServiceServer, @Autowired val someWebService: SomeWebService) {
@Test
fun mockServerCall() {
server
.expect(RequestMatchers.payload(StringSource("<request/>")))
.andRespond(ResponseCreators.withPayload(StringSource("<response><status>200</status></response>")))
assertThat(this.someWebService.test()).extracting(Response::status).isEqualTo(200)
}
}
Автонастроенные тесты сервера Spring Web Services
Вы можете использовать @WebServiceServerTest для тестирования приложений, которые реализуют веб-сервисы с помощью проекта Spring Web Services. По умолчанию он настраивает бин MockWebServiceClient, который можно использовать для вызова ваших точек входа веб-сервиса. (Дополнительную информацию об использовании веб-сервисов с Spring Boot см. в "io.html".)
Список настроек автоконфигурации, включённых @WebServiceServerTest, можно найти в приложении. |
Следующий пример демонстрирует использование аннотации @WebServiceServerTest:
@WebServiceServerTest(ExampleEndpoint.class)
class MyWebServiceServerTests {
@Autowired
private MockWebServiceClient client;
@Test
void mockServerCall() {
this.client
.sendRequest(RequestCreators.withPayload(new StringSource("<ExampleRequest/>")))
.andExpect(ResponseMatchers.payload(new StringSource("<ExampleResponse>42</ExampleResponse>")));
}
}
@WebServiceServerTest(ExampleEndpoint::class)
class MyWebServiceServerTests(@Autowired val client: MockWebServiceClient) {
@Test
fun mockServerCall() {
client
.sendRequest(RequestCreators.withPayload(StringSource("<ExampleRequest/>")))
.andExpect(ResponseMatchers.payload(StringSource("<ExampleResponse>42</ExampleResponse>")))
}
}
8.3.33. Дополнительная автоконфигурация и нарезка
Каждый срез предоставляет одну или несколько аннотаций @AutoConfigure…, которые, в частности, определяют автоконфигурации, которые должны быть включены в качестве части среза. Дополнительные автоконфигурации можно добавить в тесты по отдельности, создав пользовательскую аннотацию @AutoConfigure… или добавив @ImportAutoConfiguration в тест, как показано в следующем примере:
@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration.class)
class MyJdbcTests {
}
@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration::class)
class MyJdbcTests
Убедитесь, что не используете обычную аннотацию @Import для импорта автоконфигураций, поскольку Spring Boot обрабатывает их особым образом. |
В качестве альтернативы, дополнительные автоконфигурации можно добавить для любого использования аннотации среза, зарегистрировав их в файле, хранящемся в META-INF/spring, как показано в следующем примере:
com.example.IntegrationAutoConfiguration
В этом примере com.example.IntegrationAutoConfiguration включена для каждого теста, помеченного аннотацией @JdbcTest.
Вы можете использовать комментарии с # в этом файле. |
Срез или аннотация @AutoConfigure… можно настроить таким образом, если она мета-аннотирована @ImportAutoConfiguration. |
8.3.34. Конфигурация пользователя и нарезка
Если вы структурируете свой код разумным образом, ваш класс @SpringBootApplication по умолчанию используется в качестве конфигурации ваших тестов.
Затем становится важным не засорять основной класс приложения настройками, специфичными для конкретной области его функциональности.
Предположим, что вы используете Spring Data MongoDB, полагаетесь на автоматическую конфигурацию для него и включили аудит. Вы можете определить свой класс @SpringBootApplication следующим образом:
@SpringBootApplication
@EnableMongoAuditing
public class MyApplication {
// ...
}
@SpringBootApplication
@EnableMongoAuditing
class MyApplication {
// ...
}
Поскольку этот класс является исходной конфигурацией для теста, любой тестовый срез фактически пытается включить аудит Mongo, что определенно не то, что вам нужно. Рекомендуемый подход заключается в перемещении конфигурации, специфичной для области, в отдельный класс @Configuration на том же уровне, что и ваше приложение, как показано в следующем примере:
@Configuration(proxyBeanMethods = false)
@EnableMongoAuditing
public class MyMongoConfiguration {
// ...
}
@Configuration(proxyBeanMethods = false)
@EnableMongoAuditing
class MyMongoConfiguration {
// ...
}
В зависимости от сложности вашего приложения, вы можете иметь либо один класс @Configuration для ваших настроек, либо по одному классу на область предметной области. Последний подход позволяет включить его в одном из ваших тестов, если необходимо, с помощью аннотации @Import. См. этот раздел руководства для получения дополнительных сведений о том, когда вам может потребоваться включить определенные классы @Configuration для тестовых срезов. |
Тестовые срезы исключают классы @Configuration из сканирования. Например, для @WebMvcTest следующая конфигурация не будет включать заданный бин WebMvcConfigurer в контекст приложения, загруженный тестовым срезом:
@Configuration(proxyBeanMethods = false)
public class MyWebConfiguration {
@Bean
public WebMvcConfigurer testConfigurer() {
return new WebMvcConfigurer() {
// ...
};
}
}
@Configuration(proxyBeanMethods = false)
class MyWebConfiguration {
@Bean
fun testConfigurer(): WebMvcConfigurer {
return object : WebMvcConfigurer {
// ...
}
}
}
Однако, конфигурация ниже приведет к загрузке настраиваемого WebMvcConfigurer тестовым срезом.
@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {
// ...
}
@Component
class MyWebMvcConfigurer : WebMvcConfigurer {
// ...
}
Еще одним источником путаницы является сканирование classpath. Предположим, что, хотя вы структурировали свой код разумным образом, вам нужно просканировать дополнительный пакет. Ваше приложение может иметь следующий код:
@SpringBootApplication
@ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {
// ...
}
@SpringBootApplication
@ComponentScan("com.example.app", "com.example.another")
class MyApplication {
// ...
}
Это фактически переопределяет стандартную директиву сканирования компонентов с побочным эффектом сканирования этих двух пакетов независимо от выбранного среза. Например, тестовый срез может внезапно начать сканировать компоненты и пользовательские конфигурации вашего приложения. Опять же, перемещение пользовательской директивы в отдельный класс — хороший способ исправить эту проблему.
Если это для вас не вариант, вы можете создать @SpringBootConfiguration где-то в иерархии вашего теста, чтобы он использовался вместо него. В качестве альтернативы, вы можете указать источник для своего теста, что отключит поведение поиска по умолчанию. |
8.3.35. Использование Spock для тестирования приложений Spring Boot
Spock 2.2 или более поздней версии можно использовать для тестирования приложения Spring Boot. Для этого добавьте зависимость от версии -groovy-4.0 модуля spock-spring Spock в ваш проект. spock-spring интегрирует фреймворк тестов Spring в Spock. Дополнительные сведения см. в документации модуля Spring для Spock.
8.4. Тестконтейнеры
Библиотека Testcontainers предоставляет способ управления службами, работающими внутри контейнеров Docker. Она интегрируется с JUnit, позволяя вам создавать класс тестов, который может запускать контейнер перед выполнением любых тестов. Testcontainers особенно полезна для написания интеграционных тестов, которые взаимодействуют с реальной службой бэкенда, такой как MySQL, MongoDB, Cassandra и другие.
Testcontainers можно использовать в тесте Spring Boot следующим образом:
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Container
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
@Test
void myTest() {
// ...
}
}
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Test
fun myTest() {
// ...
}
companion object {
@Container
val neo4j = Neo4jContainer("neo4j:5")
}
}
Это запустит контейнер Docker с Neo4j (если Docker запущен локально) перед запуском любых тестов. В большинстве случаев необходимо настроить приложение для подключения к службе, работающей в контейнере.
8.4.1. Подключения к сервисам
Подключение к сервису — это соединение с любой удаленной службой. Автоконфигурация Spring Boot может использовать детали подключения к сервису и использовать их для установления соединения с удаленной службой. При этом данные подключения имеют приоритет над любыми свойствами конфигурации, связанными с подключением.
При использовании Testcontainers данные подключения могут быть автоматически созданы для сервиса, работающего в контейнере, путем аннотирования поля контейнера в классе тестов.
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Container
@ServiceConnection
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
@Test
void myTest() {
// ...
}
}
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Test
fun myTest() {
// ...
}
companion object {
@Container
@ServiceConnection
val neo4j = Neo4jContainer("neo4j:5")
}
}
Благодаря @ServiceConnection, вышеуказанная настройка позволяет связанным с Neo4j компонентам приложения взаимодействовать с Neo4j, работающим внутри управляемого Testcontainers Docker контейнера. Это делается путем автоматического определения компонента Neo4jConnectionDetails, который затем используется автоконфигурацией Neo4j, переопределяя любые свойства конфигурации, связанные с подключением.
Вам необходимо добавить модуль spring-boot-testcontainers в качестве тестовой зависимости, чтобы использовать подключения к сервисам с Testcontainers. |
Аннотации подключений к сервисам обрабатываются классами ContainerConnectionDetailsFactory, зарегистрированными с помощью spring.factories. Класс ContainerConnectionDetailsFactory может создавать компонент ConnectionDetails на основе конкретного подкласса Container или имени образа Docker.
В JAR-файле spring-boot-testcontainers предоставляются следующие фабрики подключений к сервисам:
| Детали подключения | Сопоставлено по |
|---|---|
| Контейнеры типа |
| Контейнеры типа |
| По умолчанию все соответствующие компоненты подключения будут созданы для данного Если требуется создать только подмножество применимых типов, можно использовать атрибут |
По умолчанию используется Container.getDockerImageName() для получения имени, используемого для поиска данных подключения. При использовании пользовательского образа Docker можно использовать атрибут name компонента @ServiceConnection для его переопределения.
Например, если у вас есть GenericContainer, использующий образ Docker registry.mycompany.com/mirror/myredis, вам нужно использовать @ServiceConnection(name="redis"), чтобы гарантировать создание RedisConnectionDetails.
8.4.2. Динамические свойства
Несколько более подробный, но и более гибкий вариант подключения к сервисам — это @DynamicPropertySource. Статический метод @DynamicPropertySource позволяет добавлять динамические значения свойств в среду Spring.
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Container
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
@Test
void myTest() {
// ...
}
@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", neo4j::getBoltUrl);
}
}
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Test
fun myTest() {
// ...
}
companion object {
@Container
val neo4j = Neo4jContainer("neo4j:5")
@DynamicPropertySource
fun neo4jProperties(registry: DynamicPropertyRegistry) {
registry.add("spring.neo4j.uri") { neo4j.boltUrl }
}
}
}
Вышеуказанная конфигурация позволяет связанным с Neo4j компонентам приложения взаимодействовать с Neo4j, работающим внутри управляемого Testcontainers Docker контейнера.
8.4.3. Использование Testcontainers на этапе разработки
Помимо использования Testcontainers для интеграционных тестов, их также можно применять на этапе разработки. Такой подход позволяет разработчикам быстро запускать контейнеры для служб, от которых зависит приложение, исключая необходимость ручного развёртывания, например, баз данных. Использование Testcontainers таким образом обеспечивает функциональность, аналогичную Docker Compose, за исключением того, что конфигурация контейнера задаётся на Java, а не в YAML.
Чтобы использовать Testcontainers на этапе разработки, необходимо запустить ваше приложение, используя «тестовый» класспаф, а не «основной». Это позволит вам получить доступ ко всем объявленным тестовым зависимостям и предоставит естественное место для написания тестовой конфигурации.
Для создания запускаемого в тестовом режиме варианта вашего приложения, необходимо создать класс «Application» в каталоге src/test. Например, если ваше основное приложение находится в src/main/java/com/example/MyApplication.java, то вы должны создать src/test/java/com/example/TestMyApplication.java
Класс TestMyApplication может использовать метод SpringApplication.from(…) для запуска реального приложения:
public class TestMyApplication {
public static void main(String[] args) {
SpringApplication.from(MyApplication::main).run(args);
}
}
fun main(args: Array<String>) {
fromApplication<MyApplication>().run(*args)
}
Вам также необходимо определить экземпляры Container, которые вы хотите запустить вместе с приложением. Для этого нужно убедиться, что модуль spring-boot-testcontainers добавлен в качестве зависимости test. После этого вы можете создать класс @TestConfiguration, который объявляет методы @Bean для контейнеров, которые вы хотите запустить.
Вы также можете аннотировать методы @Bean с помощью @ServiceConnection для создания ConnectionDetails бинов. Подробности поддерживаемых технологий см. в разделе соединений с сервисами выше.
Типичная конфигурация Testcontainers будет выглядеть так:
@TestConfiguration(proxyBeanMethods = false)
public class MyContainersConfiguration {
@Bean
@ServiceConnection
public Neo4jContainer<?> neo4jContainer() {
return new Neo4jContainer<>("neo4j:5");
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyContainersConfiguration {
@Bean
@ServiceConnection
fun neo4jContainer(): Neo4jContainer<*> {
return Neo4jContainer("neo4j:5")
}
}
Жизненный цикл Container бинов автоматически управляется Spring Boot. Контейнеры будут автоматически запускаться и останавливаться. |
После определения конфигурации тестов, вы можете использовать метод with(…) для подключения её к запуску ваших тестов:
public class TestMyApplication {
public static void main(String[] args) {
SpringApplication.from(MyApplication::main).with(MyContainersConfiguration.class).run(args);
}
}
fun main(args: Array<String>) {
fromApplication<MyApplication>().with(MyContainersConfiguration::class).run(*args)
}
Теперь вы можете запустить TestMyApplication как любой обычный Java main метод для запуска вашего приложения и контейнеров, необходимых для его работы.
Вы можете использовать Maven цель spring-boot:test-run или Gradle задачу bootTestRun для этого из командной строки. |
Внесение динамических свойств во время разработки
Если вы хотите внести динамические свойства на этапе разработки из ваших Container @Bean методов, вы можете сделать это, введя DynamicPropertyRegistry. Это работает аналогично @DynamicPropertySource аннотации, которую можно использовать в ваших тестах. Это позволяет добавлять свойства, которые станут доступны после запуска вашего контейнера.
Типичная конфигурация будет выглядеть так:
@TestConfiguration(proxyBeanMethods = false)
public class MyContainersConfiguration {
@Bean
public MongoDBContainer mongoDbContainer(DynamicPropertyRegistry properties) {
MongoDBContainer container = new MongoDBContainer("mongo:5.0");
properties.add("spring.data.mongodb.host", container::getHost);
properties.add("spring.data.mongodb.port", container::getFirstMappedPort);
return container;
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyContainersConfiguration {
@Bean
fun monogDbContainer(properties: DynamicPropertyRegistry): MongoDBContainer {
var container = MongoDBContainer("mongo:5.0")
properties.add("spring.data.mongodb.host", container::getHost);
properties.add("spring.data.mongodb.port", container::getFirstMappedPort);
return container
}
}
Использование @ServiceConnection рекомендуется всегда, когда это возможно, однако динамические свойства могут быть полезной заменой для технологий, которые пока не поддерживают @ServiceConnection. |
Импортирование классов объявлений контейнеров
Распространённый шаблон при использовании Testcontainers — объявлять экземпляры Container как статические поля. Часто эти поля объявляются непосредственно в классе теста. Их также можно объявить в родительском классе или в интерфейсе, который реализует тест.
Например, следующий интерфейс MyContainers объявляет контейнеры mongo и neo4j:
public interface MyContainers {
@Container
MongoDBContainer mongoContainer = new MongoDBContainer("mongo:5.0");
@Container
Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:5");
}
Если у вас уже есть контейнеры, определённые таким образом, или вы просто предпочитаете этот стиль, вы можете импортировать эти классы объявлений, вместо определения ваших контейнеров как @Bean методов. Для этого добавьте аннотацию @ImportTestcontainers к вашему классу конфигурации тестов:
@TestConfiguration(proxyBeanMethods = false)
@ImportTestcontainers(MyContainers.class)
public class MyContainersConfiguration {
}
@TestConfiguration(proxyBeanMethods = false)
@ImportTestcontainers(MyContainers::class)
class MyContainersConfiguration {
}
Вы можете использовать аннотацию @ServiceConnection на Container полях для установления соединений с сервисами. Вы также можете добавить @DynamicPropertySource аннотированные методы в ваш класс объявления. |
Использование DevTools с Testcontainers на этапе разработки
При использовании devtools вы можете аннотировать бины и методы бинов с помощью @RestartScope. Такие бины не будут пересозданы при перезапуске приложения devtools. Это особенно полезно для Testcontainer Container бинов, так как они сохраняют своё состояние, несмотря на перезапуск приложения.
@TestConfiguration(proxyBeanMethods = false)
public class MyContainersConfiguration {
@Bean
@RestartScope
public MongoDBContainer mongoDbContainer() {
return new MongoDBContainer("mongo:5.0");
}
}
@TestConfiguration(proxyBeanMethods = false)
class MyContainersConfiguration {
@Bean
@RestartScope
fun monogDbContainer(): MongoDBContainer {
return MongoDBContainer("mongo:5.0")
}
}
Если вы используете Gradle и хотите использовать эту функцию, вам нужно изменить конфигурацию зависимости spring-boot-devtools со значения developmentOnly на testImplementation. При стандартном scope developmentOnly, задача bootTestRun не будет подхватывать изменения в вашем коде, так как devtools не активны. |
8.5. Инструменты для тестирования
Несколько классов-инструментов для тестирования, которые обычно полезны при тестировании вашего приложения, упакованы в составе spring-boot.
8.5.1. ConfigDataApplicationContextInitializer
ConfigDataApplicationContextInitializer — это ApplicationContextInitializer, который вы можете применить к вашим тестам для загрузки файлов конфигурации Spring Boot application.properties. Вы можете использовать его, когда вам не нужен полный набор функций, предоставляемых @SpringBootTest, как показано в следующем примере:
@ContextConfiguration(classes = Config.class, initializers = ConfigDataApplicationContextInitializer.class)
class MyConfigFileTests {
// ...
}
@ContextConfiguration(classes = [Config::class], initializers = [ConfigDataApplicationContextInitializer::class])
class MyConfigFileTests {
// ...
}
Использование только ConfigDataApplicationContextInitializer не обеспечивает поддержку инъекции @Value("${…}"). Его единственная задача — обеспечить загрузку файлов конфигурации application.properties в контекст Spring Environment. Для поддержки @Value, вам необходимо либо дополнительно настроить PropertySourcesPlaceholderConfigurer, либо использовать @SpringBootTest, который автоматически настраивает его. |
8.5.2. TestPropertyValues
TestPropertyValues позволяет быстро добавлять свойства в ConfigurableEnvironment или ConfigurableApplicationContext. Вы можете вызвать его со строками key=value, как показано ниже:
class MyEnvironmentTests {
@Test
void testPropertySources() {
MockEnvironment environment = new MockEnvironment();
TestPropertyValues.of("org=Spring", "name=Boot").applyTo(environment);
assertThat(environment.getProperty("name")).isEqualTo("Boot");
}
}
class MyEnvironmentTests {
@Test
fun testPropertySources() {
val environment = MockEnvironment()
TestPropertyValues.of("org=Spring", "name=Boot").applyTo(environment)
assertThat(environment.getProperty("name")).isEqualTo("Boot")
}
}
8.5.3. OutputCapture
OutputCapture — это JUnit Extension, которое можно использовать для захвата System.out и System.err вывода. Для использования его добавьте @ExtendWith(OutputCaptureExtension.class) и введите CapturedOutput в качестве аргумента конструктора класса теста или метода теста следующим образом:
@ExtendWith(OutputCaptureExtension.class)
class MyOutputCaptureTests {
@Test
void testName(CapturedOutput output) {
System.out.println("Hello World!");
assertThat(output).contains("World");
}
}
@ExtendWith(OutputCaptureExtension::class)
class MyOutputCaptureTests {
@Test
fun testName(output: CapturedOutput?) {
println("Hello World!")
assertThat(output).contains("World")
}
}
8.5.4. TestRestTemplate
TestRestTemplate — это удобная альтернатива Spring’s RestTemplate, которая полезна в интеграционных тестах. Вы можете получить шаблон без настроек или шаблон, который отправляет аутентификацию Basic HTTP (с именем пользователя и паролем). В любом случае, шаблон устойчив к ошибкам. Это означает, что он ведет себя дружелюбно по отношению к тестам, не выбрасывая исключений при 4xx и 5xx ошибках. Вместо этого такие ошибки можно обнаружить с помощью возвращаемого ResponseEntity и его кода состояния.
Spring Framework 5.0 предоставляет новый WebTestClient, который работает для тестов интеграции WebFlux и для тестирования WebFlux и MVC "от конца до конца". Он предоставляет удобочитаемый API для утверждений, в отличие от TestRestTemplate. |
Рекомендуется, но не обязательно, использовать Apache HTTP Client (версия 5.1 или выше). Если он есть в вашем классе, TestRestTemplate отреагирует, правильно настроив клиент. Если вы используете Apache HTTP клиент, некоторые дополнительные функции, дружественные для тестирования, будут включены:
-
Перенаправлений не происходит (чтобы вы могли утверждать расположение ответа).
-
Куки игнорируются (чтобы шаблон был бессостоятельным).
TestRestTemplate можно создать напрямую в интеграционных тестах, как показано в следующем примере:
class MyTests {
private final TestRestTemplate template = new TestRestTemplate();
@Test
void testRequest() {
ResponseEntity<String> headers = this.template.getForEntity("https://myhost.example.com/example", String.class);
assertThat(headers.getHeaders().getLocation()).hasHost("other.example.com");
}
}
class MyTests {
private val template = TestRestTemplate()
@Test
fun testRequest() {
val headers = template.getForEntity("https://myhost.example.com/example", String::class.java)
assertThat(headers.headers.location).hasHost("other.example.com")
}
}
В качестве альтернативы, если вы используете аннотацию @SpringBootTest с WebEnvironment.RANDOM_PORT или WebEnvironment.DEFINED_PORT, вы можете внедрить полностью настроенный TestRestTemplate и начать использовать его. При необходимости дополнительные настройки можно применить через бин RestTemplateBuilder. Любые URL-адреса, которые не указывают хост и порт, автоматически подключаются к встраиваемому серверу, как показано в следующем примере:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MySpringBootTests {
@Autowired
private TestRestTemplate template;
@Test
void testRequest() {
HttpHeaders headers = this.template.getForEntity("/example", String.class).getHeaders();
assertThat(headers.getLocation()).hasHost("other.example.com");
}
@TestConfiguration(proxyBeanMethods = false)
static class RestTemplateBuilderConfiguration {
@Bean
RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1))
.setReadTimeout(Duration.ofSeconds(1));
}
}
}
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MySpringBootTests(@Autowired val template: TestRestTemplate) {
@Test
fun testRequest() {
val headers = template.getForEntity("/example", String::class.java).headers
assertThat(headers.location).hasHost("other.example.com")
}
@TestConfiguration(proxyBeanMethods = false)
internal class RestTemplateBuilderConfiguration {
@Bean
fun restTemplateBuilder(): RestTemplateBuilder {
return RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1))
.setReadTimeout(Duration.ofSeconds(1))
}
}
}
9. Поддержка Docker Compose
Docker Compose — популярная технология, позволяющая определять и управлять несколькими контейнерами для служб, необходимых вашему приложению. Обычно создается файл compose.yml рядом с вашим приложением, который определяет и настраивает контейнеры служб.
Типичный рабочий процесс с Docker Compose заключается в запуске docker compose up, работе над вашим приложением с подключением к запущенным службам, а затем запуске docker compose down по завершении работы.
Модуль spring-boot-docker-compose можно включить в проект для поддержки работы с контейнерами с использованием Docker Compose. Добавьте зависимость модуля в свой билдер, как показано в следующих примерах для Maven и Gradle:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-docker-compose</artifactId>
<optional>true</optional>
</dependency>
</dependencies> dependencies {
developmentOnly("org.springframework.boot:spring-boot-docker-compose")
} При включении этого модуля в качестве зависимости Spring Boot выполнит следующие действия:
-
Поиск файла
compose.ymlи других распространенных файлов compose в каталоге вашего приложения -
Вызов
docker compose upс обнаруженным файломcompose.yml -
Создание бинов соединений со службой для каждого поддерживаемого контейнера
-
Вызов
docker compose stopпри завершении работы приложения
Для корректной работы поддержки Spring Boot приложение docker compose или docker-compose CLI должно быть доступно в вашей системе. |
По умолчанию поддержка Docker Compose в Spring Boot отключена при запуске тестов. Чтобы включить её, установите spring.docker.compose.skip.in-tests в значение false. |
9.1. Соединения со службами
Соединение со службой — это соединение с любой удаленной службой. Автоконфигурация Spring Boot может потреблять данные о соединении со службой и использовать их для установления соединения с удаленной службой. В этом случае данные соединения имеют приоритет над любыми свойствами конфигурации, относящимися к соединению.
При использовании поддержки Docker Compose в Spring Boot устанавливаются соединения со службой к порту, сопоставленному с контейнером.
| Docker Compose обычно используется таким образом, что порты внутри контейнера отображаются на временные порты на вашем компьютере. Например, сервер Postgres может работать внутри контейнера по порту 5432, но быть сопоставленным с совершенно другим портом локально. Соединение со службой всегда обнаружит и будет использовать сопоставленный локальный порт. |
Соединения со службами устанавливаются с помощью имени образа контейнера. В настоящее время поддерживаются следующие соединения со службами:
| Подробности соединения | Соответствие |
|---|---|
| Контейнеры с именем «cassandra» |
| Контейнеры с именем «elasticsearch» |
| Контейнеры с именем «gvenzl/oracle-xe», «mariadb», «mssql/server», «mysql» или «postgres» |
| Контейнеры с именем «mongo» |
| Контейнеры с именем «gvenzl/oracle-xe», «mariadb», «mssql/server», «mysql» или «postgres» |
| Контейнеры с именем «rabbitmq» |
| Контейнеры с именем «redis» |
| Контейнеры с именем «openzipkin/zipkin». |
9.2. Пользовательские образы
Иногда может потребоваться использовать собственную версию образа для предоставления сервиса. Вы можете использовать любой пользовательский образ, если он ведет себя так же, как стандартный образ. В частности, любые переменные среды, которые поддерживает стандартный образ, также должны использоваться в вашем пользовательском образе.
Если ваш образ использует другое имя, вы можете использовать метку в файле compose.yml, чтобы Spring Boot мог предоставить соединение со службой. Используйте метку с именем org.springframework.boot.service-connection для предоставления имени службы.
Например:
services:
redis:
image: 'mycompany/mycustomredis:7.0'
ports:
- '6379'
labels:
org.springframework.boot.service-connection: redis 9.3. Пропуск определенных контейнеров
Если в вашем файле compose.yml определен образ контейнера, который вы не хотите подключать к своему приложению, вы можете использовать метку для его игнорирования. Любой контейнер с меткой org.springframework.boot.ignore будет пропущен Spring Boot.
Например:
services:
redis:
image: 'redis:7.0'
ports:
- '6379'
labels:
org.springframework.boot.ignore: true 9.4. Использование определенного файла Compose
Если файл compose находится не в той же директории, что и ваше приложение, или имеет другое имя, вы можете использовать spring.docker.compose.file в своих файлах конфигурации application.properties или application.yaml, чтобы указать на другой файл. Свойства могут быть определены как точный путь или путь, относительный к вашему приложению.
Например:
spring.docker.compose.file=../my-compose.yml spring:
docker:
compose:
file: "../my-compose.yml" 9.5. Ожидание готовности контейнера
Контейнеры, запущенные с помощью Docker Compose, могут некоторое время занимать готовность. Рекомендуемый способ проверки готовности — добавление раздела healthcheck в определение службы в вашем файле compose.yml.
Поскольку нередко конфигурация healthcheck пропускается из файлов compose.yml, Spring Boot также проверяет готовность службы напрямую. По умолчанию контейнер считается готовым, когда можно установить TCP/IP-соединение с его сопоставленным портом.
Вы можете отключить это на основе контейнера, добавив метку org.springframework.boot.readiness-check.tcp.disable в ваш файл compose.yml.
Например:
services:
redis:
image: 'redis:7.0'
ports:
- '6379'
labels:
org.springframework.boot.readiness-check.tcp.disable: true Вы также можете изменить значения тайм-аута в файле application.properties или application.yaml:
spring.docker.compose.readiness.tcp.connect-timeout=10s
spring.docker.compose.readiness.tcp.read-timeout=5s spring:
docker:
compose:
readiness:
tcp:
connect-timeout: 10s
read-timeout: 5s Общий тайм-аут можно настроить с помощью spring.docker.compose.readiness.timeout.
9.6. Управление жизненным циклом Docker Compose
По умолчанию Spring Boot вызывает docker compose up при запуске приложения и docker compose stop при его завершении. Если вы предпочитаете другой способ управления жизненным циклом, вы можете использовать свойство spring.docker.compose.lifecycle-management.
Поддерживаются следующие значения:
-
none— не запускать и не останавливать Docker Compose -
start-only— запустить Docker Compose при запуске приложения и оставить его работающим -
start-and-stop— запустить Docker Compose при запуске приложения и остановить его при выходе JVM
Кроме того, вы можете использовать свойство spring.docker.compose.start.command, чтобы изменить использование docker compose up или docker compose start. Свойство spring.docker.compose.stop.command позволяет настроить использование docker compose down или docker compose stop.
Следующий пример демонстрирует настройку управления жизненным циклом:
spring.docker.compose.lifecycle-management=start-and-stop
spring.docker.compose.start.command=start
spring.docker.compose.stop.command=down
spring.docker.compose.stop.timeout=1m spring:
docker:
compose:
lifecycle-management: start-and-stop
start:
command: start
stop:
command: down
timeout: 1m 9.7. Активация профилей Docker Compose
Профили Docker Compose аналогичны профилям Spring, позволяя настраивать конфигурацию Docker Compose для определенных сред. Для активации определенного профиля Docker Compose можно использовать свойство spring.docker.compose.profiles.active в файлах application.properties или application.yaml:
spring.docker.compose.profiles.active=myprofile spring:
docker:
compose:
profiles:
active: "myprofile" 10. Создание собственной автоконфигурации
Если вы работаете в компании, которая разрабатывает общие библиотеки, или если вы работаете над открытым исходным кодом или коммерческой библиотекой, вам может потребоваться разработать собственную автоконфигурацию. Классы автоконфигурации могут быть объединены во внешних JAR-файлах и все равно будут подбираться Spring Boot.
Автоконфигурация может быть связана с «стартером», который предоставляет код автоконфигурации, а также типичные библиотеки, которые вы будете использовать с ним. Сначала мы рассмотрим, что вам нужно знать для создания собственной автоконфигурации, а затем перейдем к типичным шагам, необходимым для создания настраиваемого стартера.
10.1. Понимание автоконфигурируемых бинов
Классы, реализующие автоконфигурацию, аннотированы с помощью @AutoConfiguration. Эта аннотация сама по себе мета-аннотирована с помощью @Configuration, делая автоконфигурации стандартными @Configuration классами. Дополнительные @Conditional аннотации используются для ограничения того, когда должна применяться автоконфигурация. Обычно классы автоконфигурации используют @ConditionalOnClass и @ConditionalOnMissingBean аннотации. Это гарантирует, что автоконфигурация применяется только тогда, когда найдены соответствующие классы и когда вы не объявили собственные @Configuration.
Вы можете просмотреть исходный код spring-boot-autoconfigure, чтобы увидеть @AutoConfiguration классы, предоставляемые Spring (см. META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports файл).
10.2. Поиск кандидатов на автоконфигурацию
Spring Boot проверяет наличие файла META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports в вашем опубликованном JAR-файле. Файл должен содержать список ваших конфигурационных классов, по одному имени класса в строке, как показано в следующем примере:
com.mycorp.libx.autoconfigure.LibXAutoConfiguration com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
Вы можете добавлять комментарии к файлу импортов, используя символ #. |
Автоконфигурации должны загружаться только путем указания их имен в файле импортов. Убедитесь, что они определены в конкретном пространстве пакетов и что они никогда не являются целевыми для сканирования компонентов. Кроме того, классы автоконфигурации не должны активировать сканирование компонентов для поиска дополнительных компонентов. Вместо этого следует использовать специальные аннотации @Import. |
Если ваша конфигурация должна применяться в определенном порядке, вы можете использовать атрибуты before, beforeName, after и afterName в аннотации @AutoConfiguration или специальные аннотации @AutoConfigureBefore и @AutoConfigureAfter. Например, если вы предоставляете конфигурацию, специфичную для веб-приложений, ваш класс может потребоваться применить после WebMvcAutoConfiguration.
Если вам нужно упорядочить определенные автоконфигурации, которые не должны иметь прямого знания друг о друге, вы также можете использовать @AutoConfigureOrder. Эта аннотация имеет тот же смысл, что и обычная аннотация @Order, но предоставляет специальный порядок для классов автоконфигурации.
Как и в случае со стандартными @Configuration классами, порядок применения классов автоконфигурации влияет только на порядок определения их бинов. Порядок, в котором эти бины создаются впоследствии, не затрагивается и определяется зависимостями каждого бина и любыми отношениями @DependsOn.
10.3. Условные аннотации
Практически всегда необходимо включать одну или несколько аннотаций @Conditional в вашем классе автоконфигурации. Аннотация @ConditionalOnMissingBean — это распространённый пример, используемый для того, чтобы разработчики могли переопределять автоконфигурацию, если их не устраивают ваши значения по умолчанию.
Spring Boot включает ряд аннотаций @Conditional, которые можно повторно использовать в собственном коде, аннотируя классы @Configuration или отдельные методы @Bean. Эти аннотации включают:
10.3.1. Условные аннотации для классов
Аннотации @ConditionalOnClass и @ConditionalOnMissingClass позволяют включать классы @Configuration на основе наличия или отсутствия определённых классов. Поскольку метаданные аннотаций анализируются с помощью ASM, вы можете использовать атрибут value для ссылки на реальный класс, даже если этот класс фактически не присутствует в загружаемом приложении. Также можно использовать атрибут name, если предпочитаете указывать имя класса, используя значение String.
Этот механизм не применяется к методам @Bean таким же образом, так как в типичном случае возвращаемый тип является целью условия: перед применением условия к методу JVM загружает класс и потенциально обрабатывает ссылки на методы, что может привести к ошибке, если класса нет.
Для обработки этой ситуации можно использовать отдельный класс @Configuration, чтобы изолировать условие, как показано в следующем примере:
@AutoConfiguration
// Some conditions ...
public class MyAutoConfiguration {
// Auto-configured beans ...
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SomeService.class)
public static class SomeServiceConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService();
}
}
}
@Configuration(proxyBeanMethods = false)
// Some conditions ...
class MyAutoConfiguration {
// Auto-configured beans ...
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SomeService::class)
class SomeServiceConfiguration {
@Bean
@ConditionalOnMissingBean
fun someService(): SomeService {
return SomeService()
}
}
}
Если вы используете @ConditionalOnClass или @ConditionalOnMissingClass в качестве части мета-аннотации для создания собственных составных аннотаций, вы должны использовать name, так как в этом случае ссылка на класс не обрабатывается. |
10.3.2. Условные аннотации для бинов
Аннотации @ConditionalOnBean и @ConditionalOnMissingBean позволяют включать бины на основе наличия или отсутствия определённых бинов. Вы можете использовать атрибут value для указания бинов по типу или name для указания бинов по имени. Атрибут search позволяет ограничить иерархию ApplicationContext, которую следует учитывать при поиске бинов.
При размещении на методе @Bean целевой тип по умолчанию — возвращаемый тип метода, как показано в следующем примере:
@AutoConfiguration
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService();
}
}
@Configuration(proxyBeanMethods = false)
class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
fun someService(): SomeService {
return SomeService()
}
}
В приведенном примере бин someService будет создан, если бин типа SomeService ещё не содержится в ApplicationContext.
Следует очень внимательно следить за порядком добавления определений бинов, так как эти условия оцениваются на основе того, что уже обработано. По этой причине рекомендуется использовать только аннотации @ConditionalOnBean и @ConditionalOnMissingBean в классах автоконфигурации (так как они гарантированно загружаются после добавления любых определений бинов, заданных пользователем). |
@ConditionalOnBean и @ConditionalOnMissingBean не препятствуют созданию классов @Configuration. Единственное отличие между использованием этих условий на уровне класса и пометкой каждого вложенного метода @Bean аннотацией заключается в том, что первое предотвращает регистрацию класса @Configuration как бин, если условие не соответствует. |
При объявлении метода @Bean предоставляйте как можно больше информации о типе в возвращаемом типе метода. Например, если ваш конкретный класс бина реализует интерфейс, возвращаемый тип метода бина должен быть конкретным классом, а не интерфейсом. Предоставление как можно большей информации о типе в методах @Bean особенно важно при использовании условных аннотаций для бинов, так как их оценка может полагаться только на информацию о типе, доступную в сигнатуре метода. |
10.3.3. Условные аннотации для свойств
Аннотация @ConditionalOnProperty позволяет включать конфигурацию на основе свойства среды Spring. Используйте атрибуты prefix и name для указания проверяемого свойства. По умолчанию соответствует любое существующее свойство, которое не равно значению false. Вы также можете создавать более сложные проверки, используя атрибуты havingValue и matchIfMissing.
10.3.4. Условные аннотации для ресурсов
Аннотация @ConditionalOnResource позволяет включать конфигурацию только при наличии конкретного ресурса. Ресурсы можно указывать, используя стандартные соглашения Spring, как показано в следующем примере: file:/home/user/test.dat.
10.3.5. Условные аннотации для веб-приложений
Аннотации @ConditionalOnWebApplication и @ConditionalOnNotWebApplication позволяют включать конфигурацию в зависимости от того, является ли приложение веб-приложением. Веб-приложение на основе сервлетов — это любое приложение, использующее Spring WebApplicationContext, определяющее область session или имеющее ConfigurableWebEnvironment. Реактивное веб-приложение — это любое приложение, использующее ReactiveWebApplicationContext или имеющее ConfigurableReactiveWebEnvironment.
Аннотации @ConditionalOnWarDeployment и @ConditionalOnNotWarDeployment позволяют включать конфигурацию в зависимости от того, является ли приложение традиционным приложением WAR, развернутым в контейнере сервлетов. Это условие не будет соответствовать приложениям, запущенным с встроенным веб-сервером.
10.3.6. Условные аннотации для выражений SpEL
Аннотация @ConditionalOnExpression позволяет включать конфигурацию на основе результата выражения SpEL.
| Ссылка на бин в выражении приведёт к инициализации этого бина на очень ранней стадии обработки контекста. В результате бин не будет подходить для пост-обработки (например, привязки свойств конфигурации) и его состояние может быть неполным. |
10.4. Тестирование автоматической конфигурации
Автоматическую конфигурацию могут затрагивать многие факторы: пользовательская конфигурация (@Bean определение и Environment настройка), проверка условий (наличие определённой библиотеки) и другие. Конкретно, каждый тест должен создавать чётко определённый ApplicationContext, представляющий комбинацию этих настроек. ApplicationContextRunner предоставляет отличный способ достижения этого.
ApplicationContextRunner обычно определяется как поле класса теста для сбора базовой, общей конфигурации. Следующий пример гарантирует, что MyServiceAutoConfiguration всегда вызывается:
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration.class));
val contextRunner = ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration::class.java))
| Если необходимо определить несколько автоматических конфигураций, нет необходимости упорядочивать их объявления, так как они вызываются в том же порядке, что и при запуске приложения. |
Каждый тест может использовать запуск (runner) для представления конкретного сценария использования. Например, пример ниже вызывает пользовательскую конфигурацию (UserConfiguration) и проверяет, что автоматическая конфигурация корректно отказывает. Вызов run предоставляет контекст обратного вызова, который может быть использован с AssertJ.
@Test
void defaultServiceBacksOff() {
this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(MyService.class);
assertThat(context).getBean("myCustomService").isSameAs(context.getBean(MyService.class));
});
}
@Configuration(proxyBeanMethods = false)
static class UserConfiguration {
@Bean
MyService myCustomService() {
return new MyService("mine");
}
}
@Test
fun defaultServiceBacksOff() {
contextRunner.withUserConfiguration(UserConfiguration::class.java)
.run { context: AssertableApplicationContext ->
assertThat(context).hasSingleBean(MyService::class.java)
assertThat(context).getBean("myCustomService")
.isSameAs(context.getBean(MyService::class.java))
}
}
@Configuration(proxyBeanMethods = false)
internal class UserConfiguration {
@Bean
fun myCustomService(): MyService {
return MyService("mine")
}
}
Также можно легко настроить Environment, как показано в следующем примере:
@Test
void serviceNameCanBeConfigured() {
this.contextRunner.withPropertyValues("user.name=test123").run((context) -> {
assertThat(context).hasSingleBean(MyService.class);
assertThat(context.getBean(MyService.class).getName()).isEqualTo("test123");
});
}
@Test
fun serviceNameCanBeConfigured() {
contextRunner.withPropertyValues("user.name=test123").run { context: AssertableApplicationContext ->
assertThat(context).hasSingleBean(MyService::class.java)
assertThat(context.getBean(MyService::class.java).name).isEqualTo("test123")
}
}
Запуск (runner) также может использоваться для отображения ConditionEvaluationReport. Отчёт может быть напечатан на уровне INFO или DEBUG. Следующий пример демонстрирует, как использовать ConditionEvaluationReportLoggingListener для печати отчёта в тестах автоматической конфигурации.
class MyConditionEvaluationReportingTests {
@Test
void autoConfigTest() {
new ApplicationContextRunner()
.withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
.run((context) -> {
// Test something...
});
}
}
class MyConditionEvaluationReportingTests {
@Test
fun autoConfigTest() {
ApplicationContextRunner()
.withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
.run { context: AssertableApplicationContext? -> }
}
}
10.4.1. Моделирование веб-контекста
Если необходимо протестировать автоматическую конфигурацию, которая работает только в контексте сервлета или реактивного веб-приложения, используйте WebApplicationContextRunner или ReactiveWebApplicationContextRunner соответственно.
10.4.2. Переопределение класса
Также можно проверить, что произойдёт, если определённый класс и/или пакет отсутствуют во время выполнения. Spring Boot поставляется с FilteredClassLoader, который легко использовать с помощью запуска (runner). В следующем примере мы утверждаем, что если MyService отсутствует, автоматическая конфигурация корректно отключена:
@Test
void serviceIsIgnoredIfLibraryIsNotPresent() {
this.contextRunner.withClassLoader(new FilteredClassLoader(MyService.class))
.run((context) -> assertThat(context).doesNotHaveBean("myService"));
}
@Test
fun serviceIsIgnoredIfLibraryIsNotPresent() {
contextRunner.withClassLoader(FilteredClassLoader(MyService::class.java))
.run { context: AssertableApplicationContext? ->
assertThat(context).doesNotHaveBean("myService")
}
}
10.5. Создание собственного стартера
Типичный стартер Spring Boot содержит код для автоматической настройки и настройки инфраструктуры заданной технологии, назовём её "acme". Для простоты расширения, в специальном пространстве имён могут быть доступны различные ключи конфигурации для среды. И, наконец, для облегчения начала работы пользователями предоставляется одна зависимость "starter".
Конкретно, пользовательский стартер может содержать следующее:
-
Модуль
autoconfigure, содержащий код автоматической настройки для "acme". -
Модуль
starter, предоставляющий зависимость от модуляautoconfigure, а также от "acme" и любых дополнительных зависимостей, которые обычно полезны. Коротко говоря, добавление стартера должно предоставить всё необходимое для начала использования этой библиотеки.
Это разделение на два модуля необязательно. Если у "acme" есть несколько вариантов, опций или дополнительных функций, то лучше разделить автоматическую настройку, так как вы сможете чётко выразить, что некоторые функции являются необязательными. Кроме того, у вас есть возможность создать стартер, который выражает мнение об этих необязательных зависимостях. В то же время другие могут полагаться только на модуль autoconfigure и создавать свои собственные стартеры с другими мнениями.
Если автоматическая настройка относительно проста и не имеет необязательных функций, объединение двух модулей в стартере — определённо вариант.
10.5.1. Имена
Вы должны убедиться, что для вашего стартера предоставлено соответствующее пространство имён. Не начинайте имена своих модулей с spring-boot, даже если вы используете другой Maven groupId. В будущем мы можем предложить официальную поддержку того, что вы настраиваете автоматически.
Как правило, комбинированный модуль следует именовать по имени стартера. Например, предположим, что вы создаёте стартер для "acme", и вы называете модуль автоматической настройки acme-spring-boot, а стартер — acme-spring-boot-starter. Если у вас только один модуль, объединяющий оба, назовите его acme-spring-boot-starter.
10.5.2. Ключи конфигурации
Если ваш стартер предоставляет ключи конфигурации, используйте уникальное пространство имён для них. В частности, не включайте свои ключи в пространства имён, используемые Spring Boot (например, server, management, spring и так далее). Если вы используете то же пространство имён, мы можем в будущем изменить эти пространства имён таким образом, что ваши модули сломаются. Как правило, добавляйте префикс ко всем вашим ключам с собственным пространством имён (например, acme).
Убедитесь, что ключи конфигурации документированы путём добавления Javadoc для каждого свойства, как показано в следующем примере:
@ConfigurationProperties("acme")
public class AcmeProperties {
/**
* Whether to check the location of acme resources.
*/
private boolean checkLocation = true;
/**
* Timeout for establishing a connection to the acme server.
*/
private Duration loginTimeout = Duration.ofSeconds(3);
}
@ConfigurationProperties("acme")
class AcmeProperties(
/**
* Whether to check the location of acme resources.
*/
var isCheckLocation: Boolean = true,
/**
* Timeout for establishing a connection to the acme server.
*/
var loginTimeout:Duration = Duration.ofSeconds(3))
Вы должны использовать только обычный текст с Javadoc поля @ConfigurationProperties, поскольку они не обрабатываются перед добавлением в JSON. |
Вот некоторые правила, которым мы следуем внутренне, чтобы обеспечить согласованность описаний:
-
Не начинайте описание со слов "The" или "A".
-
Для типов
booleanначинайте описание со слов "Whether" или "Enable". -
Для типов, основанных на коллекциях, начинайте описание с "Comma-separated list".
-
Используйте
java.time.Durationвместоlongи описывайте единицу по умолчанию, если она отличается от миллисекунд, например: "Если суффикс длительности не указан, будут использованы секунды". -
Не указывайте значение по умолчанию в описании, если оно не должно определяться во время выполнения.
Убедитесь, что сгенерировано метаданные, чтобы в IDE была доступна помощь по вашим ключам. Вы можете просмотреть сгенерированные метаданные (META-INF/spring-configuration-metadata.json), чтобы убедиться, что ваши ключи должным образом документированы. Использование собственного стартера в совместимой IDE — хорошая идея для проверки качества метаданных.
10.5.3. Модуль «autoconfigure»
Модуль autoconfigure содержит всё необходимое для начала работы с библиотекой. Он также может содержать определения ключей конфигурации (например, @ConfigurationProperties) и любой интерфейс обратного вызова, который может использоваться для дальнейшей настройки инициализации компонентов.
Вы должны пометить зависимости от библиотеки как необязательные, чтобы вы могли легче включать модуль autoconfigure в свои проекты. В таком случае библиотека не предоставляется, и Spring Boot, по умолчанию, отказывается от неё. |
Spring Boot использует процессор аннотаций для сбора условий автоматической настройки в файле метаданных (META-INF/spring-autoconfigure-metadata.properties). Если этот файл присутствует, он используется для раннего фильтрации автоматических настроек, которые не соответствуют условиям, что улучшит время запуска.
При сборке с Maven рекомендуется добавить следующую зависимость в модуль, содержащий автоматические настройки:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency> Если вы определили автоматические настройки непосредственно в своём приложении, убедитесь, что настроили spring-boot-maven-plugin, чтобы предотвратить добавление зависимости в fat jar инструментом repackage:
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project> С Gradle зависимость должна быть объявлена в конфигурации annotationProcessor, как показано в следующем примере:
dependencies {
annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
} 10.5.4. Модуль стартера
Стартер — это фактически пустой jar-файл. Его единственная цель — предоставить необходимые зависимости для работы с библиотекой. Его можно рассматривать как предвзятый взгляд на то, что необходимо для начала работы.
Не делайте предположений о проекте, в который добавляется ваш стартер. Если библиотека, которую вы настраиваете автоматически, обычно требует других стартеров, упомяните их тоже. Предоставление правильного набора по умолчанию зависимостей может быть сложным, если количество необязательных зависимостей велико, так как следует избегать включения ненужных зависимостей для типичного использования библиотеки. Другими словами, необязательные зависимости включать не следует.
В любом случае ваш стартер должен ссылаться на основной стартер Spring Boot (spring-boot-starter) напрямую или косвенно (нет необходимости добавлять его, если ваш стартер опирается на другой стартер). Если проект создан только с вашим пользовательским стартером, основные функции Spring Boot будут учтены благодаря наличию основного стартера. |
11. Поддержка Kotlin
Kotlin — это статически типизированный язык, ориентированный на JVM (и другие платформы), который позволяет писать лаконичный и элегантный код, обеспечивая совместимость с существующими библиотеками на Java.
Spring Boot обеспечивает поддержку Kotlin, используя поддержку других проектов Spring, таких как Spring Framework, Spring Data и Reactor. Дополнительную информацию можно найти в документации по поддержке Kotlin в Spring Framework.
Самый простой способ начать работу с Spring Boot и Kotlin — следовать этому исчерпывающему учебнику. Вы можете создавать новые проекты на Kotlin, используя start.spring.io. Не стесняйтесь присоединиться к каналу #spring на Kotlin Slack или задать вопрос с тегами spring и kotlin на Stack Overflow, если вам нужна помощь.
11.1. Требования
Spring Boot требует как минимум Kotlin 1.7.x и управляет подходящей версией Kotlin через управление зависимостями. Для использования Kotlin, org.jetbrains.kotlin:kotlin-stdlib и org.jetbrains.kotlin:kotlin-reflect должны быть доступны в классе. Также могут использоваться варианты kotlin-stdlib kotlin-stdlib-jdk7 и kotlin-stdlib-jdk8.
Поскольку классы Kotlin по умолчанию являются final, вам, вероятно, потребуется настроить плагин kotlin-spring, чтобы автоматически открывать аннотированные Spring классы, чтобы их можно было проксировать.
Модуль Jackson для Kotlin требуется для сериализации/десериализации данных JSON в Kotlin. Он автоматически регистрируется, если найден в классе. Если Jackson и Kotlin присутствуют, но модуль Jackson для Kotlin отсутствует, выводится предупреждение.
| Эти зависимости и плагины предоставляются по умолчанию, если вы создаете проект Kotlin на start.spring.io. |
11.2. Безопасность от null
Одной из ключевых особенностей Kotlin является безопасность от null. Она обрабатывает null значения во время компиляции, а не откладывает проблему до выполнения и не сталкивается с NullPointerException. Это помогает устранить распространенную причину ошибок без необходимости оберток, таких как Optional. Kotlin также позволяет использовать функциональные конструкции с nullable значениями, как описано в этом исчерпывающем руководстве по безопасности от null в Kotlin.
Хотя Java не позволяет выражать безопасность от null в своей системе типов, Spring Framework, Spring Data и Reactor теперь обеспечивают безопасность от null своих API с помощью удобных для инструментов аннотаций. По умолчанию типы из Java-API, используемые в Kotlin, распознаются как платформенные типы, для которых проверки null ослаблены. Поддержка Kotlin для аннотаций JSR 305 в сочетании с аннотациями для обработки null обеспечивают безопасность от null для соответствующего Spring API в Kotlin.
Проверки JSR 305 можно настроить, добавив флаг компилятора -Xjsr305 со следующими параметрами: -Xjsr305={strict|warn|ignore}. По умолчанию поведение такое же, как -Xjsr305=warn. Значение strict требуется, чтобы безопасность от null учитывалась в типах Kotlin, выведенных из Spring API, но следует использовать с пониманием того, что объявления nullability Spring API могут меняться даже между незначительными выпусками, и в будущем могут быть добавлены дополнительные проверки).
| Универсальные типы аргументов, varargs и элементы массивов nullable пока не поддерживаются. Смотрите SPR-15942 за актуальной информацией. Также обратите внимание, что собственный API Spring Boot еще не аннотирован. |
11.3. Kotlin API
11.3.1. runApplication
Spring Boot предоставляет удобный способ запуска приложения с runApplication<MyApplication>(*args), как показано в следующем примере:
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
Это полная замена для SpringApplication.run(MyApplication::class.java, *args). Также позволяет настраивать приложение, как показано в следующем примере:
runApplication<MyApplication>(*args) {
setBannerMode(OFF)
}
11.3.2. Расширения
Kotlin расширения предоставляют возможность расширять существующие классы дополнительными функциями. Spring Boot Kotlin API использует эти расширения для добавления новых удобств, специфичных для Kotlin, в существующие API.
TestRestTemplate расширения, аналогичные тем, которые предоставляет Spring Framework для RestOperations в Spring Framework, предоставляются. Среди прочего, расширения позволяют воспользоваться преимуществами реифицированных параметров типов Kotlin.
11.4. Управление зависимостями
Для избежания смешивания разных версий зависимостей Kotlin в классе, Spring Boot импортирует BOM Kotlin.
В Maven версия Kotlin может быть настроена путем установки свойства kotlin.version, и управление плагинами предоставляется для kotlin-maven-plugin. В Gradle плагин Spring Boot автоматически согласует kotlin.version с версией плагина Kotlin.
Spring Boot также управляет версией зависимостей Coroutines, импортируя BOM Kotlin Coroutines. Версию можно настроить, установив свойство kotlin-coroutines.version.
org.jetbrains.kotlinx:kotlinx-coroutines-reactor зависимость предоставляется по умолчанию, если вы создаете проект Kotlin с хотя бы одной реактивной зависимостью на start.spring.io. |
11.5. @ConfigurationProperties
@ConfigurationProperties при использовании в сочетании с связыванием по конструктору поддерживает классы с неизменяемыми val свойствами, как показано в следующем примере:
@ConfigurationProperties("example.kotlin")
data class KotlinExampleProperties(
val name: String,
val description: String,
val myService: MyService) {
data class MyService(
val apiToken: String,
val uri: URI
)
}
Чтобы сгенерировать свою метаданные с помощью обработчика аннотаций, kapt следует настроить с зависимостью spring-boot-configuration-processor. Обратите внимание, что некоторые функции (например, обнаружение значения по умолчанию или устаревших элементов) не работают из-за ограничений в модели, предоставляемой kapt. |
11.6. Тестирование
Хотя можно использовать JUnit 4 для тестирования кода Kotlin, по умолчанию предоставляется и рекомендуется JUnit 5. JUnit 5 позволяет создавать экземпляр тестового класса один раз и использовать его повторно для всех тестов класса. Это позволяет использовать @BeforeAll и @AfterAll аннотации на нестатических методах, что хорошо подходит для Kotlin.
Для создания mocks классов Kotlin рекомендуется использовать MockK. Если вам нужен эквивалент MockK аннотаций Mockito, таких как @MockBean и @SpyBean аннотации, вы можете использовать SpringMockK, который предоставляет аналогичные аннотации @MockkBean и @SpykBean.
11.7. Ресурсы
11.7.1. Дополнительное чтение
-
Kotlin Slack (с выделенным каналом #spring)
-
Геопространственный мессенджер с Kotlin, Spring Boot и PostgreSQL
11.7.2. Примеры
-
spring-boot-kotlin-demo: обычный проект Spring Boot + Spring Data JPA
-
mixit: Spring Boot 2 + WebFlux + Reactive Spring Data MongoDB
-
spring-kotlin-fullstack: пример полнофункционального приложения WebFlux на Kotlin с Kotlin2js для фронтенда вместо JavaScript или TypeScript
-
spring-petclinic-kotlin: версия приложения Spring PetClinic на Kotlin
-
spring-kotlin-deepdive: пошаговая миграция от Boot 1.0 + Java к Boot 2.0 + Kotlin
-
spring-boot-coroutines-demo: пример проекта с использованием Coroutines
12. SSL
Spring Boot предоставляет возможность настроить SSL-материалы доверия, которые могут применяться к нескольким типам соединений для поддержки защищенной связи. Свойства конфигурации с префиксом spring.ssl.bundle могут использоваться для указания именованных наборов материалов доверия и связанной информации.
12.1. Настройка SSL с файлами хранилища ключей Java
Свойства конфигурации с префиксом spring.ssl.bundle.jks могут использоваться для настройки наборов материалов доверия, созданных с помощью утилиты Java keytool и сохранённых в файлах хранилища ключей Java в формате JKS или PKCS12. Каждый набор имеет предоставленное пользователем имя, которое может быть использовано для ссылки на набор.
При использовании для защиты встроенного веб-сервера, обычно настраивается keystore с хранилищем ключей Java, содержащим сертификат и закрытый ключ, как показано в этом примере:
spring.ssl.bundle.jks.mybundle.key.alias=application
spring.ssl.bundle.jks.mybundle.keystore.location=classpath:application.p12
spring.ssl.bundle.jks.mybundle.keystore.password=secret
spring.ssl.bundle.jks.mybundle.keystore.type=PKCS12 spring:
ssl:
bundle:
jks:
mybundle:
key:
alias: "application"
keystore:
location: "classpath:application.p12"
password: "secret"
type: "PKCS12" При использовании для защиты клиентского подключения, обычно настраивается truststore с хранилищем ключей Java, содержащим сертификат сервера, как показано в этом примере:
spring.ssl.bundle.jks.mybundle.truststore.location=classpath:server.p12
spring.ssl.bundle.jks.mybundle.truststore.password=secret spring:
ssl:
bundle:
jks:
mybundle:
truststore:
location: "classpath:server.p12"
password: "secret" См. JksSslBundleProperties для полного набора поддерживаемых свойств.
12.2. Настройка SSL с PEM-кодированными сертификатами
Свойства конфигурации с префиксом spring.ssl.bundle.pem могут использоваться для настройки наборов материалов доверия в виде PEM-кодированного текста. Каждый набор имеет предоставленное пользователем имя, которое может быть использовано для ссылки на набор.
При использовании для защиты встроенного веб-сервера, обычно настраивается keystore с сертификатом и закрытым ключом, как показано в этом примере:
spring.ssl.bundle.pem.mybundle.keystore.certificate=classpath:application.crt
spring.ssl.bundle.pem.mybundle.keystore.private-key=classpath:application.key spring:
ssl:
bundle:
pem:
mybundle:
keystore:
certificate: "classpath:application.crt"
private-key: "classpath:application.key" При использовании для защиты клиентского подключения, обычно настраивается truststore с сертификатом сервера, как показано в этом примере:
spring.ssl.bundle.pem.mybundle.truststore.certificate=classpath:server.crt spring:
ssl:
bundle:
pem:
mybundle:
truststore:
certificate: "classpath:server.crt" См. PemSslBundleProperties для полного набора поддерживаемых свойств.
12.3. Применение наборов SSL
После настройки с помощью свойств, наборы SSL могут быть сосланы по имени в свойствах конфигурации для различных типов подключений, которые автоматически настраиваются Spring Boot. Смотрите разделы по встроенным веб-серверам, технологиям данных и клиентам REST для получения дополнительной информации.
12.4. Использование наборов SSL
Spring Boot автоматически настраивает бин типа SslBundles, который предоставляет доступ к каждому из настроенных именованных наборов с помощью свойств spring.ssl.bundle.
SslBundle может быть получен из автоматически настроенного бин SslBundles и использован для создания объектов, которые используются для настройки SSL-соединений в клиентских библиотеках. SslBundle предоставляет послойный подход к получению этих SSL-объектов:
-
getStores()предоставляет доступ к хранилищу ключей и хранилищу сертификатовjava.security.KeyStoreэкземплярам, а также к любому необходимому паролю хранилища ключей. -
getManagers()предоставляет доступ кjava.net.ssl.KeyManagerFactoryиjava.net.ssl.TrustManagerFactoryэкземплярам, а также к массивамjava.net.ssl.KeyManagerиjava.net.ssl.TrustManager, которые они создают. -
createSslContext()предоставляет удобный способ получения новогоjava.net.ssl.SSLContextэкземпляра.
Кроме того, SslBundle предоставляет сведения о используемом ключе, протоколе и любых параметрах, которые должны быть применены к SSL-движку.
Следующий пример показывает получение SslBundle и использование его для создания SSLContext:
@Component
public class MyComponent {
public MyComponent(SslBundles sslBundles) {
SslBundle sslBundle = sslBundles.getBundle("mybundle");
SSLContext sslContext = sslBundle.createSslContext();
// do something with the created sslContext
}
}
@Component
class MyComponent(sslBundles: SslBundles) {
init {
val sslBundle = sslBundles.getBundle("mybundle")
val sslContext = sslBundle.createSslContext()
// do something with the created sslContext
}
}
13. Что читать дальше
Если вы хотите узнать больше о классах, обсуждаемых в этом разделе, обратитесь к документации API Spring Boot или вы можете просмотреть исходный код напрямую. При наличии конкретных вопросов, обратитесь к разделу пошагового руководства.
Если вы знакомы с основными функциями Spring Boot, вы можете продолжить чтение о функциях, готовых к использованию в производстве.
Copyright © 2012-2023 VMware, Inc.
Licensed under the Apache License, Version 2.0.
https://docs.spring.io/spring-boot/docs/3.1.3/reference/html/features.html