Как говорить о циклических зависимостях Spring посредством отладки кода?

Меня много-много раз спрашивали, почему циклические зависимости Spring используют кеширование третьего уровня для решения этого вопроса на собеседовании, и это сложно объяснить ясно. Я думаю, что после прочтения исходного кода Spring мне все равно нужно записывать идеи дизайна Spring для решения циклических зависимостей и делиться ими с энтузиастами технологий.

 После того, как меня впервые спросили на интервью, я прочитал много-много статей и получил некоторое вдохновение, но немногие из них были основаны на реальных тематических исследованиях, из-за чего мне было легко забыть, и мне пришлось четко объяснять правду. интервьюеру во время собеседования. Это сложно. Позже я решил досконально разобраться в циклических зависимостях на реальных примерах. Сегодня я буду использовать реальные примеры, чтобы узнать, как Spring обрабатывает циклические зависимости во время отладки.

иллюстрировать

Студенты, читающие эту статью, должны иметь представление о процессах Spring ioc и di, а также о создании компонентов Spring и заполнении атрибутов компонентов.

Вспомните получение bean-компонентов из контейнера Spring.

Во-первых, мы можем найти метод получения bean-компонентов Spring, который будет получен из трех кешей.

    //一级缓存,存储可以直接使用的bean
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    //二级缓存 存储不完整对象 不能直接用的 循环依赖时,某些依赖的属性没有设置,这个时候bean就是不完整对象
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    //三级缓冲 存储bean工厂 可以延迟调用实例化 使用的时候才调用工厂方法获取对象
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    //从Spring容器获取bean对象方法
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //一级缓存
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
            //二级缓存
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                    //三级缓存
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }
复制代码

Зачем здесь нужны 3 тайника? Почему не 2? Ни одного? Давайте начнем разбираться, почему?

Моделирование сценариев циклической зависимости

Классы A и B имеют атрибуты класса друг друга.

@Service
public class A {
    
    private B b;
}
复制代码
@Service
public class B {

    private A a;
}
复制代码

Имитация ручного внедрения циклических зависимостей друг в друга.

Объект a зависит от объекта b, а объект b зависит от объекта a. Процесс внедрения атрибута заключается в том, чтобы сначала создать экземпляр B, внедрить A, а затем заполнить A.

A a = new A();

B b = new B();

b.setA(a); //B里面注入不完整的A了

a.setB(b); //注入依赖的对象,A里面有B了,a注入完成变成完整的a

复制代码

Отладка потока обработки внедрения циклических зависимостей Spring

Тот же сценарий, посмотрите, как Spring с этим справится.

Первым шагом является создание объекта a класса A.

 В Spring после создания экземпляра класса A вместо экземпляра A кэшируется фабрика. Почему?

На втором этапе начинается внедрение атрибута b объекта a.

 Когда a внедряет b, кеш регистрирует фабричный класс вместо экземпляра b.

На третьем этапе объект b снова начинает вводить атрибут объекта a.

 Объект b снова начинает внедрять объект a. На данный момент у a есть только соответствующая фабрика.

Прокси-объект a возвращается через фабрику A, и этот прокси-объект помещается в ранний открытый кеш EarlySingletonObjects (кэш второго уровня). продукт, поэтому a по-прежнему помещается в промежуточный кеш. 

Шаг 4. Внедрите объект b в объект

 В это время инъекция Б завершена, но инъекция А еще не завершена. B внутри A внутри B не присвоено значение

Пятый шаг — внедрить полный объект B (на самом деле еще не завершенный).

Шаг 6. Добавьте атрибут b объекта a.

 Наконец, добавьте атрибут B объекта A. На данный момент объект A завершен, поэтому объект B также завершен (чтобы компенсировать предыдущий шаг).

Завершить граф объекта

Полный граф объекта b

Подводя итог, зачем использовать кеш уровня 3?

Когда в следующий раз на собеседовании меня спросят о циклических зависимостях, как я объясню это интервьюеру?

1. Spring использует три карты кэша для решения проблемы циклических зависимостей, а именно: SingletonObjects, SingletonFactories и EarlySingletonObjects.

2. Кэш SingletonObjects хранит полные объекты и может использоваться напрямую.

3. Кэш singletonFactories предназначен для отложенной инициализации и является ключом к решению циклических зависимостей. При внедрении циклических зависимостей может потребоваться проксирование внедренных объектов. Этот фабричный класс используется для создания экземпляра прокси-класса.

4. Кэш EarlySingletonObjects используется для внедрения b в a, а b — в a. A — это полуфабрикат, и его необходимо использовать для хранения состояния полуфабрикатов bean-компонентов, таких как a.

Вам нужно подождать, пока a внедрит b, и тогда кеш будет удален.

5. SingletonFactories и EarlySingletonObjects являются промежуточными состояниями хранимых объектов. Они используются для обеспечения завершения окончательного внедренного объекта и его очистки после завершения внедрения зависимостей.

Supongo que te gusta

Origin blog.csdn.net/2301_76607156/article/details/130557749
Recomendado
Clasificación