Есть ли альтернативы self injection для создания новой транзакции при вызове метода внутри класса
Что проверяют
Понимаете ли вы, почему @Transactional(propagation = Propagation.REQUIRES_NEW) на методе, вызванном через this, не открывает новую транзакцию. Интервьюер хочет услышать про прокси Spring AOP, а затем спрашивает, какое решение вы бы выбрали в реальном коде.
Как отвечать
Причина одна: транзакционность добавляет прокси вокруг бина, а внутренний вызов this.method() идёт мимо прокси, и аннотация игнорируется. Self injection (ссылка бина на самого себя через @Lazy или ObjectProvider) это обходит, но есть альтернативы:
- отдельный бин: самый чистый вариант. Вызов идёт через прокси другого сервиса, границы транзакций видны из структуры кода;
TransactionTemplate: программное управление. ЗадаётеsetPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW)и выполняете нужный кусок вexecute(). От прокси не зависит, поэтому работает при любом способе вызова;AopContext.currentProxy(): при@EnableAspectJAutoProxy(exposeProxy = true)можно достать текущий прокси и вызвать метод через него. Работает, но бизнес-код начинает знать про AOP;- AspectJ weaving:
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)с compile-time или load-time weaving. Логика вшивается в байткод, и внутренний вызов тоже становится транзакционным, но сборка и запуск заметно усложняются.
Хорошая позиция: по умолчанию вынос в отдельный бин, а TransactionTemplate, когда новая транзакция нужна небольшому фрагменту, например записи аудита, которая должна сохраниться даже при откате основной.
Назовите и цену REQUIRES_NEW: внешняя транзакция приостанавливается, но держит своё соединение, а новая берёт второе из пула. При маленьком пуле и высокой нагрузке потоки исчерпывают соединения и ждут друг друга.
Где спотыкаются
Предлагают написать собственный аспект. Он тоже работает через прокси и при внутреннем вызове не сработает ровно так же.
Называют решением паттерн «шаблонный метод». Он меняет структуру классов, но вызов из базового класса в подкласс остаётся вызовом через this.
Начинают с перечня вариантов, не объяснив причину. Без рассказа про прокси любой список выглядит заученным.