dependencia circular
La dependencia circular es uno de los problemas comunes en el marco Spring. Cuando dos o más clases se refieren entre sí, se produce una dependencia circular. En este caso, el marco Spring no puede determinar qué clase se debe crear una instancia e inicializar primero, lo que genera una excepción. Las soluciones comunes incluyen: inyección de constructor, inyección de método de establecimiento, inyección de método de fábrica estático y uso de bibliotecas de terceros .
Versión utilizada esta vez:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.0</version>
<relativePath/>
</parent>
Caso
public interface ServiceA {
}
public interface ServiceB {
}
@Service
public class ServiceAImpl implements ServiceA {
private ServiceB serviceB;
public ServiceAImpl(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Service
public class ServiceBImpl implements ServiceB {
private ServiceA serviceA;
public ServiceBImpl(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
Solución
1. Rediseño
Cuando hay una dependencia circular es probable que haya un problema de diseño y las responsabilidades no estén bien separadas. Los componentes deben rediseñarse adecuadamente tanto como sea posible para que su jerarquía esté bien diseñada y no sean necesarias dependencias circulares.
Si un componente no se puede rediseñar (puede haber muchas razones: código heredado, código que ha sido probado y no se puede modificar, no hay suficiente tiempo o recursos para rediseñarlo por completo...), pero existen algunas soluciones para resolver el problema.
2.@Perezoso
Una solución simple a las dependencias circulares de Spring es utilizar la carga diferida de un bean . En otras palabras: este Bean no se ha inicializado por completo, de hecho, se inyecta en un proxy, que solo se inicializará por completo cuando se utilice por primera vez.
@Service
public class ServiceAImpl implements ServiceA {
private ServiceB serviceB;
public ServiceAImpl(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
3.Setter/Inyección de campo
En pocas palabras, utiliza la inyección de setter (o inyección de campo) para los beans que necesita inyectar, no la inyección de constructor. Al crear un Bean de esta manera, de hecho, sus dependencias no se inyectan en este momento y se inyectarán solo cuando lo necesite.
@Service
public class ServiceAImpl implements ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
SpringBoot 2.6.x no recomienda el uso de dependencias circulares. La forma más sencilla es permitir referencias circulares en el archivo de configuración global. El valor predeterminado de esta propiedad es falso y la declaración de visualización es verdadera, lo que puede evitar excepciones de referencia circular de la consola. cuando comienza el proyecto.
spring:
main:
allow-circular-references: true
4.@PostConstrucción
Otra forma de romper el ciclo es usarlo en la propiedad que desea inyectar (que es un bean) @Autowired
y usar @PostConstruct
la anotación en otro método y establecer la dependencia de otros métodos en ese método.
@Service
public class ServiceAImpl implements ServiceA {
@Autowired
private ServiceB serviceB;
@PostConstruct
public void init() {
System.out.println(serviceB);
serviceB.setServiceA(this);
}
}
@Service
public class ServiceBImpl implements ServiceB {
private ServiceA serviceA;
public void setServiceA(ServiceA serviceA) {
System.out.println(serviceA);
this.serviceA = serviceA;
}
}
Resumir:
Forma | Dependencias | Método de inyección | Capacidad para resolver dependencias circulares. |
---|---|---|---|
Situación uno | interdependencia AB | Todos usan el método setter | capaz |
Situación 2 | interdependencia AB | Ambos usan el método constructor. | no puedo |
Situación tres | interdependencia AB | Inyectar B en A usa un setter, e inyectar A en B usa un constructor | capaz |
Situación cuatro | interdependencia AB | Inyectar B en A usa un constructor, e inyectar A en B usa un setter | no puedo |
Situación cinco | interdependencia AB | Inyecte B en A y úselo @Autowired , inyecte A en B y use @PostConstruct + setter |
capaz |
Situación seis | interdependencia AB | Inyectar B en A usa @PostConstruct + setter, inyectar A en B usa@Autowired |
capaz |