[Перевод] Что нового в Spring Boot 3.4
Недавно состоялся релиз Spring Framework 6.2, вслед за ним последовал релиз Spring Boot 3.4.
Команда Spring АйО подготовила перевод статьи Josh Long об основных изменениях и улучшениях, которые вошли в последний релиз.
Этот релиз объединяет все нововведения вместе. Spring Boot упрощает интеграцию всех Spring проектов и старается решить возникающие проблемы. Кроме того, он предоставляет удобные инструменты для пользователей Spring фреймворков.
Например: когда мы добавили поддержку GraalVM native image в Spring Framework 6 и Spring Boot 3, это происходило в несколько этапов. Сначала в Spring Framework была реализована модель компонентов, жизненный цикл и базовые SPI. Затем эта модель позволила другим проектам на базе Spring Framework (например, Spring Data, Spring Security, Spring Batch, Spring Integration и др.) обеспечить поддержку GraalVM для своих решений. Наконец, Spring Boot объединил всё это в одно целое, добавив интеграцию с GraalVM для себя и сторонних проектов, предложив плагины для инструментов сборки и организовав процессы, необходимые ещё на этапе компиляции, до запуска вашего кода. Spring Boot также поддерживает репозиторий метаданных для GraalVM и Buildpacks для контейнеризации GraalVM native image. Когда всё сделано правильно, результат впечатляет — именно поэтому Spring Boot так популярен.
В этом релизе снова полно удивительных возможностей! Давайте, как обычно, начнем с обзора нового и интересного из release notes, а затем углубимся в полюбившиеся именно мне функции.
Улучшен и упрощён процесс автоматической настройки для реализаций
HttpRequestFactory
, которые подключаются кRestClient
илиRestTemplate
. Если у вас Apache HTTP Components, будет использоватьсяHttpComponentsClientHttpRequestFactory
. Если есть Jetty, то будет выбранJettyClientHttpRequestFactory
. Если и Jetty нет, но есть Reactor Netty, то будет использоватьсяReactorClientHttpRequestFactory
. Если ничего из этого нет, но вы используете более современные версии JDK, то будет работатьJdkClientHttpRequestFactory
. Если же нет ничего из перечисленного, то будет применёнSimpleClientHttpRequestFactory
, который, по сути, представляет собойHttpURLConnection
из JDK. Не лучший вариант! Кстати, в этом релизе также изменено поведение по умолчанию: раньше использовалсяHttpURLConnection
, даже если JVM поддерживалаHttpClient
Появился новый
ClientHttpRequestFactoryBuilder
, который позволяет создавать эти реализации программно и согласованноВсе клиенты теперь по умолчанию следуют редиректам, но вы можете отключить эту функцию
Приложения теперь завершают работу «аккуратно» по умолчанию (graceful shutdown). В Spring Boot такая возможность существует уже несколько лет. Основная идея в том, что Spring Boot завершает работу сразу после того, как оркестратор контейнеров, платформа или оператор отправляет команду завершения (
SIGTERM
) JVM. При этом текущим операциям даётся время завершиться в течение настраиваемого периода. После этого приложение останавливается. В течение этого времени оно отклоняет новые HTTP-запросы и т.д.Теперь вы можете использовать аннотацию
@AutoConfigureTestDatabase
с контейнерами, что вы не хотите её заменять, как это было ранее@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
.В этом релизе добавлена более гибкая настройка видимости эндпоинтов Actuator
Базовая версия для этого релиза — HTMLUnit 4.3, которая теперь располагается по другой Maven ссылке. Обновите свой код соответствующим образом.
Добавлена поддержка структурного логирования с встроенной поддержкой Elastic Common Schema (ecs), Graylog Extended Log Format (gelf) и Logstash (logstash). Чтобы включить структурное логирование в файлы, установите параметр
logging.structured.format.file
в значениеecs
,gelf
илиlogstash
. Аналогично, чтобы включить структурное логирование в консоль, установитеlogging.structured.format.console
.Теперь возможно отправлять OTLP-spans через gRPC
Для подключения к кластеру Couchbase теперь можно использовать клиентские сертификаты вместо традиционной аутентификации с помощью имени пользователя и пароля
Теперь переменные FreeMarker, используемые автоматически настроенным объектом FreeMarker«s Configuration, можно настраивать
Поскольку ActiveMQ Classic снова поддерживает встроенный брокер сообщений, автонастройка была обновлена, чтобы это учитывать. (Встроенный ActiveMQ снова доступен, если вы добавите зависимость
org.apache.activemq:activemq-broker
!)Появился новый механизм для документирования и уведомления о том, что автонастройка устарела:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.replacements
OtlpMeterRegistry
и Undertow теперь корректно работают с виртуальными потокамиSpring Boot теперь по умолчанию использует
paketobuildpacks/builder-jammy-java-tiny
. Этот сборщик поддерживает платформы ARM и x64 из коробкиDocker Compose теперь поддерживает несколько конфигурационных файлов Docker Compose, а также позволяет передавать пользовательские параметры в командную строку при запуске Docker Compose файлов
Эндпоинты Spring Boot Actuator теперь отображают больше информации о SSL-бандлах
Дополнительная информация о запланированных задачах доступна по эндпоинту
/actuator/scheduledtasks
Так много новых функций! С чего же начать? Я не был уверен, поэтому решил сосредоточиться не на коде, а на работе приложения в рантайме, особенно на двух функциях: поддержке GraalVM native images в build-packs на Apple Silicon и «аккуратном» завершении работы (graceful shutdown). Давайте остановимся на «аккуратном» завершении. Здесь не так уж много сложного.
Посмотрим на один небольшой, но полезный класс, который выполняет многие задачи правильно.
package com.example.bootiful_34.boot;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestClient;
@Controller
@ResponseBody
class GracefulController {
private final RestClient http;
GracefulController(RestClient.Builder http) {
this.http = http.build();
}
@GetMapping("/delay")
String delay() {
return this.http.get().uri("https://httpbin.org/delay/5").retrieve().body(String.class);
}
}
Во-первых, этот класс использует обновлённый автонастраиваемый RestClient
builder, который появился в Spring Boot 3.4. Я использую Java 21+ (точнее, Java 23), так что у меня доступен новый JdkClientHttpRequestFactory
, основанный на java.net.http.HttpClient
. Неплохо начали, дальше будет только интереснее!
Каждый запрос к этому контроллеру вызывает HTTP-запрос на сайт HTTPBin. HTTPBin специально задерживает ответ на столько секунд, сколько мы укажем в запросе. В нашем случае — 5 секунд. Это долго! И не забывайте, что мы используем контейнер сервлетов, а значит, каждый запрос требует отдельного потока. Вы вызываете localhost:8080/delay
(помните, что, возможно, нужно будет войти в систему, так как мы настроили защиту через Spring Security ранее…), и запрос будет блокироваться на целых пять секунд. Поток просто будет простаивать, тратя время впустую, а таких потоков по умолчанию очень мало. Но, к счастью, это Spring Boot, и благодаря простой настройке (spring.threads.virtual.enabled=true
), мы можем включить виртуальные потоки Java 21.
Теперь, когда пользователь делает запрос, RestClient отправляет его, и пока запрос ждёт эти пять секунд, JVM автоматически снимает его с потока операционной системы и помещает в режим ожидания. Почему это важно? Потому что теперь кто-то другой может использовать этот поток в это время! Это значительно улучшает масштабируемость, без лишних затрат. И это не новая функция — её можно было использовать ещё с Spring Boot 3.2.
Ещё одно преимущество в этой ситуации в том, что каждый запрос требует времени для выполнения. Пять секунд плюс задержка сети. Что произойдёт, если оркестратор, например Kubernetes или Cloud Foundry, решит завершить приложение? Мы всё ещё ждём завершения запроса! Поэтому JVM тоже подождёт. По умолчанию это 30 секунд, но можно настроить с помощью spring.lifecycle.timeout-per-shutdown-phase=30s
. То есть, если платформа завершает работу приложения (это можно симулировать, остановив приложение в IntelliJ IDEA), она будет ждать до 30 секунд, чтобы завершить все текущие запросы, а потом остановится. Иначе приложение завершится немедленно. Удобно.
Также есть улучшение для пользователей Apple Silicon — поддержка Buildpacks теперь корректно работает на этой архитектуре.
Теперь давайте превратим это приложение в нативный образ GraalVM. Выполните следующую команду в корневой папке bootiful-34
.
./mvnw -DskipTests clean -Pnative spring-boot:build-image
Вот скрипт, который я использую, чтобы скомпилировать, а затем запустить на своем ноутбуке Apple M4 с процессором Apple Silicon.
#!/usr/bin/env bash
rm -rf target
./mvnw -DskipTests spring-javaformat:apply
./mvnw -DskipTests -Pnative spring-boot:build-image
Вот скрипт, который я использую, чтобы запустить его. Я предполагаю, что у вас уже установлен Docker или что-то похожее, и что у вас настроена переменная окружения (как у меня) в операционной системе хоста, которая содержит ключ API OpenAI с именем SPRING_AI_OPENAI_API_KEY
.
#!/usr/bin/env bash
export SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal/mydatabase
docker run \
-e SPRING_DATASOURCE_URL=$SPRING_DATASOURCE_URL \
-e SPRING_AI_OPENAI_API_KEY=$SPRING_AI_OPENAI_API_KEY \
docker.io/library/bootiful-34:0.0.1-SNAPSHOT
Знаете, что меня поражает? Иногда, по крайней мере на моем компьютере, Linux-контейнер с бинарным файлом GraalVM для Apple Silicon работает быстрее, чем если бы я его просто скомпилировал в macOS-совместимый бинарный файл и запустил на macOS! Понятия не имею, как это происходит! Это просто универсальное решение! У нас есть веб-сервер, Spring Batch Job
, несколько потоков Spring Integration, множество обращений к SQL DataSource
, Spring Modulith, Spring AI, который вызывает функции локально, три типа безопасности и полноценный OAuth IDP (Spring Authorization Server), HTTP эндпоинты, сущности Spring Data JDBC и репозитории, и многое другое. Так что поверьте мне, когда я говорю, что это нестандартная нагрузка, и я не ожидал, что бинарник под Linux на моем компьютере будет работать быстрее. На macOS приложение иногда запускается за 0,450 — 0,5 секунды. В Docker я видел, как оно запускалось за 0,264 секунды. Ума не приложу, как так может быть!
Цифры ещё заметнее в обычных сервисах. У меня есть более простое приложение в том же репозитории GitHub, которое называется demo
. demo
использует Spring Data JDBC для общения с PostgreSQL и Spring MVC. Оно ничего особенного не делает. Версия на Buildpacks запускается примерно за 0,050 секунды, а версия для macOS — за 0,080 секунды! Это не в два раза быстрее, но всё равно разница заметная! Обожаю Buildpacks! И так приятно тестировать все на локальной машине.
Spring Boot — это отличная основа для других наших проектов. Уже выпущены два проекта с поддержкой Spring Boot 3.4: Spring Modulith и Spring AI. О них я расскажу здесь. Ещё один проект, который базируется на Spring Boot 3.4 — Spring Cloud — скоро тоже будет выпущен.
Spring Boot 3.4 — это просто невероятно. Помните, что этот релиз — один из последних перед выпуском Spring Boot 4.0 в следующем году, так что при обновлении обращайте внимание на устаревшие функции. Лучше разобраться с ними прямо сейчас!
Присоединяйтесь к русскоязычному сообществу разработчиков на Spring Boot в телеграм — Spring АйО, чтобы быть в курсе последних новостей из мира разработки на Spring Boot и всего, что с ним связано.
Ждем всех, присоединяйтесь!