[Primavera] Problema de dependencia circular de Bean

Basado en el último tutorial de Spring Framework de [Power Node], el primer tutorial de Spring 6 en toda la red, aprenda Spring desde cero hasta avanzado con Lao Du y las notas originales de Lao Du https://www.yuque.com/docs/share /866abad4-7106 -45e7-afcd-245a733b073f?# "Spring6" está organizado, contraseña del documento: mg9b


Los artículos relacionados con la primavera se compilan y resumen en: https://www.yuque.com/u27599042/zuisie


¿Qué es la dependencia circular de Bean?

  • La dependencia circular de Bean significa que el objeto A tiene el atributo B y el objeto B tiene el atributo A. Es decir, yo dependo de ti y tú dependes de mí.
  • Por ejemplo: marido es Marido, esposa es Esposa, Marido tiene una referencia a Esposa y Esposa tiene una referencia a Marido.
package cw.spring.bean;

/**
 * ClassName: Wife
 * Package: cw.spring.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-12 16:35
 * @Version 1.0
 */
public class Wife {
    
    
    private String name;
    private Husband husband;
    
    public void setName(String name) {
    
    
        this.name = name;
    }
    
    public String getName() {
    
    
        return name;
    }
    
    public void setHusband(Husband husband) {
    
    
        this.husband = husband;
    }
    
    @Override
    public String toString() {
    
    
        return "Wife{" + "name='" + name + '\'' + ", husband=" + husband.getName() + '}';
    }
}
package cw.spring.bean;

/**
 * ClassName: Husband
 * Package: cw.spring.bean
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-12 16:36
 * @Version 1.0
 */
public class Husband {
    
    
    private String name;
    private Wife wife;
    
    public void setName(String name) {
    
    
        this.name = name;
    }
    
    public String getName() {
    
    
        return name;
    }
    
    public void setWife(Wife wife) {
    
    
        this.wife = wife;
    }
    
    @Override
    public String toString() {
    
    
        return "Husband{" + "name='" + name + '\'' + ", wife=" + wife.getName() + '}';
    }
}

Patrón singleton + inyección establecida

Modo singleton + dependencia circular en caso de inyección establecida

  • En el caso del modo singleton + inyección de conjuntos, no hay ningún problema con las dependencias circulares y Spring puede resolver este problema.
<bean id="husband" class="cw.spring.bean.Husband" scope="singleton">
  <property name="name" value="张三"/>
  <property name="wife" ref="wife"/>
</bean>
<bean id="wife" class="cw.spring.bean.Wife" scope="singleton">
  <property name="name" value="李四"/>
  <property name="husband" ref="husband"/>
</bean>
@org.junit.Test
public void test01() {
    
    
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    Husband husband = applicationContext.getBean("husband", Husband.class);
    Wife wife = applicationContext.getBean("wife", Wife.class);
    System.out.println(husband);
    System.out.println(wife);
}

imagen.png

Analizar dependencias circulares en el caso del modo singleton + inyección de conjuntos

  • En el modo set + singleton, la gestión de beans por parte de Spring se divide principalmente en dos etapas:
    • La primera etapa: cuando se carga el contenedor Spring, se crean instancias de beans. Tan pronto como se crea una instancia de cualquiera de los beans, se "expone" inmediatamente y se expone sin esperar la asignación de atributos.
      • La exposición temprana se puede utilizar sin esperar la asignación de atributos, porque solo se creará un objeto de este tipo en una sola instancia, y la exposición temprana es lo mismo que la exposición tardía, ambas son objetos.
    • La segunda etapa: después de "exponer" el Bean, se asignan los atributos y se llama al método set.
  • La solución principal para resolver la dependencia circular en el caso del modo singleton + inyección de conjuntos es: la creación de instancias del objeto y la asignación de propiedades del objeto se completan en dos etapas.
  • Nota: Sólo cuando el alcance es un singleton, el Bean tomará medidas de "exposición" tempranas. No habrá exposición temprana bajo el prototipo. Si se exponen varios objetos por adelantado en el modo de instancias múltiples, Spring no sabrá qué objeto usar cuando use el objeto.

Modo de instancia múltiple + inyección de conjuntos

<bean id="husband" class="cw.spring.bean.Husband" scope="prototype">
  <property name="name" value="张三"/>
  <property name="wife" ref="wife"/>
</bean>
<bean id="wife" class="cw.spring.bean.Wife" scope="prototype">
  <property name="name" value="李四"/>
  <property name="husband" ref="husband"/>
