Приступаем к отладке транзакций Spring в рабочей среде

Приступаем к отладке транзакций Spring в рабочей среде

27 апреля 2022 г.

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


Это частично верно. Мы не можем поставить точку останова на транзакционной аннотации. Но я забегаю вперед.


Что такое метод декларативного управления транзакциями Spring?


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


  • Уровни изоляции транзакций — позволяют нам решать такие проблемы, как грязное чтение, неповторяемое чтение, фантомное чтение и т. д.

  • Менеджер транзакций

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

  • Атрибут readOnly — БД не всегда поддерживает транзакцию только для чтения. Но когда он поддерживается, это отличная функция настройки производительности/надежности.

И многое другое.


Не связана ли транзакция с драйвером базы данных?


Концепция транзакционных методов очень сбивает с толку новых разработчиков Spring. Транзакции — это функция драйвера базы данных/соединения JDBC, а не метода. Зачем объявлять это в методе?


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


Как мы можем написать программное управление транзакциями, если мы не используем API базы данных?


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


Вы можете внедрить этот менеджер в типичный класс Spring, но гораздо проще просто написать декларативное управление транзакциями, как этот код Java:


```java


@транзакционный


общественный недействительным myMethod () {


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


Это обеспечивает исключительную гибкость и отлично подходит для отделения бизнес-кода от деталей низкоуровневых транзакций JDBC.


Динамический прокси, аспектно-ориентированное программирование и аннотации


Ключом к отладке транзакций является то, как Spring реализует эту логику. Spring использует механизм прокси для реализации декларативных возможностей аспектно-ориентированного программирования. По сути, это означает, что когда вы вызываете myMethod для MyObject или MyClass, Spring создает между ними прокси-класс и экземпляр прокси-объекта.


Spring направляет ваш вызов через типы прокси, которые реализуют все декларативные аннотации. Таким образом, транзакционный прокси заботится о проверке статуса транзакции и его обеспечении.


Отладка управления транзакциями Spring с помощью Lightrun


ВАЖНО: я предполагаю, что вы знакомы с основами Lightrun. Если нет, прочтите это.


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


Но если аннотация не сработает, метод не будет вызван, и мы не получим обратный вызов.


Однако аннотации — это не волшебство. Spring использует прокси-объект, как мы обсуждали выше. Этот прокси-механизм вызывает общий код, который мы можем использовать для привязки моментального снимка. Как только мы привяжем туда снимок, мы сможем определить типы прокси в стеке. К сожалению, отладка механизмов проксирования проблематична, поскольку нет физического кода для отладки. Все в механизмах проксирования генерируется динамически во время выполнения. К счастью, это не имеет большого значения. У нас достаточно хуков для отладки и без этого.


Поиск фактического класса транзакции


Первое, что нам нужно сделать, это найти класс, реализующий функциональность транзакций. Открытие представления класса IntelliJ/IDEA (Command-O или CTRL-O) позволяет нам найти класс по имени. Ввод «Транзакции» привел к следующему виду:


image1.png


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


В данном случае интересным является класс TransactionAspectSupport. Как только мы откроем класс, нам нужно выбрать вариант загрузки исходного кода класса.


Как только это будет сделано, мы можем искать применимый публичный метод. getTransactionManager казался идеальным, но он слишком голый. Размещение снимка дало мне подсказку:


image2.png


У меня не так много информации, но метод invokeWithinTransaction вверх по стеку идеален!


Переходя к этому методу, я хотел бы отслеживать информацию, относящуюся к транзакции, в методе findById:


image3.png


Чтобы ограничить область действия только findById, мы добавляем условие:


```java


метод.getName().equals("findById")


Как только метод сработал, мы можем увидеть детали транзакции в стеке.


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


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


TL;DR


Декларативная конфигурация в Spring значительно упрощает транзакционные операции. Это значительно упрощает разработку приложений и отделяет логику объекта от низкоуровневых деталей поведения транзакций.


Spring использует прокси-серверы на основе классов для реализации аннотаций. Поскольку они генерируются, мы не можем отлаживать их напрямую, но мы можем отлаживать классы, которые они используют внутри. В частности: TransactionAspectSupport — отличный пример.


Огромным преимуществом Lightrun является то, что он не приостанавливает текущий поток. Это означает, что проблемы, связанные с параллелизмом, могут быть воспроизведены в облегченном режиме. Все, что здесь обсуждается, можно выполнить с помощью бесплатной версии Lightrun.


Также опубликовано здесь



Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE