предисловие
Нам часто задают вопрос: как Spring решает проблему циклических зависимостей. Этот вопрос является высокочастотным вопросом интервью о Spring, потому что, если вы не изучаете его намеренно, я полагаю, что даже если вы прочитали исходный код, интервьюер может не сразу подумать о загадке.
Эта статья в основном посвящена этой проблеме и объясняет принцип ее реализации с точки зрения исходного кода.
1. Что такое круговая зависимость
Несколько компонентов зависят друг от друга, образуя замкнутый цикл. Например: A зависит от B, B зависит от c, а c зависит от A.
Вообще говоря, если вы спрашиваете, как решать циклические зависимости внутри контейнера Spring, это должно относиться к сценарию, в котором свойства ссылаются друг на друга в одноэлементном bean-компоненте по умолчанию. Другими словами, циклическая зависимость Spring — это проблема, возникающая при внедрении контейнера Spring.
2. Как Spring решает циклические зависимости
1. Кэш третьего уровня singleton bean-компонентов в Spring
Кэш первого уровня (также называемый пулом синглетонов) singletonObjects: хранит объекты Bean, прошедшие полный жизненный цикл.
Кэш второго уровня: EarlySingletonObjects, в котором хранятся объекты Bean, выставленные ранее, жизненный цикл Bean не закончен (свойства еще не заполнены)
Кэш третьего уровня: Map
2. Жизненный цикл фасоли весной
3. Основной метод инициализации Бина
getSingleton: надеюсь получить singleton bean-компонент из контейнера, если нет
doCreateBean: создать бин без него
populateBean: после завершения создания необходимо заполнить свойства
addSingleton: после заполнения добавьте его в контейнер для использования
4. Специальные инструкции
A нуждается в B в процессе создания, поэтому A помещает себя в кеш L3 для создания экземпляра B.
Когда создается экземпляр B, он обнаруживает, что A необходим, поэтому B сначала проверяет кэш первого уровня, нет, затем проверяет кэш второго уровня, или нет, затем проверяет кэш третьего уровня, находит A, а затем помещает A в кеше третьего уровня в кеш кеша второго уровня и удалить A в кеше третьего уровня
После того, как B успешно инициализирован, он помещает себя в кеш первого уровня (в это время A в B все еще находится в состоянии создания), а затем возвращается и создает A. В это время B был создан, получить B непосредственно из кэша первого уровня, а затем завершить создание и поместить A в кэш первого уровня.
5. Диаграмма
3. Зачем использовать кеш L3
1. Используйте кеш первого уровня
Создать экземпляр A-> поместить полуфабрикат A в singletonObjects-> обнаружить, что B нельзя получить при заполнении свойств A-> создать экземпляр B-> вынуть A из singletonObjects и заполнить свойства B-> положить готовый продукт B в singletonObjects-> заполнить B в свойствах A -> поместить готовый A в singletonObjects.
Проблема: Этот базовый процесс распространен, но если другой поток хочет получить A в течение всего процесса, он может получить только полуфабрикат A с нулевыми атрибутами, что вызовет проблемы.
2. Используйте кеш второго уровня
а) использовать singletonObjects и EarlySingletonObjects
Готовый продукт помещается в singletonObjects, а полуфабрикат — в EarlySingletonObjects.
Процесс может идти так: создать экземпляр A -> поместить полуфабрикат A в EarlySingletonObjects -> обнаружить, что B нельзя получить при заполнении свойств A -> создать экземпляр B -> положить полуфабрикат A в EarlySingletonObjects — > из EarlySingletonObjects Выньте A и заполните свойства B -> поместите готовый продукт B в singletonObjects и удалите B из EarlySingletonObjects -> заполните B в свойствах A -> поместите готовый продукт A в singletonObjects и удалите EarlySingletonObjects.
Проблема: Такой процесс является потокобезопасным, но если к А добавляется аспект (АОП), этот подход не будет соответствовать потребностям, потому что все примитивные объекты хранятся в EarlySingletonObjects, и то, что нам нужно внедрить, на самом деле является прокси-объектом А .
б) с использованием singletonObjects и singletonFactory
Готовый продукт помещается в singletonObjects, а полуфабрикат получается через singletonFactory
Процесс выглядит следующим образом: создайте экземпляр A -> создайте фабрику объектов A и поместите ее в singletonFactories -> обнаружите, что B нельзя получить при заполнении свойств A -> создайте экземпляр B -> создайте фабрику объектов B и поместите ее в singletonFactories -> Получить фабрику объектов A из singletonFactories и заставить A заполнить B -> поместить готовый продукт B в singletonObjects и удалить фабрику объектов B из singletonFactories -> заполнить B в свойствах A -> поместить готовый продукт A в singletonObjects и удалить фабрику объектов A.
Проблема: Этот процесс подходит и для обычного IOC, но если к А добавляется аспект (АОП), эта ситуация не может удовлетворить требованиям, потому что прокси-объект, полученный через singletonFactories.getObject(), каждый раз разный.
В конце концов
В последнее время многие люди берут интервью. Я собрал один здесь: многопоточные материалы Java, семейные ведра серии Spring (1187 страниц документов), систематические материалы Java: (включая последние основные знания Java в 2021 году, темы интервью и 20 Internet Zhenti , электронные книги и т. д.), нуждающиеся друзья могут подписаться на официальный аккаунт [Программа Юань Сяован], чтобы получить его.