</bean>

Insertar descripción de la imagen aquí

  • Cuando el alcance de los dos beans en la dependencia circular es prototipo, hay un problema con la dependencia circular en el modo prototipo + setter y se producirá la excepción BeanCurrentlyInCreationException (se está creando el Bean actual).
  • Debido a que todos son modelos de instancias múltiples, un nuevo objeto será nuevo cada vez que se necesite un objeto. Si se necesita un objeto B en el objeto A, un nuevo objeto B será nuevo. Si se necesita un objeto A en el objeto B objeto, un nuevo objeto A será nuevo. , el nuevo objeto A necesita el objeto B, y se creará un nuevo objeto B... Al final, el objeto nunca se creará por completo.
  • Si alguno de ellos es un singleton, no se producirá ninguna excepción (porque el bean en modo singleton quedará "expuesto" después de que se complete la creación de instancias, y la dependencia del bucle infinito se puede detener después de la "exposición")
  • El objeto singleton se crea al analizar el archivo de configuración. Cuando se necesita un prototipo, se puede crear un objeto prototipo inmediatamente y asignarlo al singleton. Solo hay un singleton requerido por el objeto prototipo, que puede detener la dependencia, por lo que hay no hay problema El objeto prototipo y el tiempo de creación de objetos singleton son similares a la inversa.

Patrón singleton + inyección de constructor

  • La dependencia circular generada por el modo singleton + inyección de constructor no se puede resolver.
package cw.spring.pojo;

/**
 * ClassName: Husband
 * Package: cw.spring.pojo
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-12 17:16
 * @Version 1.0
 */
public class Husband {
    
    
    private String name;
    private Wife wife;
    
    public Husband(String name, Wife wife) {
    
    
        this.name = name;
        this.wife = wife;
    }
    
    public String getName() {
    
    
        return name;
    }
    
    @Override
    public String toString() {
    
    
        return "Husband{" + "name='" + name + '\'' + ", wife=" + wife.getName() + '}';
    }
}
package cw.spring.pojo;

import cw.spring.bean.Husband;

/**
 * ClassName: Wife
 * Package: cw.spring.pojo
 * Description:
 *
 * @Author tcw
 * @Create 2023-05-12 17:16
 * @Version 1.0
 */
public class Wife {
    
    
    private String name;
    private Husband husband;
    
    public Wife(String name, Husband husband) {
    
    
        this.name = name;
        this.husband = husband;
    }
    
    public String getName() {
    
    
        return name;
    }
    
    @Override
    public String toString() {
    
    
        return "Wife{" + "name='" + name + '\'' + ", husband=" + husband.getName() + '}';
    }
}
<!-- 默认情况下,Spring创建对象都是单例模式的 -->
<bean id="wife" class="cw.spring.pojo.Wife">
  <constructor-arg name="name" value="张三"/>
  <constructor-arg name="husband" ref="husband"/>
</bean>
<bean id="husband" class="cw.spring.pojo.Husband">
  <constructor-arg name="name" value="李四"/>
  <constructor-arg name="wife" ref="wife"/>
</bean>
@org.junit.Test
public void test01() {
    
    
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    Husband husband = applicationContext.getBean("husband", Husband.class);
    Wife wife = applicationContext.getBean("wife", Wife.class);
    System.out.println(husband);
    System.out.println(wife);
}

imagen.png

  • El bean solicitado se encuentra actualmente en creación: ¿Existe una referencia circular que no se puede resolver?
  • El bean solicitado se está creando actualmente: ¿hay alguna referencia circular sin resolver?
  • Las dependencias circulares generadas en función de la inyección del constructor no se pueden resolver. Al utilizar la inyección del constructor, el objeto no se puede crear y exponer inmediatamente, porque la creación de un objeto requiere otro objeto como atributo y la creación del objeto no se puede completar (similar a un punto muerto )
  • Al crear un objeto A, se necesita el objeto B y el objeto B se creará inmediatamente. Sin embargo, el objeto A es necesario para crear el objeto B. Sin embargo, el objeto A todavía se está creando en este momento, lo que hace que el objeto B no pueda ser Al final, los dos objetos no se pueden crear, lo que provoca que fallen la creación de objetos y la inyección de dependencia.
  • La dependencia circular generada en función del método de inyección de construcción no se puede resolver, porque la inyección del método de construcción hará que el proceso de instanciación del objeto y el proceso de asignación de propiedades del objeto no estén separados y deben completarse juntos.

