Комментарии 16
Частный случай первого пункта, аннотация над приватным методом класса, тоже часто встречается.
Проблема может быть решена тремя основными способами:
- Самостоятельная инъекция (Self-inject)
- Создать еще один уровень абстракции
- Использовать
TransactionTemplate
в методеregisterAccount()
, обернув вызовcreateAccount()
Не упомянут один довольно важный и ценный способ решения — перейти в приложении с run-time-weaving
на compile-time-weaving
. В этом случае вызов аспектов будет встроен прямо в байт-код класса, а не проведён через прокси, как это сделано по умолчанию.
Также compile-time
weaving позволяет работать даже аннотациям, которые стоят на методах компонента, не включённых ни в один его интерфейс. Я лично влетал в такую проблему с load-time
, когда у меня EventListener
'ы не работали из-за этого.
не включённых ни в один его интерфейс
Чтобы такое работало в load-time, достаточно просто включить создание прокси через cglib, а не через стандартный явовский Proxy.
@EnableAspectJAutoProxy(proxyTargetClass = true)
Да, сама проблема в статье это следствие использования Java Proxy. И compile-time weaving
, и cglib проблемы такой не имеют, потому что оба встраивают вызов аспектов в байт-код. Только один в рантайме, а второй при компиляции.
Зачем тогда по-умолчанию используется Java Proxy?
Это мне неведомо, но предположительно из-за меньшей инвазивности в процесс сборки проекта.
У Java пока что нет нормального ClassFile API, который был бы относительно стабилен между версиями, а существующее байтоложество - штука достаточно хрупкая, хотя за годы развития отлажена стала неплохо. Proxy, при этом - стабилен, включён в JDK, и позволяет удовлетворительно закрыть множество сценариев использования аннотаций. Особенно в комбинации с анализаторами типа Sonar, которые будут бить по рукам и напоминать.
А если класс не реализует никакой интерфейс как в примере, то такой проблемы не возникает при вызове снаружи класса? То есть если класс реализует какой-то интерфейс, то при вызове снаружи класса метода (который не объявлен в интерфейсе) с аннотацией transaction, эта аннотация не сработает?
Также
compile-time
weaving позволяет работать даже аннотациям, которые стоят на методах компонента, не включённых ни в один его интерфейс
Если интерфейсов нет, то будет откат к генерации класса через CGLIB.
То есть если класс реализует какой-то интерфейс, то при вызове снаружи класса метода (который не объявлен в интерфейсе) с аннотацией transaction, эта аннотация не сработает?
Сначала хотел ответить будто знаю, как он себя поведёт - но в итоге понял, что не знаю, и надо будет экспериментировать с этим.
Точно знаю, что пару лет назад декларации @EventListener
требовали наличия их в интерфейсе, так как для их работы было нужно, чтобы прокси-класс при просмотре через рефлексию возвращал соответствующие методы, а он сможет это делать только если они были в его родительских интерфейсах. У меня не было похожих проблем с @Transactional
, но и ситуации, когда имеется помеченный метод, не входящий в какой-либо интерфейс не было тоже.
Даже для @EventListener
это поведение, в принципе, с того времени могло измениться, хотя для такого старого компонента системы шансы на это мизерные.
>>Различные механизмы распространения помогают связать транзакции в нашей бизнес-логике. Например, если вам нужно запустить какой-то код в другой транзакции, а не во внешней, можно использовать распространение REQUIRES_NEW
, которое приостанавливает внешнюю транзакцию, создает новую, а затем возобновляет внешнюю транзакцию.
REQUIRES_NEW ничего не "приостанавливает" - вы из одного потока управления стратанули 2 транзакции, можете стартануть 3, 4 и так далее - это все будут активные транзакции созданные в одном потоке. Схема скорее походит на ChainedTransactionManager с той лишь разницой что у всех транзакций один и тот же TransactionManager
ChainedTransactionManager - depricated
А вы сами ссылку на bitronix, которую даёте, открывали? Из современных поддерживаемых проектов narayana есть.
Есть полный список таких транзакций?
Так происходит и с другими аннотациями, как, к примеру, @Cacheable.
Spring @Transactional — ошибки, которые совершали все