Как Spring AI 2.0 исправляет структурированный вывод по JSON Schema
Обычный
Новый
В статье Dan Vega показан пример на Spring Boot с Anthropic и Ollama, а также настройка журнала повторных попыток. Для миграции проверку можно включать точечно там, где нестабильный JSON уже приводит к ошибкам.
Обычный
entity(TalkSubmission.class) строит схему из Java-типа и преобразует ответ модели в объект. Если небольшая локальная модель пропустит поле или вернёт недопустимый null, запрос завершится ошибкой 500.Новый
validateSchema(...) сверяет JSON со схемой. При ошибке Spring AI передаёт модели причину и повторяет вызов, по умолчанию до трёх раз. Режим выключен по умолчанию, поэтому существующие вызовы entity после обновления не меняют поведение.В статье Dan Vega показан пример на Spring Boot с Anthropic и Ollama, а также настройка журнала повторных попыток. Для миграции проверку можно включать точечно там, где нестабильный JSON уже приводит к ошибкам.
👏1
Quarkus 3.29: что изменилось в тестах CDI-компонентов
В Quarkus 3.29 переработали загрузку классов. Расширение теперь поддерживает преобразование байткода, упрощённую инъекцию через конструктор, final-классы и методы. Новый SPI позволяет подключать логику до сборки и после запуска контейнера. Интеграция с
В обзоре Quarkus также разобраны жизненный цикл контейнера, автоматический выбор тестируемых компонентов и изменения версий 3.13 и 3.21.
QuarkusComponentTest запускает не всё приложение, а только CDI-контейнер и сервис конфигурации. Тестируемый бин остаётся реальным, а отсутствующие зависимости автоматически заменяются моками Mockito. Их можно внедрить через @InjectMock и настроить в тестовом методе.В Quarkus 3.29 переработали загрузку классов. Расширение теперь поддерживает преобразование байткода, упрощённую инъекцию через конструктор, final-классы и методы. Новый SPI позволяет подключать логику до сборки и после запуска контейнера. Интеграция с
quarkus-panache-mock добавила компонентные тесты для Panache-сущностей.В обзоре Quarkus также разобраны жизненный цикл контейнера, автоматический выбор тестируемых компонентов и изменения версий 3.13 и 3.21.
🤔1
Как показать вызовы инструментов в интерфейсе на Spring AI 2.0
Spring AI 2.0 вынес цикл вызова инструментов из реализаций отдельных моделей в цепочку советников
В примере наблюдающий советник ставится перед
В разборе есть рабочий проект на Spring Boot 4.1: инструменты с
Spring AI 2.0 вынес цикл вызова инструментов из реализаций отдельных моделей в цепочку советников
ChatClient. Теперь его можно обернуть собственным советником, увидеть промежуточные события и показать пользователю, какой метод выполняется до появления ответа модели.В примере наблюдающий советник ставится перед
ToolCallingAdvisor и получает события начала и завершения каждого вызова. Контроллер передаёт их в браузер через серверный поток событий вместе с ответом модели. Память диалога сохраняет контекст между запросами, поэтому следующий вопрос может ссылаться на предыдущий.В разборе есть рабочий проект на Spring Boot 4.1: инструменты с
@Tool, потоковый контроллер и советник для отслеживания вызовов. Подход пригодится, если интерфейсу корпоративного ИИ-сервиса нужна наблюдаемость без изменений внутри модели.Value-классы в JDK 28: где JVM убирает аллокации, а где возвращает их
JEP 401 вошёл в JDK 28 как предварительная возможность. Отказ от идентичности позволяет JVM хранить поля без ссылок и раскладывать компоненты по регистрам или стеку, но ускорение не гарантировано.
Неизменяемые поля JVM может хранить плоско, а для изменяемых выбирает ссылку, чтобы потоки не увидели значение из частей разных записей. JIT-компилятор C2 способен убрать создание value record в цикле, но стирание типов и виртуальный вызов могут вернуть объект и аллокацию.
Перед миграцией проверьте горячие участки и переходы между представлениями: три таких случая разобраны в статье Value Classes Still Need Compiler Sympathy.
JEP 401 вошёл в JDK 28 как предварительная возможность. Отказ от идентичности позволяет JVM хранить поля без ссылок и раскладывать компоненты по регистрам или стеку, но ускорение не гарантировано.
Неизменяемые поля JVM может хранить плоско, а для изменяемых выбирает ссылку, чтобы потоки не увидели значение из частей разных записей. JIT-компилятор C2 способен убрать создание value record в цикле, но стирание типов и виртуальный вызов могут вернуть объект и аллокацию.
Перед миграцией проверьте горячие участки и переходы между представлениями: три таких случая разобраны в статье Value Classes Still Need Compiler Sympathy.
Johan Sjölén's blog
Value Classes Still Need Compiler Sympathy
This post discusses preview features in JDK 28\nJEP 401, a major Valhalla milestone, has been integrated as a preview feature in JDK 28. This is very exciting, as value classes increase both our ability to communicate the semantics of our programs to others…
Почему ref = null обычно не помогает сборщику мусора JVM
Локальная ссылка в стеке потока остаётся корнем GC, пока её фрейм активен. После выхода из метода сборщик её не обходит, поэтому
Сборщик копирует достижимые объекты в другой пул, а прежний затем использует заново. Он сохраняет всё, до чего может добраться от корней, а не удаляет мусор по одному объекту.
Редкое исключение возникает, когда узлы LinkedList лежат в разных поколениях памяти: разрыв связи с обработанной частью иногда сокращает удержание. Но рост памяти требует искать ошибку жизненного цикла: растущую статическую коллекцию, кеш без вытеснения или слушатель без отписки. Разбор стека и достижимости помогает отличить утечку от лишнего присваивания.
Локальная ссылка в стеке потока остаётся корнем GC, пока её фрейм активен. После выхода из метода сборщик её не обходит, поэтому
ref = null перед возвратом обычно ничего не меняет.Сборщик копирует достижимые объекты в другой пул, а прежний затем использует заново. Он сохраняет всё, до чего может добраться от корней, а не удаляет мусор по одному объекту.
Редкое исключение возникает, когда узлы LinkedList лежат в разных поколениях памяти: разрыв связи с обработанной частью иногда сокращает удержание. Но рост памяти требует искать ошибку жизненного цикла: растущую статическую коллекцию, кеш без вытеснения или слушатель без отписки. Разбор стека и достижимости помогает отличить утечку от лишнего присваивания.
Как перенести Spring Boot 3.5 на 4.1 и не пропустить тихие поломки
Поддержка Spring Boot 3.5 закончилась 30 июня 2026 года на версии 3.5.16. При этом проект может собраться и запуститься с устаревшими стартерами: зелёной сборки для миграции недостаточно.
До перехода включите предупреждения об устаревших API и прогоните тесты. Затем проверьте зависимости:
Пошаговая миграция проекта разбирает Jackson 3, переход от MultiValueMap к HttpHeaders и тесты: @MockBean заменён на @MockitoBean, а @SpringBootTest больше не настраивает MockMvc, WebClient и TestRestTemplate. Это чек-лист мест, которые надо проверить после компиляции.
Поддержка Spring Boot 3.5 закончилась 30 июня 2026 года на версии 3.5.16. При этом проект может собраться и запуститься с устаревшими стартерами: зелёной сборки для миграции недостаточно.
До перехода включите предупреждения об устаревших API и прогоните тесты. Затем проверьте зависимости:
spring-boot-starter-web стал spring-boot-starter-webmvc, а Flyway получил отдельный стартер.Пошаговая миграция проекта разбирает Jackson 3, переход от MultiValueMap к HttpHeaders и тесты: @MockBean заменён на @MockitoBean, а @SpringBootTest больше не настраивает MockMvc, WebClient и TestRestTemplate. Это чек-лист мест, которые надо проверить после компиляции.
❤3
Как перевести сборку Gradle на Isolated Projects
Isolated Projects параллельно конфигурирует проекты, ускоряя локальные сборки и синхронизацию IntelliJ IDEA. Функция экспериментальная и требует совместимости с Configuration Cache.
Команда Gradle начала с
Опыт внедрения команды Gradle показывает порядок миграции: сначала локальные сборки, затем CI. Продакшен-артефакты пока собирают без этой функции, пока не подтверждена корректность результатов.
Isolated Projects параллельно конфигурирует проекты, ускоряя локальные сборки и синхронизацию IntelliJ IDEA. Функция экспериментальная и требует совместимости с Configuration Cache.
Команда Gradle начала с
help, собрала нарушения в диагностическом отчёте и затем проверила IDE и CI. Плагины соглашений изолировали логику проектов, а обмен артефактами шёл через зависимости.Опыт внедрения команды Gradle показывает порядок миграции: сначала локальные сборки, затем CI. Продакшен-артефакты пока собирают без этой функции, пока не подтверждена корректность результатов.
🤔1
Как ускорить агрегацию в Spring Batch с помощью DuckDB
В Spring Batch агрегацию миллионов строк можно вынести из Java-цикла с HashMap в SQL-запрос DuckDB внутри Tasklet.
В тесте на Java 21 и DuckDB 1.5.5: 10 млн строк обработались за 0,17 секунды вместо 1, 50 млн — за 0,61 вместо 4,83. На свежем запуске JVM влияет прогрев JIT, поэтому результаты лучше перепроверить на своих данных.
Схема интеграции и исходный код пригодятся для агрегаций и соединений. Классическую обработку оставьте для внешних вызовов и обогащения каждой строки.
В Spring Batch агрегацию миллионов строк можно вынести из Java-цикла с HashMap в SQL-запрос DuckDB внутри Tasklet.
В тесте на Java 21 и DuckDB 1.5.5: 10 млн строк обработались за 0,17 секунды вместо 1, 50 млн — за 0,61 вместо 4,83. На свежем запуске JVM влияет прогрев JIT, поэтому результаты лучше перепроверить на своих данных.
Схема интеграции и исходный код пригодятся для агрегаций и соединений. Классическую обработку оставьте для внешних вызовов и обогащения каждой строки.
HotSpot: как задачи JIT задерживают выгрузку ClassLoader
JIT создаёт для горячего метода задачу CompileTask: она держит ссылку на метод, класс и его загрузчик. Пока поток C1 или C2 не завершит компиляцию, GC не выгрузит загрузчик.
Поэтому тест после
Для диагностики дайте JIT время либо отключите фоновую компиляцию флагом
JIT создаёт для горячего метода задачу CompileTask: она держит ссылку на метод, класс и его загрузчик. Пока поток C1 или C2 не завершит компиляцию, GC не выгрузит загрузчик.
Поэтому тест после
System.gc() может срабатывать нестабильно. Повторный полный GC иногда мешает компиляторам закончить работу.Для диагностики дайте JIT время либо отключите фоновую компиляцию флагом
-XX:-BackgroundCompilation. Это делает удаление задач предсказуемее, но добавляет скачки задержки. Тест с URLClassLoader и PhantomReference показывает этот сценарий.albertnetymk's notes
OpenJDK Trivia: Class Unloading and CompileTask Retention
§The Problem When all references to a class loader are released and System.gc() is invoked, the class loader should become eligible for garbage collection. However, with JIT compilation enabled, Compi
Vaadin и Spring: как собрать интерактивный Java-интерфейс
Во второй части проекта NeuroWatch интерфейс на Vaadin дополняют диалогами, формой с валидацией и обновлением данных в реальном времени. Приложение использует Spring Security, Kafka и MongoDB.
Выбор строки в таблице открывает карточку: метаданные размещены в FormLayout, связанные записи — во вложенном Grid. Вкладки позволяют добавить форму редактирования, а Binder связывает её поля с Java-объектом и проверками.
Редактирование показывают только пользователям с ролью ADMIN. При этом проверка роли в интерфейсе не защищает данные сама по себе: ограничения нужно повторить на уровне сервисов и репозиториев.
Схема диалогов, форм и обновлений пригодится для Spring-приложений, где серверный Java-код управляет состоянием интерфейса.
Во второй части проекта NeuroWatch интерфейс на Vaadin дополняют диалогами, формой с валидацией и обновлением данных в реальном времени. Приложение использует Spring Security, Kafka и MongoDB.
Выбор строки в таблице открывает карточку: метаданные размещены в FormLayout, связанные записи — во вложенном Grid. Вкладки позволяют добавить форму редактирования, а Binder связывает её поля с Java-объектом и проверками.
Редактирование показывают только пользователям с ролью ADMIN. При этом проверка роли в интерфейсе не защищает данные сама по себе: ограничения нужно повторить на уровне сервисов и репозиториев.
Схема диалогов, форм и обновлений пригодится для Spring-приложений, где серверный Java-код управляет состоянием интерфейса.
Gradle 9.5.1 в одноразовом CI: сокращаем запуск сборки
В свежем CI-контейнере нет состояния предыдущего запуска, поэтому Gradle снова получает дистрибутив и готовит служебные файлы. Для Java-проекта это лишнее время до выполнения задач сборки.
В разборе запуска Gradle проверяют три приёма: использовать дистрибутив
Результаты служат ориентиром: небольшой Spring Boot-проект собирали в чистом Docker-окружении по пять раз, базовый прогон занял 31,7 секунды. Повторите замеры на своей кодовой базе: в крупной сборке больше времени могут отнимать зависимости, конфигурация или выполнение задач.
В свежем CI-контейнере нет состояния предыдущего запуска, поэтому Gradle снова получает дистрибутив и готовит служебные файлы. Для Java-проекта это лишнее время до выполнения задач сборки.
В разборе запуска Gradle проверяют три приёма: использовать дистрибутив
-bin вместо -all, восстанавливать каталоги Wrapper и заранее готовить generated-gradle-jars вместе с jars-9. Версии образа и Wrapper нужно согласовать, а после обновления Gradle пересобрать подготовленный образ и проверить пути кэша.Результаты служат ориентиром: небольшой Spring Boot-проект собирали в чистом Docker-окружении по пять раз, базовый прогон занял 31,7 секунды. Повторите замеры на своей кодовой базе: в крупной сборке больше времени могут отнимать зависимости, конфигурация или выполнение задач.
Как проверить отправку Kafka-сообщения в Spring Boot без Docker
В интеграционном тесте с Embedded Kafka настоящий брокер запускается внутри тестового процесса. Аннотация
Тест подтверждает доставку от продюсера до брокера и обратно к потребителю, но не результат работы метода с
В интеграционном тесте с Embedded Kafka настоящий брокер запускается внутри тестового процесса. Аннотация
@EmbeddedKafka создаёт топик greetings, поэтому внешняя Kafka и Docker не нужны.KafkaTemplate публикует сообщение, а отдельный KafkaConsumer получает запись через KafkaTestUtils.getSingleRecord. Перед отправкой тест считывает сообщения, созданные ApplicationRunner при запуске, чтобы проверка не забрала старую запись.Тест подтверждает доставку от продюсера до брокера и обратно к потребителю, но не результат работы метода с
@KafkaListener. Эффект обработчика стоит проверять отдельно: так транспортная интеграция и бизнес-логика не маскируют ошибки друг друга.