Haga un dibujo para resolver la dependencia cíclica de Spring y comprenda completamente la IOC.

Prefacio

 Cómo Spring resuelve la dependencia circular es una pregunta de la entrevista de Java que se ha vuelto popular en los últimos dos años.

De hecho, el propio autor sigue siendo escéptico sobre este tipo de preguntas sobre el código fuente del marco .

Si yo, como el entrevistador puede preguntar, como "Si la propiedad de la inyección es  nula , se puede ir en varias direcciones desde las cuales la investigación" Estos problemas de escenas .

Entonces, ahora que este artículo está escrito, déjeme dejar de hablar, veamos cómo  Spring resuelve las dependencias circulares y le mostraremos la esencia de las dependencias circulares.

texto

En términos generales, si pregunta cómo Spring resuelve internamente las dependencias circulares, debe ser  un escenario en el que las propiedades se refieran entre sí en un solo bean singleton predeterminado .

Por ejemplo, referencias mutuas entre varios Beans:

Incluso "ciclo" y confía en ti mismo:

Primero explique la premisa: el  escenario del prototipo (Prototipo) no admite dependencias circulares. Por lo general, irá al AbstractBeanFactoryjuicio debajo de la clase y arrojará una excepción.

if (isPrototypeCurrentlyInCreation(beanName)) {
  throw new BeanCurrentlyInCreationException(beanName);
}

La razón es fácil de entender. Al crear un nuevo A  , se encuentra que el campo prototipo B debe inyectarse y se  encuentra que un nuevo B se inyecta en el campo prototipo A ...

Esta es la muñeca, ¿ adivinas si es StackOverflow  o  OutOfMemory primero  ?

Spring teme que no puedas adivinar, por lo que lanza BeanCurrentlyInCreationException primero  

La dependencia circular basada en el constructor, sin mencionar que los documentos oficiales son todos enfrentamientos. Si quieres inyectar el constructor para soportar dependencias circulares, no existe, por lo que es mejor cambiar el código.

Entonces, en el escenario de inyección de atributo singleton predeterminado,  ¿cómo admite Spring las dependencias circulares?

Spring  resuelve dependencias circulares

En primer lugar, Spring mantiene tres mapas internamente  , que es lo que solemos llamar caché de tres niveles .

El autor examinó la documentación de Spring pero no encontró el concepto de caché de tres niveles, que también puede ser un vocabulario local para una fácil comprensión.

En la DefaultSingletonBeanRegistryclase de Spring , notará estos tres mapas colgando sobre la clase:

  • singletonObjects es nuestro amigo más familiar, comúnmente conocido como " grupo singleton " y " contenedor ", que almacena en caché el lugar donde se crean los beans singleton.

  • singletonFactories mapea la fábrica original que creó el Bean

  • earlySingletonObjects mapea las primeras referencias del Bean , lo que significa que el Bean en este Mapa no está completo, y ni siquiera se puede llamar un " Bean ", solo una  Instancia .

Los dos últimos mapas se encuentran en el nivel de " trampolín ". Solo se utilizan para ayudar a crear Beans y se borrarán después de la creación.

Así que el autor estaba un poco confundido acerca del término "caché de tres niveles" en el artículo anterior, probablemente porque todos los comentarios comienzan con Cache of.

¿Por qué los dos últimos mapas son trampolines? Suponga que  el Bean que finalmente se coloca en  singletonObjects es la taza de "frío y blanco" que desea.

Luego Spring ha preparado dos tazas, a saber, singletonFactories y earlySingletonObjects, para "verter" hacia adelante y hacia atrás varias veces, para secar el agua caliente en "blanco frío abierto" y ponerla en  singletonObjects  .

Sin chismes, todo está concentrado en la imagen.

Lo anterior es un GIF, si no lo ve, es posible que aún no esté cargado. Un fotograma durante tres segundos, no la tarjeta de su computadora.

El autor hizo 17 dibujos para simplificar los pasos principales de Spring. Encima del GIF está el caché de tres niveles que se acaba de mencionar, y los métodos principales se muestran a continuación .

Por supuesto, en este punto, debe mirarlo junto con el código fuente de Spring, o puede que no lo entienda.

Si solo desea obtener una descripción general o una entrevista, primero puede recordar el " caché de tres niveles " que mencioné anteriormente y la esencia de la que hablaré a continuación.

La naturaleza de la dependencia circular

Después de entender cómo Spring maneja las dependencias circulares arriba, saltemos del pensamiento de " leer el código fuente ". Supongamos que implementas una función con las siguientes características, ¿qué harías?

  • Establecer algunas instancias de clase especificadas como singletons

  • Los campos de la clase también son instancias de singleton.

  • Apoyar dependencias circulares

