Подтвердить что ты не робот

Как обрабатывать внутренние вызовы на Spring/EJB/Mockito... прокси?

Как вы знаете, когда вы проксируете объект, например, когда вы создаете bean с транзакционными атрибутами для Spring/EJB или даже когда вы создаете частичный макет с некоторыми фреймворками, объект proxies не знает, что, а внутренние вызовы не перенаправляются, а затем не перехватываются...

Вот почему, если вы делаете что-то подобное в Spring:

@Transactionnal
public void doSomething() {
    doSomethingInNewTransaction();
    doSomethingInNewTransaction();
    doSomethingInNewTransaction();
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void doSomethingInNewTransaction() {
    ...
}

Когда вы вызываете doSomething, вы ожидаете иметь 3 новых транзакции в дополнение к основному, но на самом деле из-за этой проблемы вы получаете только один...


Так что интересно, как вы справляетесь с такими проблемами...

Я нахожусь в ситуации, когда я должен обрабатывать сложную транзакционную систему, и я не вижу лучшего способа, чем разделение моего сервиса на многие небольшие службы, поэтому я уверен, что пройду через все прокси...

Это меня очень беспокоит, потому что весь код принадлежит к одному и тому же функциональному домену и не должен быть разделен...

Я нашел этот связанный вопрос с интересными ответами: Spring - @Transactional - Что происходит в фоновом режиме?

Rob H говорит, что мы можем ввести прокси-сервер spring внутри службы и вызвать proxy.doSomethingInNewTransaction(); вместо. Это довольно легко сделать, и это работает, но мне это не очень нравится...

Юньфэн Хоу говорит об этом:

Итак, я пишу свою собственную версию CglibSubclassingInstantiationStrategy и прокси-создателя, чтобы он использовал CGLIB для создания реального подкласса которые делегаты призывают к своему супер, а не к другому экземпляру, который spring делает сейчас. Поэтому я могу свободно комментировать любые методы (так долго поскольку он не является частным), и откуда бы я ни называл эти методы, они будет позаботиться. Ну, у меня еще есть цена для оплаты: 1. Я должен перечислить все аннотации, которые я хочу включить в новый подкласс класса CGLIB создание. 2. Я не могу аннотировать окончательный метод, так как я сейчас генерируя подкласс, поэтому окончательный метод не может быть перехвачен.

Что он подразумевает под словом "который spring делает сейчас"? Означает ли это, что внутренние транзакционные вызовы теперь перехватываются?


Как вы думаете, лучше?

Разделяете ли вы свои классы, когда вам нужна гранулярность транзакций? Или вы используете некоторое обходное решение, как описано выше? (пожалуйста, поделитесь им)

4b9b3361

Ответ 1

Я расскажу о Spring и @Transactional, но совет применим и для многих других фреймворков.

Это неотъемлемая проблема с аспектами, основанными на прокси. Это обсуждается в документации Spring здесь:

http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies

Существует ряд возможных решений.

Рефакторинг ваших классов, чтобы избежать вызовов самозапуска, которые обходят прокси.

Документация Spring описывает это как "Лучший подход (термин" лучше всего используется здесь свободно ").

Преимущества такого подхода - его простота и отсутствие связей с какой-либо структурой. Тем не менее, это может быть неприемлемо для очень транзакционной базы тяжелого кода, так как вы в конечном итоге получаете несколько небольших классов.

Внутренне в классе получить ссылку на прокси.

Это можно сделать, введя прокси-сервер или жестко закодированный вызов AopContext.currentProxy() (см. ниже Spring).

Этот метод позволяет избежать разделения классов, но во многом отрицает преимущества использования транзакционной аннотации. Мое личное мнение состоит в том, что это одна из тех вещей, которая немного уродлива, но уродство самодостаточно и может быть прагматичным подходом, если используется большое количество транзакций.

Переключиться на использование AspectJ

Поскольку AspectJ не использует прокси, то самозапуск не является проблемой

Это очень чистый метод, но это за счет введения другой структуры. Я работал над большим проектом, где AspectJ был представлен именно по этой причине.

Не используйте @Transactional вообще

Восстановите свой код, чтобы использовать ручную демаркацию транзакций - возможно, используя шаблон декоратора.

Опция - но такая, которая требует умеренного рефакторинга, введения дополнительных рамочных связей и повышенной сложности - поэтому, вероятно, не предпочтительный вариант

Мой совет

Обычно расщепление кода является лучшим ответом и также может быть хорошим для разнесения проблем. Однако, если бы у меня была структура/приложение, которое в значительной степени полагалось на вложенные транзакции, я бы рассмотрел возможность использования AspectJ для самостоятельного вызова.

Ответ 2

Как всегда при моделировании и разработке сложных вариантов использования - сосредоточиться на понятной и поддерживаемой конструкции и коде. Если вы предпочитаете определенный шаблон или дизайн, но он сталкивается с базовой структурой, подумайте над тем, стоило ли это сложной обходной путь, чтобы подгонять ваш дизайн в структуру, или если вы должны пойти на компромисс и приспособить свой дизайн к инфраструктуре, где это необходимо. Не сражайтесь с каркасом, если вам абсолютно не нужно.

Мой совет - если вы можете достичь своей цели с таким легким компромиссом, как разделить на несколько дополнительных классов обслуживания - сделайте это. Это звучит намного дешевле с точки зрения времени, тестирования и агонии, чем альтернатива. И это, конечно, звучит намного легче для поддержания и меньше головной боли для следующего парня, который возьмет верх.

Ответ 3

Я обычно делаю это простым, поэтому я разделяю код на два объекта.

Альтернативой является разграничение новой транзакции самостоятельно, если вам нужно сохранить все в одном файле, используя TransactionTemplate. Еще несколько строк кода, но не более, чем определение нового bean. И это иногда делает точку более очевидной.