Mecanismo de Spring para resolver dependencias circulares (análisis de código fuente)

  • Spring puede resolver dependencias circulares en modo set + singleton:
    • La razón fundamental es que este método puede separar las dos acciones de "crear instancias de Bean" y "asignar valores a las propiedades de Bean".
    • Al crear una instancia de un Bean: llame al constructor sin parámetros para completar. En este momento, no puede asignar un valor a la propiedad y puede "exponer" el objeto Bean al mundo exterior por adelantado.
    • Al asignar valores a las propiedades de Bean: llame al método setter para completar.
    • Los dos pasos se pueden completar completamente por separado y no es necesario completarlos al mismo tiempo. En otras palabras, los Beans son todos singleton. Primero podemos crear instancias de todos los beans singleton y ponerlos en una colección (podemos llamarlo caché). Después de crear instancias de todos los beans singleton, luego llamamos lentamente al método setter para asignar valores a las propiedades. Esto resuelve el problema de las dependencias circulares.

Seguimiento del código fuente

  • Una vez iniciado el contenedor Spring, se ejecutará el método doCreateBean en AbstractAutowireCapableBeanFactory para crear el Bean.
  • imagen.png
  • imagen.png
  • El Bean se almacena en caché aquí, y el objeto de fábrica que crea el Bean se almacena en caché, exponiendo así el Bean para que los objetos que necesitan usarlo puedan obtenerlo.
  • addSingletonFactory es un método en la clase DefaultSingletonBeanRegistry (registro de Bean singleton predeterminado)
    • Hay tres colecciones de mapas en la clase DefaultSingletonBeanRegistry, que son el caché de tercer nivel de beans de caché.
      • private final Map<String, Object> singletonObjects: Caché de nivel 1
      • private final Map<String, Object> earlySingletonObjects: Caché de nivel 2
      • private final Map<String, ObjectFactory<?>> singletonFactories: Caché de nivel 3
    • Las claves de la colección Map almacenan el nombre del bean (bean id):
      • El caché de primer nivel almacena: objetos Bean singleton. Un objeto Bean singleton completo, lo que significa que a las propiedades del objeto Bean en este caché se les han asignado valores. Es un objeto Bean completo.
      • El caché de segundo nivel almacena: primeros objetos Bean singleton. A las propiedades del objeto Bean singleton en esta caché no se les asignan valores. Sólo un objeto de instancia temprana.
      • El caché de tercer nivel almacena: objetos de fábrica singleton. Aquí se almacena una gran cantidad de "objetos de fábrica", y cada objeto Bean singleton corresponde a un objeto de fábrica singleton. En esta colección se almacena el objeto de fábrica singleton correspondiente al momento en que se creó el objeto singleton.
  • imagen.png
  • Hay un método getSingleton en la clase DefaultSingletonBeanRegistry, que se utiliza para obtener objetos Bean singleton.
  • imagen.png
  • Como se puede ver en el código fuente, Spring primero obtendrá el Bean del caché de primer nivel. Si no se puede obtener, obtendrá el Bean del caché de segundo nivel. Si el caché de segundo nivel aún no puede obtenerlo , Obtendrá la exposición anterior del caché de tercer nivel.El objeto ObjectFactory obtiene la instancia de Bean a través del objeto ObjectFactory, resolviendo así el problema de la dependencia circular.
  • Después de almacenar en caché el objeto Bean, el objeto Bean se asignará en el método doCreateBean en AbstractAutowireCapableBeanFactory
    • Los procesos de creación y asignación de objetos de instancia de Bean singleton se separan y el Bean se expone de antemano para resolver el problema de las dependencias circulares.
  • imagen.png
  • Spring solo puede resolver dependencias circulares entre beans singleton inyectados mediante métodos setter. ClassA depende de ClassB y ClassB depende de ClassA, formando un circuito cerrado de dependencia. Después de que Spring crea el objeto ClassA, lo expone directamente al caché del bean sin esperar a asignar valores a las propiedades. Al analizar las propiedades de ClassA, se encuentra que depende de ClassB, por lo que vamos a obtener ClassB nuevamente. Al analizar las propiedades de ClassB, se encuentra que las propiedades de ClassA son necesarias, pero en este momento ClassA ha estado expuesta. de antemano y se agrega al caché del bean que se está creando. No es necesario crear una nueva instancia de ClassA, simplemente consígala directamente del caché. Esto resuelve el problema de la dependencia circular.

Supongo que te gusta

Origin blog.csdn.net/m0_53022813/article/details/132058646
Recomendado
Clasificación