Как выполнить самостоятельное внедрение Spring Boot 3+, чтобы пройти через прокси и применить транзакционное поведение

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

@RequiredArgsConstructor
public class Service {
  @Lazy private final Service self;

  public void foo() {
    self.bar();
  }

  @Transactional
  public void bar() {
    // do some transactional stuff
  }
}

Я хочу сделать это также, чтобы соответствовать java:S6809

A method annotated with Spring’s @Async or @Transactional annotations will not work as expected if invoked directly from within its class.
This is because Spring generates a proxy class with wrapper code to manage the method’s asynchronicity (@Async) or to handle the transaction (@Transactional). However, when called using this, the proxy instance is bypassed, and the method is invoked directly without the required wrapper code.

Проблема в том, что я не могу этого сделать, иначе при запуске получаю ошибку.

The dependencies of some of the beans in the application context form a cycle:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
  • Я использовал @Lazy
  • Я использовал lombok.copyableAnnotations += org.springframework.context.annotation.Lazy в lombok.config

Является ли установка spring.main.allow-circular-references значением true единственным решением для самостоятельного внедрения весной?

(некоторые ссылки: https://medium.com/javarevisited/spring-transactional-mistakes-everyone-did-31418e5a6d6b)

Это будет работать только с внедрением поля, а не с внедрением конструктора. Удалите final и добавьте @Autowired рядом с @Lazy.

M. Deinum 05.03.2024 15:42

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

dorian.naa 05.03.2024 15:47

Исправлять это не рекомендуется, но то же самое можно сказать и о самостоятельном внедрении, поскольку это скорее обходной путь, чем реальное решение. Реальное решение — установить правильные границы транзакций. Другой вариант — внедрить ObjectProvider<Service>, а затем выполнять поиск всякий раз, когда вам нужно выполнить самовызов (еще один обходной путь, но не требующий внедрения полей или разрешения циклических ссылок).

M. Deinum 05.03.2024 15:50

Я согласен. В итоге мне пришлось переработать способ организации моего бизнес-кода, чтобы вызывать транзакционные методы из другого сервиса. Сделав это таким образом, я убедился, что за обработку транзакции отвечает вызываемый сервис. Другой метод службы не был транзакционным и поэтому делегировал все, что касается транзакций, вызываемому. Об этой штуке ObjectProvider определенно стоит позаботиться. Спасибо за ваш вклад.

dorian.naa 05.03.2024 17:34
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
4
533
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете попробовать TransactionTemplate вместо @Transactional

@RequiredArgsConstructor
class MyService {
    @Lazy
    private final MyService self;
    private final TransactionTemplate transactionTemplate;


    public void foo() {
        self.bar();
    }

    public void bar() {
        transactionTemplate.executeWithoutResult(ts -> {
            // do tx stuff
        });
    }
}

видеть https://docs.spring.io/spring-framework/reference/data-access/transaction/programmatic.html

Другие вопросы по теме