Команда Spring АйО подготовила перевод статьи о том, как Spring Data тихо, но уверенно избавляется от «магии рантайма» и учит репозитории работать через AOT. Меньше скрытых прокси, больше прозрачного кода, быстрее старт сервисов. Кажется, это одно из самых крутых обновлений Spring за последние годы.
Давайте разберёмся, какие преимущества дают AOT-репозитории Spring Data.
Ещё в мае 2025 года мы впервые представили Ahead of Time (AOT) репозитории как превью-фичу для JPA и MongoDB — вместе с третьим milestone следующего поколения Spring Data. Если коротко, эта фича использует AOT-обработку, чтобы реализовать методы запросов репозиториев в виде реального исходного кода, опираясь на специфику хранилища, под которое создан репозиторий.
С тех пор мы учли обратную связь, отполировали шероховатости и добавили ещё два модуля: Apache Cassandra и JDBC. Это означает, что в релизе 2025.1.0 вы сможете использовать AOT-сгенерированные репозитории в четырёх модулях Spring Data:
Spring Data for Apache Cassandra
Spring Data JDBC
Spring Data JPA
Spring Data MongoDB
Что вы получаете
Благодаря общей инфраструктуре Spring AOT у нас есть подробное представление о конфигурации вашего приложения, что обеспечивает куда более тесную интеграцию, чем если бы мы использовали annotation processor. Предварительно инициализированный application context для AOT-обработки даёт доступ ко всем зарегистрированным бинам, их типам и свойствам — то есть к вашим настройкам и решениям. Как вы понимаете, это критически важно, если речь идёт, например, о Data JDBC: хотя он и нацелен на стандартный SQL, модуль JDBC должен учитывать возможные особенности целевой базы данных, которые часто имеют собственные диалектные отклонения. Имея доступ к зарегистрированным бинам, таким как JdbcDialect, мы можем генерировать корректные выражения.
Поэтому если взглянуть на сгенерированный код, он следует общим принципам отдельных модулей, но остаётся максимально близок к целевой технологии.
Точно так же, как каждый модуль Spring Data ориентирован на свою технологию, сгенерированный код тоже получается специфичным. Например, простой метод запроса вроде findByLastnameStartingWith для JDBC выглядит совершенно иначе, чем его аналоги для JPA или Apache Cassandra:
Spring Data JDBC
public List<User> findByLastnameStartingWith(String lastname) {
Criteria criteria = Criteria.where("lastname").like(escape(lastname) + "%");
StatementFactory.SelectionBuilder builder = getStatementFactory().select(User.class).filter(criteria);
RowMapper rowMapper = getRowMapperFactory().create(User.class);
List result = (List) builder.executeWith((sql, paramSource) -> getJdbcOperations().query(sql, paramSource, new RowMapperResultSetExtractor<>(rowMapper)));
return (List<User>) convertMany(result, User.class);
}
Spring Data JPA
public List<User> findByLastnameStartingWith(String lastname) {
String queryString = "SELECT u FROM example.springdata.aot.User u WHERE u.lastname LIKE :lastname ESCAPE '\\'";
Query query = entityManager.createQuery(queryString);
query.setParameter("lastname", "%s%%".formatted(lastname));
return (List<User>) query.getResultList();
}
Spring Data for Apache Cassandra
public List<User> findByLastnameStartingWith(String lastname) {
Query query = Query.query(Criteria.where("lastname").like(lastname + "%"));
ExecutableSelectOperation.TerminatingSelect<User> select = operations.query(User.class).matching(query);
return select.all();
}
Эксперт сообщества, Михаил Поливаха, выступал на Joker 2025 с темой Spring AOT Application Context, где в том числе затронул тему преимущества использования AOT Bean Definitions (в том числе AOT Spring Data репозиториев) вместе с Aot Cache и App CDS, о чём сказано позже.
AOT-репозитории генерируются и подключаются по умолчанию при сборке с использованием общей поддержки Spring AOT. Вы можете отключить генерацию репозиториев через spring.aot.repositories.enabled=false или выбрать конкретные модули, например spring.aot.jdbc.repositories.enabled=false.
Чтобы реально ими пользоваться, приложение должно запускаться в AOT-режиме (либо через свойство spring.aot.enabled, либо как GraalVM Native Image).
Но давайте посмотрим и на преимущества для разработки и отладки.
Отладка и метаданные
Сгенерированные AOT-репозитории не только показывают в коде, как выполняется ваш запрос — в IDE можно ставить брейкпоинты и отлаживать выполняемые выражения.
Продолжая мысль о том, что во время AOT-обработки нам доступен фактический текст запросов, мы добавили JSON-представление метаданных для интерфейсов Spring Data. Для репозитория из примера — допустим, UserRepository — Spring Data создаёт ресурс UserRepository.json в том же пакете, где расположен интерфейс репозитория. Этот файл содержит информацию о методах запросов, о том, какая реализация будет использована и какой запрос будет выполнен. Он подходит для документации, а также может использоваться вашей IDE для подсказок прямо в процессе разработки.
Spring Data JDBC
{
"name": "example.springdata.UserRepository",
"module": "JDBC",
"type": "IMPERATIVE",
"methods": [
{
"name": "findByLastnameStartingWith",
"signature": "public abstract List<User> UserRepository.findByLastnameStartingWith(String)",
"query": {
"query": "SELECT 'USER'.'ID' AS 'ID', 'USER'.'LAST_NAME' AS 'LAST_NAME', 'USER'.'FIRST_NAME' AS 'FIRST_NAME' FROM 'USER' WHERE 'USER'.'LASTNAME' LIKE :name"
}
}
//...
Spring Data JPA
{
// ...
"query": {
"query": "SELECT u FROM example.springdata.User u WHERE u.lastname LIKE ?1 ESCAPE '\\'"
}
}
Spring Data for Apache Cassandra
{
// ...
"query": {
"query": "SELECT * FROM users WHERE last_name LIKE ?"
}
}
Вы можете отключить генерацию метаданных, установив spring.aot.repositories.metadata.enabled=false.
AOT Cache и Project Leyden
Генерация кода репозиториев заранее полезна не только для отладки — она отлично сочетается и с новыми возможностями последних версий Java. В отличие от частей, создаваемых во время выполнения, заранее сгенерированные репозитории могут быть проанализированы JVM во время тренировки AOT Cache. Это помогает сократить время запуска: реализации репозиториев уже загружены и линкованы из файла Java shared objects.
[info][class,load] example.springdata.aot.UserRepositoryImpl__AotRepository source: shared objects file
Хорошо известная особенность модулей Spring Data для NoSQL — их возможности объектного маппинга. Менее известно, что во время выполнения Spring Data старается оптимизировать создание новых экземпляров и доступ к свойствам доменных объектов, генерируя байткод на основе MethodHandle, чтобы ускорить операции загрузки/сохранения.
Комментарий от Михаила Поливахи
Данная функциональность по доступу к properties объекта через MethodHandle доступна в целом во всех модулях Spring Data. Кому интересно, логику того, как конкретно это делает Spring Data можно найти вот тут.
Собранная информация о вашей доменной модели позволила нам включить сгенерированный байткод для аксессоров свойств и средства создания сущностей ещё на этапе сборки. Поскольку этот байткод уже предсобран, нет нужды выполнять эти оптимизации в рантайме. Кроме того, готовые instantiator'ы и аксессоры свойств упакованы в итоговый JAR и доступны напрямую через class loader, что делает всё ещё эффективнее.
[info][class,load] example.springdata.User__Instantiator_cu2hga source: shared objects file
[info][class,load] example.springdata.User__Accessor_cu2hga source: shared objects file
Есть ли недостатки?
Разумеется, время вычислений никуда не исчезает — оно просто смещается. В данном случае — в фазу сборки. Да, анализ приложения, подготовка данных и тренировка JVM занимают время и могут потребовать изменений в вашей сборочной и деплой-пайплайне. Вы также обмениваете часть динамических возможностей фреймворка на меньшее потребление памяти и более быстрый старт. Некоторые элементы конфигурации придётся «заморозить» при переходе на AOT. Помните про JdbcDialect? Это как раз тот случай: невозможно сгенерировать SQL, оптимизированный под одну БД, а потом просто переключиться на другую, будто ничего не изменилось.
Кроме того, репозитории Ahead of Time пока поддерживают только императивные интерфейсы репозиториев. Реактивные интерфейсы код не генерируют.
Если вам интересно, просто соберите и запустите приложение с spring.aot.enabled или загляните в Spring Data Examples — там уже есть демо-приложения, которые можно попробовать.
В любом случае, вы всегда можете запустить приложение без флага spring.aot.enabled, чтобы отключить все улучшения Ahead of Time. Это позволяет собирать и тестировать приложение в AOT-режиме, сохраняя при этом возможность отката.
Мы с нетерпением ждём, как вы будете применять фичи Ahead of Time.
И, как всегда, ваш фидбек важен! Нам интересно услышать ваши впечатления и идеи для улучшений.

Присоединяйтесь к русскоязычному сообществу разработчиков на Spring Boot в телеграм — Spring АйО, чтобы быть в курсе последних новостей из мира разработки на Spring Boot и всего, что с ним связано.
