Как обрабатывать внутренние звонки на Spring / EJB / Mockito ... прокси? - PullRequest
15 голосов
/ 07 ноября 2011

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

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

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

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

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


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

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

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

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

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

Юньфэн Хоу говорит так:

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

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


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

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

Ответы [ 3 ]

17 голосов
/ 23 ноября 2011

Я поговорю о Spring и @Transactional, но рекомендации применимы и ко многим другим фреймворкам.

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

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 вообще

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

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

My Advice

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

4 голосов
/ 07 ноября 2011

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

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

3 голосов
/ 07 ноября 2011

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

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

...