Spring - @Transactional - Что происходит в фоновом режиме?

Я хочу знать, что на самом деле происходит, когда вы комментируете метод с помощью @Transactional? Конечно, я знаю, что Spring перенесет этот метод в транзакцию.

Но у меня есть следующие сомнения:

  • Я слышал, что Spring создает прокси-класс? Может ли кто-нибудь объяснить это с помощью глубины. Что на самом деле находится в этом прокси-классе? Что происходит с фактическим классом? И как я могу увидеть Spring созданный прокси-класс
  • Я также читал в документах Spring, которые:

Примечание. Поскольку этот механизм основан на прокси-серверах, только "внешние" вызовы методов, поступающие через прокси-сервер, будут перехвачены. Это означает, что "self-invocation", то есть метод внутри целевого объекта, вызывающий какой-либо другой метод целевого объекта, не приведет к фактической транзакции во время выполнения, даже если вызываемый метод помечен @Transactional!

Источник: http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

Почему только вызовы внешних методов будут в транзакции, а не в методах самозапуска?

Ответ 1

Это большая тема. Справочный документ Spring посвящает ему несколько глав. Я рекомендую прочитать те, что относятся к Аспектно-ориентированное программирование и Транзакции, поскольку поддержка декларативной транзакции Spring использует AOP в ее основе.

Но на очень высоком уровне Spring создает прокси для классов, объявляющих @Transactional для самого класса или для членов. Прокси-сервер в большинстве случаев невидим во время выполнения. Он предоставляет способ Spring вводить поведение до, после или вокруг вызовов методов в прокси-объект. Управление транзакциями - это всего лишь один пример поведения, к которому можно подключиться. Проверки безопасности - это еще один. И вы можете предоставить свои собственные тоже для таких вещей, как ведение журнала. Поэтому, когда вы комментируете метод с @Transactional, Spring динамически создает прокси-сервер, который реализует один и тот же интерфейс (-и) как класс, который вы комментируете. И когда клиенты совершают вызовы в ваш объект, вызовы перехватываются, а поведение вводится через механизм прокси.

Транзакции в EJB работают, между прочим, кстати.

Как вы видели, механизм прокси работает только тогда, когда вызовы поступают от какого-либо внешнего объекта. Когда вы делаете внутренний вызов внутри объекта, вы действительно вызываете ссылку " this", которая обходит прокси. Однако есть способы обойти эту проблему. Я объясняю один подход в этот пост форума, в котором я использую BeanFactoryPostProcessor, чтобы вставить экземпляр прокси-сервера в "self во время выполнения. Я сохраняю эту ссылку в переменной-члене под названием" me ". Затем, если мне нужно сделать внутренние вызовы, требующие изменения состояния транзакции потока, я направляю вызов через прокси (например," me.someMethod()".) Сообщение форума объясняется в более детально. Обратите внимание, что теперь код BeanFactoryPostProcessor немного изменится, так как он был записан в Spring 1.x таймфрейме. Но, надеюсь, это дает вам представление. У меня есть обновленная версия, которую я мог бы сделать доступной.

Ответ 2

Когда Spring загружает определения bean и настроен на поиск аннотаций @Transactional, он создаст эти прокси-объекты вокруг вашего фактического bean. Эти прокси-объекты являются экземплярами классов, которые автоматически генерируются во время выполнения. Поведение этих прокси-объектов по умолчанию при вызове метода - это просто вызвать тот же метод в "target" bean (т.е. Ваш bean).

Однако прокси также могут быть снабжены перехватчиками, и, когда они присутствуют, эти перехватчики будут вызываться прокси-сервером до того, как он вызовет ваш целевой метод bean. Для целевого beans, аннотированного с помощью @Transactional, Spring создаст TransactionInterceptor и передаст его созданному прокси-объекту. Поэтому, когда вы вызываете метод из клиентского кода, вы вызываете метод на прокси-объекте, который сначала вызывает TransactionInterceptor (который начинает транзакцию), который, в свою очередь, вызывает метод в вашей целевой bean. Когда вызов завершается, TransactionInterceptor совершает/откатывает транзакцию. Он прозрачен для кода клиента.

Что касается объекта "внешнего метода", если ваш bean вызывает один из его собственных методов, то он не будет делать этого через прокси. Помните, Spring обертывает ваш bean в прокси, ваш bean не знает об этом. Только прокси из "внешнего" вашего bean проходят через прокси.

Помогает ли это?

Ответ 3

Как визуальный человек, мне нравится взвесить диаграмму последовательности прокси-шаблона. Если вы не знаете, как читать стрелки, я читаю первый такой: Client выполняет Proxy.method().

  • Клиент вызывает метод на целевой стороне с его точки зрения и молча перехватывает прокси
  • Если задан предшествующий аспект, прокси-сервер выполнит его
  • Затем выполняется фактический метод (цель)
  • Последующие и последующие броски являются дополнительными аспектами, которые выполняется после возвращения метода и/или если метод выдает исключение
  • После этого прокси выполняет следующий аспект (если он определен)
  • Наконец, прокси возвращается к вызывающему клиенту

Proxy Pattern Sequence Diagram (Мне разрешили опубликовать фотографию при условии, что я упомянул ее истоки. Автор: Ноэль Вайс, сайт: www.noelvaes.eu)