@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 — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки