Почему Kora: фреймворк с компиляцией на этапе сборки для современной JVM¶
17 сентября 2026
Современные JVM-фреймворки поразительно продуктивны. С помощью нескольких аннотаций можно создать HTTP-эндпоинт, внедрить зависимости, начать транзакцию, обратиться к базе данных, добавить повторы, выставить метрики, провалидировать входные данные и защитить метод.
Однако исторически такое удобство часто давалось ценой компромисса: значительная часть приложения собирается и интерпретируется только после запуска процесса. Сканируются classpath, обнаруживаются метаданные, строятся графы зависимостей, интерпретируются аннотации, создаются прокси, а рефлексивные или динамические механизмы связывают абстракции, которые в исходном коде выглядели просто.
Такой подход хорошо работает и обеспечил огромную часть экосистемы JVM. Фреймворк Kora отталкивается от другого вопроса:
Если у компилятора уже есть перед глазами классы, аннотации, сигнатуры методов, обобщённые типы и объявления зависимостей, зачем откладывать столько работы до этапа выполнения?
Ответ Kora — перенести работу фреймворка на этап компиляции. Граф приложения строится и валидируется во время компиляции. Адаптеры фреймворка генерируются как исходный код. Контроллеры становятся конкретными обработчиками запросов, интерфейсы репозиториев получают конкретные реализации, а AOP-аннотации преобразуются в сгенерированные классы, содержащие требуемое поведение.
К моменту запуска приложения обнаруживать остаётся заметно меньше. Это центральная архитектурная идея Kora: сделать как можно больше работы фреймворка до запуска приложения.
Kora описывает это как уверенность на этапе компиляции: структура приложения проверяется во время компиляции, а результирующие механизмы фреймворка представлены в виде сгенерированного исходного кода, а не скрытого состояния времени выполнения.
Модель фреймворка, ориентированная на этап выполнения¶
Традиционный фреймворк, ориентированный на этап выполнения, обычно начинается с намеренно неполного кода приложения.
Рассмотрим нечто концептуально похожее:
Исходный код уже говорит нам довольно много, но он может не полностью описывать то, что в итоге выполняется. Фреймворку всё ещё нужно определить, как обнаруживается PaymentService, какая реализация
PaymentRepository должна быть внедрена, как строится граф зависимостей, требует ли @Transactional прокси, оборачивает ли @Retryable этот прокси или наоборот, и как все эти объекты участвуют в
управлении жизненным циклом.
В архитектуре, ориентированной на этап выполнения, многие из этих вопросов решаются во время запуска приложения:
исходный код
↓
компиляция
↓
классы
↓
запуск приложения
↓
сканирование / проверка метаданных
↓
обнаружение компонентов
↓
разрешение зависимостей
↓
создание прокси
↓
построение контейнера времени выполнения
↓
начало обслуживания трафика
В этой модели нет ничего изначально плохого. Она даёт фреймворкам огромную гибкость и обеспечила годы высокопродуктивной разработки на JVM. Однако архитектурным следствием является то, что часть приложения существует как состояние фреймворка времени выполнения, а не как обычная структура программы.
Kora выбирает иной компромисс.
Сдвиньте работу влево¶
Kora сдвигает работу фреймворка к компилятору:
исходный код
↓
обработка аннотаций / KSP
↓
валидация контрактов
↓
разрешение зависимостей
↓
генерация реализаций
↓
генерация графа приложения
↓
компиляция сгенерированного исходного кода
↓
запуск приложения
Следовательно, модель времени выполнения становится гораздо проще:
сгенерированный граф приложения
↓
создание экземпляров компонентов
↓
инициализация жизненного цикла
↓
обслуживание трафика
В приложении по-прежнему есть внедрение зависимостей, контроллеры, репозитории, управление жизненным циклом и возможности в стиле AOP — такие как отказоустойчивость, транзакции, валидация, кеширование и безопасность. Разница не в том, что эти абстракции исчезают, а в том, когда фреймворк решает, как они работают.
В Kora многие из этих решений уже приняты к моменту завершения компиляции. Архитектура v2 строит и валидирует контейнер зависимостей из @KoraApp, @Component, @Module и связанных объявлений во
время компиляции. Поэтому отсутствующие или неоднозначные зависимости могут стать ошибками на этапе сборки, а не сюрпризами при запуске приложения.
Это довольно фундаментально меняет отношения между кодом приложения и кодом фреймворка.
Этап компиляции становится частью архитектуры¶
Разработчики на Java уже сильно полагаются на гарантии этапа компиляции. Если метод ожидает Payment, вы не можете случайно передать Customer. Если тип исчезает или класс больше не реализует
требуемый метод, компиляция завершается ошибкой.
Kora распространяет ту же философию на области, которые традиционные фреймворки часто делегируют механизмам времени выполнения.
Например:
Конструктор — это не просто подсказка для инжектора зависимостей времени выполнения. Он становится частью графа зависимостей, который Kora может анализировать во время компиляции. Если
PaymentRepository не может быть разрешён, фреймворку не нужно запускать приложение, чтобы обнаружить этот факт, потому что у компилятора уже достаточно информации.
Тот же принцип применим к циклам зависимостей, неоднозначным кандидатам, недопустимым маппингам и другим контрактам уровня фреймворка. Это даёт разработке иной цикл обратной связи:
написать код
↓
скомпилировать
↓
фреймворк проверяет архитектуру
↓
исправить структурные проблемы
↓
запустить
вместо:
написать код
↓
скомпилировать
↓
запустить приложение
↓
фреймворк обнаруживает архитектуру
↓
что-то падает
По мере роста приложений это различие становится всё более ценным. Чем больше архитектуры компилятор может проверить, тем меньше невидимого состояния фреймворка разработчикам приходится мысленно восстанавливать.
Внедрение зависимостей генерируется¶
Внедрение зависимостей — один из самых наглядных примеров подхода Kora.
Приложение Kora начинается с графа приложения:
Компоненты и модули добавляют узлы в этот граф:
Процессоры аннотаций Kora анализируют эти объявления и генерируют граф приложения. Документация v2 показывает это явно: интерфейс, аннотированный @KoraApp, приводит к генерации кода графа,
описывающего зависимости, используемые при запуске приложения.
Важен не точный вид или форма сгенерированного класса. Важно то, что разрешение зависимостей становится генерацией исходного кода.
Концептуально результат близок к обычному созданию объектов:
Реальный граф, естественно, обрабатывает больше, чем этот упрощённый пример: жизненный цикл, порядок зависимостей, модули, опциональные значения, теги, обновления графа и интеграции фреймворка. Но сами отношения по-прежнему представлены конкретно, а не переоткрываются с нуля при каждом запуске JVM.
Контроллеры становятся обработчиками запросов¶
Тот же принцип действует на границе HTTP.
Разработчик должен иметь возможность написать выразительный контроллер:
Фреймворк, ориентированный на этап выполнения, может проверить этот метод при запуске приложения, проанализировать его аннотации и параметры, определить конвертеры и мапперы ответов, а затем построить механизмы, необходимые для его вызова.
Kora может выполнить большую часть этого анализа во время компиляции. Он знает маршрут, параметры метода, привязки пути, типы аргументов, тип возвращаемого значения и доступные мапперы, поэтому может сгенерировать адаптер, соединяющий HTTP-сервер с контроллером.
Концептуально сгенерированный обработчик выполняет примерно следующее:
прочитать параметр пути
→ распарсить long
→ вызвать controller.get(id)
→ сопоставить User с HTTP-ответом
Это важное различие. @HttpRoute — это не просто метаданные времени выполнения, которые фреймворк многократно интерпретирует. Он действует гораздо больше как входные данные для компилятора:
аннотация описывает намерение, а сгенерированный исходный код реализует его.
Репозитории становятся реализациями¶
Доступ к базе данных следует той же модели.
Рассмотрим:
Разработчик описывает контракт с базой данных, а фреймворк генерирует реализацию. Во время компиляции Kora уже знает метод репозитория, его типы параметров и возвращаемого значения, SQL-запрос, выбранную абстракцию базы данных и доступные мапперы параметров и результатов.
Это даёт компилятору достаточно информации, чтобы сгенерировать повторяющийся связующий код, оставив важную часть — саму операцию с базой данных — явной.
Архитектурный паттерн тот же:
Это существенно отличается от трактовки интерфейсов репозиториев прежде всего как объектов времени выполнения, поддерживаемых механизмами динамического вызова.
AOP тоже становится кодом¶
Сквозное поведение — одно из тех мест, где механизмы фреймворка времени выполнения могут быть особенно трудноразличимы.
Рассмотрим:
Фреймворк, ориентированный на этап выполнения, может создать один или несколько прокси вокруг этого компонента и динамически построить цепочку вызовов. У Kora вместо этого достаточно информации, чтобы сгенерировать требуемый подкласс или обёртку во время компиляции.
Концептуально результат может напоминать:
Реальный сгенерированный код зависит от задействованных аспектов, но архитектурный принцип остаётся тем же: поведение в итоге становится обычным кодом.
Kora использует AOP на этапе компиляции для таких возможностей, как отказоустойчивость, транзакции, валидация, кеширование и другие инфраструктурные задачи, вместо того чтобы требовать динамических прокси времени выполнения для построения этих цепочек после запуска.
У этого есть и практическое преимущество для отладки. Если вам нужно понять, что окружает вызов метода, есть реальный сгенерированный код реализации, который можно изучить.
Рефлексия — не враг¶
Было бы соблазнительно охарактеризовать Kora как «фреймворк, который быстр, потому что не использует рефлексию». Это описание слишком поверхностно.
Рефлексия — это просто возможность JVM, и есть совершенно законные ситуации, где она полезна. Удаление нескольких рефлексивных вызовов методов также не объяснило бы большую часть архитектуры Kora.
Более глубокая цель — сократить интерпретацию на этапе выполнения и скрытые механизмы времени выполнения всякий раз, когда то же решение можно безопасно принять во время компиляции.
Избегание рефлексии, генерации байткода во время выполнения и большого количества механизмов динамических прокси часто естественным образом следует из такой архитектуры, но это следствия, а не главная цель.
Важное преобразование — это:
обнаружение на этапе выполнения
анализ на этапе выполнения
связывание на этапе выполнения
адаптация на этапе выполнения
в такое:
анализ на этапе компиляции
валидация на этапе компиляции
генерация исходного кода
обычное выполнение
Это смещает обсуждение с микробенчмарков о стоимости рефлексивного вызова к более содержательному вопросу: сколько работы фреймворку всё ещё нужно выполнить после того, как приложение уже запустилось?
Запуск — это архитектурное свойство¶
Время запуска иногда рассматривают как косметическую метрику для бенчмарков, но для серверной инфраструктуры это нечто гораздо большее.
Когда запускается контейнер, ориентированный на этап выполнения, ему может потребоваться выполнить значительный объём работы фреймворка, прежде чем сервис будет готов. Kora уже перенёс большую часть этой работы на этап сборки. Граф приложения существует, зависимости проверены, а адаптеры фреймворка уже сгенерированы.
Оставшийся граф можно затем инстанцировать и инициализировать, включая параллельную инициализацию там, где это позволяют зависимости.
Это важно в продакшене, потому что значимая метрика — не то, как быстро Java может войти в main(). Полезный вопрос — как быстро новый экземпляр может стать производственной мощностью.
Например:
всплеск трафика
↓
автоскейлер запрашивает новые экземпляры
↓
контейнеры запускаются
↓
приложения становятся готовыми
↓
новая мощность обслуживает трафик
То же соображение действует при поэтапных развёртываниях. Если каждый заменяющий экземпляр тратит больше времени на работу фреймворка времени выполнения, кластер дольше работает со сниженной эффективной мощностью.
Быстрый запуск также влияет на интеграционные тесты, компонентные тесты, локальную разработку, эфемерные окружения, запланированные задания, сервисы с низким трафиком и архитектуры scale-to-zero. Kora намеренно оплачивает часть стоимости фреймворка во время компиляции, чтобы каждый последующий запуск выполнял меньше работы.
Для долго работающих серверных систем это может быть очень привлекательным обменом.
Накладные расходы времени выполнения — это больше, чем тактовые циклы CPU¶
Накладные расходы фреймворка часто сводят к числам из бенчмарков, но накладные расходы времени выполнения имеют несколько измерений: CPU, память, давление аллокаций, работа при запуске, метаданные, слои прокси, сгенерированное состояние времени выполнения и дополнительные пути вызовов.
Есть и другая форма накладных расходов, которую труднее измерить бенчмарком: когнитивные накладные расходы.
Предположим, выполняется эта строка:
При отладке фреймворка, сильно ориентированного на этап выполнения, инженеру может всё ещё потребоваться определить, является ли orderService реальным объектом или прокси, какие перехватчики
применяются, основан ли прокси на интерфейсе или на подклассе, как ведёт себя самовызов, где была выбрана реализация, какой маппер был обнаружен и когда был зарегистрирован объект.
Это не затраты CPU, но это инженерные затраты.
Модель Kora с этапом компиляции атакует обе категории одновременно. Сгенерированный код может сократить ненужные механизмы времени выполнения, но он также делает поведение фреймворка видимым. Это второе свойство, пожалуй, не менее важно, чем первое.
Предсказуемость вместо магии¶
Магия фреймворка привлекательна, пока всё работает. Сложности начинаются, когда она перестаёт работать.
Аннотация, превращающая десять строк повторяющейся инфраструктуры в одну строку, полезна. Аннотация, чьё реальное поведение требует понимания нескольких слоёв скрытых механизмов времени выполнения, гораздо менее полезна.
Kora старается сохранить декларативное удобство, не делая результирующее поведение непрозрачным. Такие аннотации, как:
остаются краткими, но прежде всего действуют как объявления, участвующие в компиляции. Сгенерированный исходный код представляет, что означают эти объявления.
Это даёт сравнительно прямой путь:
Между объявлением и выполнением меньше интерпретации на этапе выполнения, что делает поведение фреймворка более детерминированным. Предсказуемость в этом смысле — не только функция производительности; это функция поддерживаемости.
Сгенерированный код должен быть скучным¶
Генерация кода иногда имеет плохую репутацию, потому что разработчики ассоциируют её с огромными, нечитаемыми файлами, полными загадочных деталей реализации.
Совсем не так должна выглядеть сгенерированная инфраструктура фреймворка.
Kora рассматривает сгенерированный исходный код как часть своей модели прозрачности. В идеале сгенерированный код должен быть относительно скучным: читаются параметры запроса, вызывается метод, применяется маппер, создаётся экземпляр компонента или AOP-обёртка делегирует через требуемую инфраструктуру.
Концептуально:
или:
Цель не в том, чтобы разработчики проводили дни за чтением сгенерированного исходного кода. При обычной разработке им это не должно быть нужно. Важное свойство в том, что они могут изучить его при необходимости.
Есть существенная разница между словами «фреймворк, наверное, как-то оборачивает этот метод» и возможностью открыть сгенерированный класс и увидеть, что именно выполняется.
Код фреймворка в итоге должен стать кодом JVM¶
Это подводит к одному из самых простых способов описать философию Kora.
Фреймворки начинаются с удобных абстракций:
Эти абстракции полезны, потому что никто не хочет вручную писать инфраструктурный связующий код снова и снова. Однако в конечном счёте машине нужны конкретные операции: создание объектов, вызовы методов, разбор параметров, маппинг ответов, вызовы драйверов баз данных, повторы и обращения к кешу.
Kora старается сделать преобразование между этими двумя уровнями явным:
Поэтому фреймворк ведёт себя меньше как интерпретатор, постоянно находящийся внутри приложения, и больше как компилятор, помогающий создать приложение.
Это тонкий, но важный сдвиг.
Не меньше абстракции — а более ранняя абстракция¶
Фреймворки с этапом компиляции иногда понимают неправильно — как попытку убрать абстракции и заставить разработчиков приблизиться к низкоуровневому инфраструктурному коду.
Это не цель.
Писать это вручную:
не изначально лучше, чем писать:
Вторая версия яснее передаёт намерение, и повторяющийся транспортный связующий код, безусловно, должен быть автоматизирован.
Ответ Kora — не устранять высокоуровневый API, а компилировать его в низкоуровневую явную реализацию. Разработчики получают лаконичную модель программирования, а время выполнения — прямой код.
Иными словами, Kora не пытается убрать абстракцию. Он пытается перенести большую часть стоимости абстракции с этапа выполнения.
Тонкие абстракции имеют значение¶
Одной лишь генерации на этапе компиляции недостаточно. Фреймворк мог бы сгенерировать огромные стеки абстракций так же легко, как построить их динамически.
Kora сочетает генерацию с ещё одним проектным решением: оставаться относительно близко к базовым технологиям. Проект использует знакомые концепции JVM вокруг Java и Kotlin, JDBC, Cassandra, HTTP, Kafka, gRPC, OpenTelemetry и другой инфраструктуры, а не пытается заменить каждую технологию совершенно отдельной концептуальной моделью.
Это важно, потому что каждая абстракция фреймворка имеет стоимость обучения. Разработчикам нужно понимать саму технологию, модель этой технологии во фреймворке и отображение между ними.
Тонкие абстракции стараются сделать это отображение небольшим. Результат — не нулевая абстракция, а абстракция, сосредоточенная на устранении повторяющейся работы без сокрытия технологии за совершенно иной ментальной моделью.
Почему это важно для больших систем¶
Для одного небольшого сервиса многие из этих различий могут выглядеть академическими. Современное оборудование легко запускает приложения, построенные на разных архитектурах JVM.
Однако организации редко эксплуатируют один сервис.
Небольшая стоимость фреймворка на экземпляр умножается на экземпляры в сервисе, зоны доступности, сервисы, команды, окружения и запас масштабирования. То, что кажется незначительным на ноутбуке разработчика, может стать реальной инфраструктурной стоимостью при повторении тысячи раз.
То же умножение происходит с инженерной сложностью. Одна малопонятная конвенция времени выполнения может быть управляемой для эксперта по фреймворку, но эта конвенция в итоге умножается на сотни сервисов, десятки разработчиков, новых сотрудников, обновления и производственные инциденты.
Поэтому Kora рассматривает эффективность и прозрачность как связанные задачи. Меньше механизмов фреймворка означает меньше ресурсов, потребляемых каждым приложением, а больше явных механизмов означает меньше состояния времени выполнения, которое инженерам нужно держать в голове.
Ошибки на этапе компиляции — это функция¶
Одно из следствий этой архитектуры — Kora может быть строже во время компиляции.
Предположим, компоненту требуется:
но допустимой реализации не существует. Фреймворк времени выполнения может позволить проекту успешно скомпилироваться, собрать образ контейнера, запустить развёртывание и лишь затем упасть при построении своего контекста приложения.
Kora предпочёл бы обнаружить эту проблему во время компиляции.
То же самое относится к неоднозначным зависимостям, недопустимым маппингам, циклам зависимостей и многим другим контрактам фреймворка.
Сравните два режима отказа:
сборка успешна
образ контейнера создан
развёртывание запускается
приложение запускается
фреймворк строит контекст
создание контекста терпит неудачу
развёртывание терпит неудачу
против:
Второй отказ дешевле.
Поэтому строгий компилятор может действовать как ещё один архитектурный тест, а не просто как проверка синтаксиса.
Стоимость на этапе сборки не бесплатна¶
Генерация на этапе компиляции, естественно, имеет стоимость. Процессоры аннотаций и KSP-процессоры должны выполняться, сгенерированный исходный код нужно компилировать, а анализ графа занимает время.
Архитектура Kora не заставляет работу фреймворка исчезнуть. Она перемещает эту работу.
Вместо того чтобы платить большую часть стоимости при каждом запуске приложения, фреймворк платит её во время сборки. Для серверных приложений это часто выгодно, потому что один артефакт может запускаться многократно на машинах разработчиков, в тестах, окружениях CI, на стейджинге, в продакшене, при поэтапных развёртываниях, событиях автоскейлинга и сбоях машин.
Компиляция происходит до всех этих запусков.
Поэтому релевантный обмен — не «работа против отсутствия работы». Это где эта работа должна находиться.
Почему не генерировать всё?¶
У генерации на этапе компиляции есть и важная граница.
Некоторая информация по своей природе динамична. Значения конфигурации меняются, результаты базы данных меняются, удалённые системы становятся здоровыми или нездоровыми, запросы несут разные данные, а состояние отказоустойчивости эволюционирует во время работы процесса.
Kora не пытается превратить серверное приложение в полностью статичную программу. Вместо этого он разделяет архитектуру, которая уже познаваема во время компиляции, и состояние, которое по-настоящему принадлежит этапу выполнения.
Вопросы этапа компиляции включают:
Какие компоненты существуют?
Что от чего зависит?
Как этот метод контроллера должен сопоставлять свои аргументы?
Как этот метод репозитория должен выполнять свой запрос?
Какие аспекты окружают этот метод?
Какой маппер преобразует этот тип?
Вопросы этапа выполнения включают:
Какое настроено время ожидания?
Какой запрос пришёл?
Что вернула база данных?
Здоров ли удалённый сервис?
Открылся ли предохранитель (circuit breaker)?
Каков текущий контекст трассировки?
Цель — не сделать всё статичным. Цель — не обнаруживать многократно на этапе выполнения то, что уже было познаваемо во время компиляции.
Фреймворк, о котором можно рассуждать¶
Практический результат — не просто фреймворк, который быстро запускается или потребляет меньше ресурсов. Это фреймворк, поведение которого задумано оставаться понятным.
Получив HTTP-запрос, инженер должен иметь возможность мысленно проследить такой путь:
HTTP-запрос
↓
сгенерированный HTTP-обработчик
↓
контроллер
↓
сервис
↓
сгенерированная AOP-обёртка
↓
сгенерированный репозиторий
↓
JDBC
не пересекая большой непрозрачный контейнер времени выполнения, чьё внутреннее состояние сначала нужно реконструировать.
Это облегчает отладку, архитектурные ревью и введение в проект, потому что конкретные отношения остаются видимыми в коде.
Есть и полезный побочный эффект для современных инструментов разработки. Инструменты с поддержкой нейро-технологий рассуждают гораздо надёжнее об явных классах Java или Kotlin, вызовах методов, сгенерированных реализациях и типизированных контрактах, чем о поведении, существующем только как состояние контейнера времени выполнения. Поэтому сгенерированный исходный код приносит пользу не только человеческой проверке, но и машинным рассуждениям.
Более широкая философия¶
Генерация на этапе компиляции в конечном счёте — одно из выражений более широкого принципа Kora: перенести знание из скрытого состояния фреймворка времени выполнения в вещи, которые можно напрямую проверить — типы, исходный код, сгенерированный исходный код, диагностику компилятора и явную конфигурацию.
Этот принцип влияет на несколько частей дизайна Kora:
- внедрение зависимостей на этапе компиляции;
- сгенерированные HTTP-адаптеры;
- сгенерированные репозитории;
- сгенерированные маппинги;
- AOP на этапе компиляции;
- строго типизированная генерация OpenAPI;
- явные модули;
- тонкие интеграции;
- прямые модели программирования на Java и Kotlin.
Результирующий фреймворк в некоторых местах намеренно менее динамичен, чем традиционные фреймворки, ориентированные на этап выполнения. Это не случайно. Kora предпочитает определённость гибкости времени выполнения, которая многим серверным приложениям на самом деле не нужна, сгенерированную реализацию — неявной интерпретации времени выполнения, а ошибки компилятора — сюрпризам на этапе выполнения.
Фреймворк должен исчезать¶
Полезный способ думать о конечной цели — что фреймворк должен помогать разработчикам выражать намерение, а затем уходить с дороги.
Разработчики пишут такие объявления:
Фреймворк генерирует связующий код, необходимый, чтобы сделать эти объявления реальными. После этого, насколько это возможно, остальное не должно удивлять инженера JVM: объекты, вызовы методов, интерфейсы, сгенерированные классы, драйверы баз данных, HTTP-обработчики, исполнители, виртуальные потоки и обычный код JVM.
Именно поэтому сгенерированный исходный код так важен для архитектуры Kora. Он представляет собой точку, где абстракция фреймворка становится конкретной реализацией программы.
Фреймворку больше не нужно оставаться постоянным интерпретатором между бизнес-кодом и JVM.
Уверенность на этапе компиляции¶
Kora иногда представляют через цифры производительности. Эти цифры важны: быстрый запуск, низкое потребление памяти и низкие накладные расходы времени выполнения — всё это желательные свойства.
Но они — следствия более глубокого архитектурного решения.
Kora задаёт более фундаментальный вопрос:
Какая часть фреймворка может стать частью скомпилированного приложения, а не оставаться частью среды выполнения?
Внедрение зависимостей может. HTTP-адаптеры могут. Репозитории могут. Мапперы могут. AOP может. Большая часть связывания приложения может.
Как только эти решения переходят на этап компиляции, вместе возникают несколько полезных свойств:
больше валидации на этапе компиляции
↓
меньше сюрпризов на этапе выполнения
меньше обнаружения на этапе выполнения
↓
быстрее запуск
меньше механизмов времени выполнения
↓
ниже накладные расходы
сгенерированный исходный код
↓
больше прозрачности
явный граф
↓
больше предсказуемости
Именно эта комбинация делает модель с этапом компиляции интересной.
Это не само по себе избегание рефлексии, не сама по себе генерация кода и не сама по себе производительность в бенчмарках. Важная идея в том, что фреймворк превращает высокоуровневые объявления в конкретный, типизированный, проверяемый код JVM до того, как продакшен вообще увидит приложение.
Это архитектура, стоящая за Kora, и её можно свести к одному принципу:
Если фреймворк может знать что-то на этапе компиляции, должна быть очень веская причина, чтобы обнаруживать это снова на этапе выполнения.