Я хотел бы создать ссылку на себя в своем сервисе, чтобы использовать ее для прохождения через прокси-сервер 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.
@Lazylombok.copyableAnnotations += org.springframework.context.annotation.Lazy в lombok.configЯвляется ли установка spring.main.allow-circular-references значением true единственным решением для самостоятельного внедрения весной?
(некоторые ссылки: https://medium.com/javarevisited/spring-transactional-mistakes-everyone-did-31418e5a6d6b)
На самом деле я тоже боролся с полевыми инъекциями 😕. Также не рекомендуется внедрение полей, но я думаю, это лучше, чем допускать циклические ссылки в свойствах.
Исправлять это не рекомендуется, но то же самое можно сказать и о самостоятельном внедрении, поскольку это скорее обходной путь, чем реальное решение. Реальное решение — установить правильные границы транзакций. Другой вариант — внедрить ObjectProvider<Service>, а затем выполнять поиск всякий раз, когда вам нужно выполнить самовызов (еще один обходной путь, но не требующий внедрения полей или разрешения циклических ссылок).
Я согласен. В итоге мне пришлось переработать способ организации моего бизнес-кода, чтобы вызывать транзакционные методы из другого сервиса. Сделав это таким образом, я убедился, что за обработку транзакции отвечает вызываемый сервис. Другой метод службы не был транзакционным и поэтому делегировал все, что касается транзакций, вызываемому. Об этой штуке ObjectProvider определенно стоит позаботиться. Спасибо за ваш вклад.




Вы можете попробовать 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
Это будет работать только с внедрением поля, а не с внедрением конструктора. Удалите
finalи добавьте@Autowiredрядом с@Lazy.