@Transactional на private final-методе — контекст не поднимется вообще

Частая ошибка в коде, который приносят на ревью: аннотация @Transactional висит на приватном методе внутри класса, помеченного final. Выглядит безобидно, но приложение не стартует.

Spring оборачивает бины прокси, чтобы перехватывать вызовы методов до их выполнения — открыть транзакцию, закоммитить, откатить при исключении. Для классов без интерфейсов Spring использует CGLIB — он создаёт подкласс целевого класса и переопределяет нужные методы.

Если класс final, подкласс создать нельзя — это запрещено на уровне JVM. Если метод private, его тоже нельзя переопределить в подклассе, даже если бы класс был открыт. Spring падает ещё на старте контекста:

BeanCreationException: ... Could not generate CGLIB subclass of class PaymentService: Common causes of this problem include using a final class or a non-visible class

Это не рантайм-ошибка на конкретном вызове — приложение не поднимется совсем, упадёт на этапе создания бина.

Спросят следом: а что если убрать final с класса, но оставить private на методе? Ответ: аннотация на private-методе просто игнорируется молча — Spring создаст прокси, но перехватывать такой метод не станет, транзакции не будет, и никакого исключения на старте не произойдёт. Это куда опаснее финальной версии бага: ошибка не кричит, а тихо не работает в проде.

Правило простое: @Transactional ставится на public или protected методах открытых классов. Если нужен приватный метод внутри транзакции — выносите логику в отдельный бин с публичным методом.

Тренажёр: 900+ вопросов и задач, мок с таймером, план повторов

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки