QuarkusMiddleTechnical
Что такое build-time augmentation и как оно связано с быстрым startup?
Build-time augmentation — этап сборки Quarkus, когда расширения анализируют аннотации, генерируют байткод и выполняют конфигурацию заранее. В результате при старте приложения JVM не сканирует classpath и не строит DI-контейнер — отсюда быстрый запуск.
Build-time Augmentation в Quarkus
Build-time augmentation — центральная архитектурная идея Quarkus. Вместо того чтобы выполнять инициализацию фреймворка во время запуска приложения (как это делает Spring Boot), Quarkus делает максимум работы на этапе сборки Maven/Gradle.
Что происходит при обычном фреймворке (Spring Boot)
- JVM загружает все классы из classpath (~5000+)
- Spring сканирует аннотации (@Component, @Autowired)
- Строит BeanFactory, создаёт ApplicationContext
- Разрешает зависимости, создаёт прокси через CGLIB
- Инициализирует DataSource, Kafka consumers и т.д.
Всё это происходит при каждом запуске → 2–5 секунд старта.
Build-time Augmentation: механизм
Quarkus-расширение состоит из двух частей:
- Build-time processor — запускается при
mvn package, анализирует аннотации, генерирует байткод - Runtime recorder — сгенерированный код, который выполняется при старте
// Пример build-time processor из расширения Quarkus
// (упрощённо — так работают внутренние механизмы)
@BuildStep
public BeanDefiningAnnotationBuildItem registerCdiAnnotations() {
// Сообщает CDI-контейнеру о кастомной аннотации на этапе сборки
return new BeanDefiningAnnotationBuildItem(
DotName.createSimple("com.example.MyComponent"),
BuiltinScope.APPLICATION_SCOPED.getInfo()
);
}
@BuildStep
@Record(RUNTIME_INIT)
public void configureDataSource(
DataSourceBuildItem dataSource,
DataSourceRecorder recorder) {
// recorder.createPool() — это шаблон, который будет
// выполнен при старте, но уже с предвычисленными параметрами
recorder.createPool(dataSource.getName(), dataSource.getConfig());
}
Что делается на build-time
- CDI DI-граф полностью строится и верифицируется
- Hibernate ORM маппинги анализируются, SQL генерируется заранее
- JAX-RS роуты компилируются в оптимальный dispatch-код
- Аннотации Fault Tolerance, Health, Metrics регистрируются
- Reflection-конфиги для GraalVM генерируются автоматически
Визуализация: что происходит при запуске
./mvnw package # Build-time augmentation: 15-30 сек
# Результат: target/quarkus-app/ с предгенерированным кодом
java -jar target/quarkus-app/quarkus-run.jar
# Quarkus стартует только runtime-часть: ~100-800 мс
# Никакого classpath-сканирования, никакого рефлексивного DI
Как это связано с Native
# При native-сборке augmentation + GraalVM native-image:
./mvnw package -Pnative
# 1. Build-time augmentation (30 сек)
# 2. GraalVM static analysis + compilation (5-10 мин)
# Результат: бинарник, стартующий за 15 мс
./target/app-runner
# 2026-01-15 10:00:00 INFO [io.quarkus] app started in 0.015s
Ограничения build-time подхода
// НЕЛЬЗЯ на build-time: читать env vars или внешние конфиги
@BuildStep
public void badStep() {
String env = System.getenv("MY_VAR"); // ошибка!
// env не существует во время сборки
}
// ПРАВИЛЬНО: читать конфиг через ConfigProperty в runtime recorder
@Record(RUNTIME_INIT)
public void goodStep(MyConfig config, MyRecorder recorder) {
recorder.initialize(config); // config читается при старте
}
Dev-режим и augmentation
В quarkus:dev augmentation выполняется инкрементально при каждом изменении файла — отсюда live coding без полного перезапуска.
Подводные камни
- @ConfigProperty с defaultValue в native — значение встраивается в бинарник на build-time; изменить без пересборки невозможно. Для runtime-конфигурации используйте env vars.
- Сторонние библиотеки без Quarkus-расширения — их инициализация остаётся в runtime; они не получают build-time оптимизаций.
- Сложность написания расширений — BuildStep API нетривиален; стандартные расширения покрывают 95% случаев, но кастомное расширение требует глубокого понимания API.
- Ошибки DI видны только при сборке — это хорошо, но к этому нужно привыкнуть; в Spring ошибки DI иногда обнаруживаются только в runtime через lazy init.
- Live reload не всегда срабатывает — изменения в build-step коде расширения требуют полного перезапуска dev-режима.
- Условные бины через @IfBuildProfile — условие проверяется на build-time; нельзя переключать профиль в runtime без пересборки.
Common mistakes
- Отвечать определением без production-сценария.
- Не называть runtime boundary, security boundary или failure mode.
- Игнорировать версию API, observability и тестовую проверку.
What the interviewer is testing
- Объясняет механизм своими словами и без выдуманных API.
- Называет реальные риски, диагностику и критерий корректности.
- Связывает ответ с текущей документацией и миграционными ограничениями.