Por ejemplo, suponga que hay una clase A:

public class A {
    private B b;
}

Clase B:

public class B {
    private A a;
}

Para decirlo sin rodeos, déjate imitar a Spring : finge que A  y  B  están decorados por @Component,
y los campos de la clase fingen estar decorados por @Autowired, y colócalos en el Mapa después de procesarlos.

De hecho, es muy simple, el autor escribió un código aproximado como referencia:

    /**
     * 放置创建好的bean Map
     */
    private static Map<String, Object> cacheMap = new HashMap<>(2);

    public static void main(String[] args) {
        // 假装扫描出来的对象
        Class[] classes = {A.class, B.class};
        // 假装项目初始化实例化所有bean
        for (Class aClass : classes) {
            getBean(aClass);
        }
        // check
        System.out.println(getBean(B.class).getA() == getBean(A.class));
        System.out.println(getBean(A.class).getB() == getBean(B.class));
    }

    @SneakyThrows
    private static <T> T getBean(Class<T> beanClass) {
        // 本文用类名小写 简单代替bean的命名规则
        String beanName = beanClass.getSimpleName().toLowerCase();
        // 如果已经是一个bean,则直接返回
        if (cacheMap.containsKey(beanName)) {
            return (T) cacheMap.get(beanName);
        }
        // 将对象本身实例化
        Object object = beanClass.getDeclaredConstructor().newInstance();
        // 放入缓存
        cacheMap.put(beanName, object);
        // 把所有字段当成需要注入的bean,创建并注入到当前bean中
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            // 获取需要注入字段的class
            Class<?> fieldClass = field.getType();
            String fieldBeanName = fieldClass.getSimpleName().toLowerCase();
            // 如果需要注入的bean,已经在缓存Map中,那么把缓存Map中的值注入到该field即可
            // 如果缓存没有 继续创建
            field.set(object, cacheMap.containsKey(fieldBeanName)
                    ? cacheMap.get(fieldBeanName) : getBean(fieldClass));
        }
        // 属性填充完成,返回
        return (T) object;
    }

El efecto de este código es que la dependencia circular se procesa realmente, y después de que se completa el procesamiento, el " Beancompleto se coloca en el cacheMap.

Esta es la esencia de las " dependencias circulares ", no "cómo Spring resuelve las dependencias circulares".

La razón de este ejemplo es descubrir que un pequeño número de amigos de la marihuana han caído en el " atolladero de leer el código fuente " y han olvidado la naturaleza del problema.

Para mirar el código fuente y mirar el código fuente, el resultado no se ha entendido, pero olvidé cuál es la esencia.

Si realmente no lo entiende, es mejor escribir primero la versión básica y revertir por qué Spring se implementa de esta manera, y el efecto puede ser mejor.

¿Qué? ¡La esencia del problema es en realidad dos sumas!

Después de leer el código hace un momento, ¿hay alguna familiaridad? Así es,   la resolución de problemas es similar a la de dos sumas .

No sé de qué   se trata dos sumas , les presentaré:

Dos suma  es la pregunta con el número de serie 1 del código de acceso en el sitio web, que es la primera pregunta para la introducción del algoritmo de la mayoría de las personas.

Las empresas que a menudo son ridiculizadas por los algoritmos , son designadas por el entrevistador y se unen. Luego ve a un   paseo de dos sumas por la escena.

El contenido de la pregunta es: dado un arreglo , dado un número . La matriz devuelta se puede agregar para obtener dos índices del número especificado .

Por ejemplo: dado, nums = [2, 7, 11, 15], target = 9
luego volver  [0, 1], porque2 + 7 = 9

La mejor solución a este problema es atravesar + HashMap una vez:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int complement = target - nums[i];
            if (map.containsKey(complement)) {
                return new int[] { map.get(complement), i };
            }
            map.put(nums[i], i);
        }
        throw new IllegalArgumentException("No two sum solution");
    }
}
//作者:LeetCode
//链接:https://leetcode-cn.com/problems/two-sum/solution/liang-shu-zhi-he-by-leetcode-2/
//来源:力扣(LeetCode)

Primero vaya al Mapa para encontrar el número que necesita , si no , guarde el número actual en el Mapa, si encuentra el número que necesita , luego regrese juntos.

¿Es el mismo que el código anterior?

Primero vaya a la caché para encontrar el  Bean , si no, cree una instancia del Bean actual y colóquelo en el  Mapa. ​​Si hay una dependencia en el Bean actual, puede obtenerlo del Mapa.

 

Supongo que te gusta

Origin blog.csdn.net/bjmsb/article/details/108550304
Recomendado
Clasificación