Начнется ли транзакция, если нетранзакционный метод вызывает транзакционный метод внутри одного Spring-бина?

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

Ответ

Нет, транзакция не начнется. Это классическое ограничение работы Spring AOP-прокси.

Причина: Транзакционность в Spring реализуется через прокси-объекты. Внутренний вызов метода (this.transactionalMethod()) минует прокси, и, следовательно, перехватчик транзакций не срабатывает.

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

@Service
public class UserService {

    public void createUserNonTransactional(String name) {
        // Этот вызов происходит напрямую, минуя прокси.
        createUserTransactional(name); // Транзакция НЕ БУДЕТ создана!
    }

    @Transactional
    public void createUserTransactional(String name) {
        // Логика, требующая транзакции
        userRepository.save(new User(name));
    }
}

Способы решения:

  1. Self-injection: Внедрить самого себя и вызывать метод через прокси.

    @Service
    public class UserService {
        @Autowired
        private UserService self; // Ссылка на прокси
    
        public void createUserNonTransactional(String name) {
            self.createUserTransactional(name); // Теперь транзакция работает.
        }
        // ...
    }
  2. Разделение на разные бины: Вынести транзакционный метод в отдельный сервисный компонент.
  3. Использование AspectJ mode: Настройка Spring на использование AspectJ для компоновки во время загрузки (LTW) или компиляции (CTW) обходит это ограничение, но сложнее в настройке.