kotlin-spring и kotlin-jpa: зачем компилятору их плагины
Вопрос звучит безобидно: «подключали kotlin-spring в проекте?» — но проверяет понимание того, как вообще работает @Transactional в Kotlin.
В Kotlin классы и методы final по умолчанию — в отличие от Java, где нужно явно писать final, чтобы запретить наследование. Spring AOP для @Transactional, @Cacheable, @Async и @Configuration в классическом Spring Boot делает прокси через наследование — CGLIB генерирует подкласс и переопределяет методы, добавляя логику вокруг вызова.
Если класс или метод final, CGLIB не может его переопределить. Прокси либо не создастся, либо создастся «пустым» — аннотация тихо перестанет работать, без ошибки компиляции и без исключения в рантайме.
// без плагина kotlin-spring: class PaymentService { // final по умолчанию @Transactional fun charge(id: Long) { ... } // CGLIB не переопределит final-метод }
Плагин kotlin-spring — это надстройка над kotlin-allopen, которая автоматически делает классы с аннотациями вроде @Component, @Service, @Configuration и их методы open, без ручного open class на каждом бине.
Отдельный плагин kotlin-jpa решает похожую, но другую проблему: JPA-сущностям нужен конструктор без аргументов и открытые (не final) классы для создания ленивых прокси Hibernate. kotlin-jpa автоматически генерирует no-arg конструктор для классов с @Entity, @MappedSuperclass и @Embeddable и делает их open — без этого Hibernate не сможет создать прокси для lazy-загрузки и упадёт на попытке инстанцировать сущность рефлексией.
Если на собесе спрашивают про этот плагин — ждут не название библиотеки, а связку: final по умолчанию в языке → CGLIB не может унаследоваться → аннотация молча не работает.
Тренажёр: 1900+ вопросов и задач, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки