Почему в Spring аннотация @Transactional не работает для private-методов?

«Почему в Spring аннотация @Transactional не работает для private-методов?» — вопрос из категории Spring, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Аннотация @Transactional в Spring не работает для privateprotected) методов из-за ограничений механизма проксирования, лежащего в основе декларативного управления транзакциями.

Причина: Spring AOP (и стандартный механизм прокси на основе JDK или CGLIB) не может перехватить вызов приватного метода.

  • Прокси-объект оборачивает целевой бин и перехватывает вызовы его методов, чтобы добавить transactional-логику (открытие/закрытие транзакции).
  • Вызов private-метода происходит внутри самого класса, минуя прокси. Это называется self-invocation.

Пример проблемы:

@Service
public class UserService {
    public void processUser() {
        updateUser(); // Внутренний вызов -> прокси не участвует!
    }

    @Transactional // НЕ СРАБОТАЕТ!
    private void updateUser() {
        // Логика обновления в БД
    }
}

Решения:

  1. Сделать метод public (или protected для CGLIB-прокси). Это стандартное решение.
  2. Использовать TransactionTemplate для программного управления транзакциями внутри метода.

    @Service
    public class UserService {
        @Autowired
        private TransactionTemplate transactionTemplate;
    
        private void updateUser() {
            transactionTemplate.execute(status -> {
                // Логика в транзакции
                return null;
            });
        }
    }
  3. Внедрить зависимость от самого себя (через AopContext или @Autowired), чтобы вызвать метод через прокси. Этот подход считается антипаттерном и нарушает инкапсуляцию.

Вывод: Для работы @Transactional метод должен быть виден для прокси, то есть иметь как минимум protected видимость (при использовании CGLIB) или public.