Я хочу знать, что на самом деле происходит, когда вы аннотируете метод с помощью @Transactional
? Конечно, я знаю, что Spring обернет этот метод в транзакции.
Но у меня есть следующие сомнения:
- Я слышал, что Spring создает прокси-класс ? Может кто-нибудь объяснить это более подробно . Что на самом деле находится в этом прокси-классе? Что происходит с реальным классом? И как я могу увидеть созданный Spring прокси класс
- Я также прочитал в Spring документы, которые:
Примечание. Поскольку этот механизм основан на прокси-серверах, будут перехватываться только внешние вызовы методов, поступающие через прокси-сервер . Это означает, что «самовывоз», то есть метод в целевом объекте, вызывающий какой-либо другой метод целевого объекта, не приведет к реальной транзакции во время выполнения, даже если вызванный метод помечен знаком
@Transactional
!
Источник: http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
Почему в транзакции будут только внешние вызовы методов, а не методы самовывоза?
Ответы:
Это большая тема. Справочный документ Spring посвящен нескольким главам. Я рекомендую ознакомиться с ними по аспектно-ориентированному программированию и транзакциям , так как поддержка декларативных транзакций Spring использует AOP в своей основе.
Но на очень высоком уровне Spring создает прокси для классов, которые объявляют @Transactional для самого класса или для членов. Прокси обычно невидим во время выполнения. Он предоставляет Spring способ внедрить поведение до, после или вокруг вызовов методов в объекте, который проксируется. Управление транзакциями - это только один пример поведения, которое можно подключить. Проверки безопасности - другой. И вы можете предоставить свои собственные, например, для ведения журнала. Поэтому, когда вы аннотируете метод с помощью @Transactional , Spring динамически создает прокси, который реализует тот же интерфейс (ы), что и класс, который вы аннотируете. И когда клиенты делают вызовы в ваш объект, вызовы перехватываются, а поведение вводится через механизм прокси.
Между прочим, транзакции в EJB работают аналогично.
Как вы заметили, прокси-механизм работает только тогда, когда поступают вызовы от какого-то внешнего объекта. Когда вы делаете внутренний вызов внутри объекта, вы действительно делаете вызов через ссылку « this », которая обходит прокси. Однако есть способы обойти эту проблему. Я объясняю один подход в этом сообщении на форуме, в котором я использую BeanFactoryPostProcessor для внедрения экземпляра прокси-сервера в « самоссылающиеся » классы во время выполнения. Я сохраняю эту ссылку в переменной-члене " me ». Затем, если мне нужно сделать внутренние вызовы, которые требуют изменения статуса транзакции потока, я направляю вызов через прокси (например, me.someMethod () ".) В сообщении на форуме объясняется более подробно. Обратите внимание, что BeanFactoryPostProcessorкод теперь будет немного другим, так как он был написан еще во времена Spring 1.x. Но, надеюсь, это даст вам идею. У меня есть обновленная версия, которую я мог бы сделать доступной.
источник
BeanFactoryPostProcessor
. Тем не менее, есть (на мой взгляд) очень похожий метод, описанный в этом ответе: stackoverflow.com/a/11277899/3667003 ... и другие решения также во всей цепочке.Когда Spring загружает ваши определения bean-компонентов и настроен на поиск
@Transactional
аннотаций, он создает эти прокси-объекты вокруг вашего фактического bean-компонента . Эти прокси-объекты являются экземплярами классов, которые автоматически генерируются во время выполнения. Поведение этих прокси-объектов по умолчанию при вызове метода состоит в том, чтобы просто вызывать тот же метод для «целевого» компонента (т. Е. Вашего компонента).Однако прокси также могут быть снабжены перехватчиками, и когда они присутствуют, эти перехватчики будут вызываться прокси, прежде чем он вызовет метод вашего целевого компонента. Для целевых компонентов, помеченных как
@Transactional
, Spring создастTransactionInterceptor
и передаст его сгенерированному прокси-объекту. Поэтому, когда вы вызываете метод из клиентского кода, вы вызываете метод для прокси-объекта, который сначала вызываетTransactionInterceptor
(который начинает транзакцию), который, в свою очередь, вызывает метод для вашего целевого компонента. По завершении вызоваTransactionInterceptor
фиксирует / откатывает транзакцию. Это прозрачно для клиентского кода.Что касается «внешнего метода», если ваш компонент вызывает один из своих собственных методов, он не будет делать это через прокси. Помните, Spring оборачивает ваш компонент в прокси, ваш компонент не знает об этом. Только вызовы "извне" вашего компонента проходят через прокси.
Это помогает?
источник
Как визуальный человек, мне нравится взвешивать диаграмму последовательности паттерна прокси. Если вы не знаете, как читать стрелки, я прочитал первый, например, так:
Client
выполняетProxy.method()
.(Мне разрешили опубликовать фотографию при условии, что я упомянул ее происхождение. Автор: Ноэль Ваес, веб-сайт: www.noelvaes.eu)
источник
Самый простой ответ:
В зависимости от того, какой метод вы объявляете,
@Transactional
граница транзакции начинается и заканчивается, когда метод завершается.Если вы используете вызов JPA, то все коммиты находятся в этой границе транзакции .
Допустим, вы сохраняете entity1, entity2 и entity3. Теперь при сохранении entity3 происходит исключение , тогда как enitiy1 и entity2 входят в одну и ту же транзакцию, поэтому entity1 и entity2 будут откатываться с entity3.
Транзакция:
Любое исключение приведет к откату всех транзакций JPA с DB. Внутренне транзакция JPA используется Spring.
источник
Это может быть поздно, но я столкнулся с чем-то, что объясняет вашу озабоченность, связанную с прокси-сервером (будут перехватываться только внешние вызовы методов, поступающие через прокси-сервер).
Например, у вас есть класс, который выглядит следующим образом
и у вас есть аспект, который выглядит так:
Когда вы выполняете это так:
}
Результаты вызова kickOff выше указанного кода выше.
но когда вы меняете код на
Видите ли, метод внутренне вызывает другой метод, поэтому он не будет перехвачен, и результат будет выглядеть следующим образом:
Вы можете обойти это, делая это
Фрагменты кода взяты из: https://www.intertech.com/Blog/secrets-of-the-spring-aop-proxy/
источник
Все существующие ответы верны, но я чувствую, что не могу дать только эту сложную тему.
Чтобы получить исчерпывающее практическое объяснение, вам, возможно, захочется взглянуть на это подробное руководство Spring @Transactional , в котором изо всех сил рассматривается управление транзакциями в ~ 4000 простых слов с множеством примеров кода.
источник