digital-garden/dev/architecture/Циклические зависимости сервисов.md

133 lines
3.8 KiB
Markdown
Raw Normal View History

---
aliases:
tags:
- maturity/🌱
date:
- - 2023-11-20
zero-link:
- "[[../../meta/zero/00 Архитектура ПО|00 Архитектура ПО]]"
parents:
linked:
---
Циклические зависимости между сервисами возникают, когда сервисы взаимно внедряются друг в друга. Например, сервис А внедряет сервис Б, но в тоже время сервис Б внедряет сервис А. В этом случае [SpringBoot](../../meta/zero/00%20SpringBoot.md) и [Quarkus](../../meta/zero/00%20Quarkus.md) не знают, как создать такие бины и внедрить их друг в друга.
> [!WARNING]
> Обычно такая ситуация сигнализирует о плохо продуманной архитектуре приложения. 
Пример циклической зависимости в Spring:
```java
@Service
public class ServiceOne {
private final ServiceTwo serviceTwo;
public ServiceOne(ServiceTwo serviceTwo) {
this.serviceTwo = serviceTwo;
}
}
```
```java
@Service
public class ServiceTwo {
private final ServiceOne serviceOne;
public ServiceTwo(ServiceOne serviceOne) {
this.serviceOne = serviceOne;
}
}
```
Вот что вы можете с этим сделать:
## Пересмотреть архитектуру приложения
==Это предпочтительный вариант.== Возможно вам стоит создать сервис В, который внедрит в себя сервисы А и Б. В таком случае вы распутаете циклическую зависимость.
```java
@Service
public class ServiceThree {
private final ServiceOne serviceOne;
private final ServiceTwo serviceTwo;
public ServiceThree(ServiceOne serviceOne, ServiceTwo serviceTwo) {
this.serviceOne = serviceOne;
this.serviceTwo = serviceTwo;
}
}
```
```java
@Service
public class ServiceOne {
}
```
```java
@Service
public class ServiceTwo {
}
```
## Ленивое внедрение
В Spring вы можете указать аннотацию `@Lazy` у аргумента конструктора одного их сервисов. Таким образом сначала будет создан один сервис, для второго сервиса спринг создаст прокси класс, создаст из него бин и внедрит его в ваш сервис. После чего создаст второй сервис и внедрит туда уже созданный первый. А далее заменит ссылку с прокси объекта на второй сервис.
```java
@Service
public class ServiceTwo {
private final ServiceOne serviceOne;
public ServiceTwo(@Lazy ServiceOne serviceOne) {
this.serviceOne = serviceOne;
}
}
```
```java
@Service
public class ServiceOne {
private final ServiceTwo serviceTwo;
public ServiceOne(ServiceTwo serviceTwo) {
this.serviceTwo = serviceTwo;
}
}
```
## Внедрение через Setter
Внедрить один из сервисов через сеттер, вместо конструктора. Таким образом, фреймворк сможет создать оба бина, а потом уже внедрит один в другой.
```java
@Service
public class ServiceOne {
private final ServiceTwo serviceTwo;
public ServiceOne(ServiceTwo serviceTwo) {
this.serviceTwo = serviceTwo;
}
}
```
```java
@Service
public class ServiceTwo {
private ServiceOne serviceOne;
@Autowired
public void setServiceOne(ServiceOne serviceOne) {
this.serviceOne = serviceOne;
}
}
```