Вопрос: Как инициализируется singleton bean в Spring?
Все мы знаем, что Spring анализирует xml-файл, описанный как BeanDefinition, анализирует BeanDefinition и, наконец, создает Bean и помещает Bean в пул singleton, так что же делает Spring в процессе создания Bean.
Самый важный метод в основном методе Spring Refresh() — это метод finishBeanFactoryInitialization() , который отвечает за инициализацию всех одноэлементных компонентов.
Метод FinishBeanFactoryInitialization() расположен на шаге 11 функции refresh().
К этому моменту все BeanFactories в контейнере Spring были созданы, то есть BeanFactoryPostProcessor
были инициализированы bean-компоненты, реализующие интерфейс. Остальное — инициализация singleton beans
. Большинство наших бизнес-бинов — синглетоны. finishBeanFactoryInitialization
Этот шаг — создание экземпляров синглетонов, а не установка лениво загруженных бинов.
Spring будет finishBeanFactoryInitialization
инициализировать все в этом методе singleton bean
.
Хорошо, давайте посмотрим на логику внутри метода FinishBeanFactoryInitialization. Суть этого метода заключается в завершении настройки BeanFactory . На этом этапе завершается создание экземпляра контекста, включая все созданные одноэлементные объекты Bean .
Ядро заключается в методе preInstantiateSingletons() . Основная задача метода preInstantiateSingletons — инициализация. До инициализации это также ряд суждений, например, загружен ли он лениво, является ли он фабричным компонентом (специальным компонентом , ответственный за компонент, созданный фабрикой), и, наконец, вызовите метод getBean().
Интерфейс , упомянутый в комментарии, SmartInitializingSingleton
позволяет компоненту выполнять некоторые операции после инициализации.
Хорошо, тогда основным методом по-прежнему является getBean()
метод, функция метода getBean() заключается в загрузке и создании экземпляра Bean. doGetBean() вызывается внутри метода, давайте заглянем непосредственно внутрь метода **doGetBean()**.
Кодов много, в основном мы рассматриваем метод createBean().
Продолжим заглядывать внутрь метода doCreateBean.
Существует много методов, но в основном нам нужно сосредоточиться на трех методах.
- createBeanInstance: создание экземпляра, по сути, заключается в вызове конструктора объекта для создания экземпляра объекта
- populateBean: заполните атрибуты, этот шаг в основном предназначен для заполнения свойств зависимостей нескольких bean-компонентов.
- initializeBean: вызовите метод инициализации в spring xml.
Из шагов инициализации одноэлементного компонента, описанных выше, мы можем знать, что циклические зависимости в основном возникают на первом и втором шагах. То есть круговые зависимости конструктора и круговые зависимости поля.
Затем мы должны начать с процесса инициализации, чтобы решить циклическую ссылку.Для синглтона существует один и только один объект за весь жизненный цикл контейнера Spring, поэтому легко подумать, что этот объект должен храниться в кеше Чтобы решить проблему одноэлементной циклической зависимости, используйте трехуровневый кеш.
Что такое кеш третьего уровня Spring?
Трехуровневый кеш в контейнере IOC Spring представляет собой структуру карты.
- Кэш 1-го уровня (зрелые бобы)
singletonObjects
Пул singleton хранит полностью инициализированные bean-компоненты, а bean-компоненты, взятые из кеша, можно использовать напрямую
- Кэш L2
earlySingletonObjects
Кэш объекта singleton, выставленного заранее, хранит исходный объект bean (еще не заполненный свойствами), который используется для решения циклической зависимости.
- Кэш L3
singletonFactories
Кэш фабрики одноэлементных объектов хранит объекты фабрики компонентов для разрешения циклических зависимостей.
Хорошо, разобравшись с кешем третьего уровня, давайте посмотрим на метод getSingleton() . Этот метод в основном используется для получения экземпляра singleton Bean с указанным именем из пула singleton.Метод внутренне реализует механизм поиска в трехуровневом кэше и получает объект экземпляра singleton Bean с указанным именем через механизм трехуровневого поиска. В то же время метод Синхронизированные блоки кода используется для обеспечения безопасности потоков в многопоточной среде.
Итак, у нас есть вопрос, почему Spring использует трехуровневый кеш для решения проблемы циклических зависимостей.
Прежде всего, мы должны прояснить, что Spring может разрешить внедрение зависимостей установщика, но не может решить внедрение зависимостей конструктора.
Если теперь у нас есть объект A и объект B , определенное поле или сеттер A зависит от экземпляра объекта B , и в то же время определенное поле или сеттер B зависит от экземпляра объекта A », этот круговой ситуация зависимости.
Первый завершает первый шаг инициализации и заранее предоставляет себя singletonFactories.В это время он выполняет второй шаг инициализации и обнаруживает, что он зависит от объекта B. В этот момент он пытается получить(B) и обнаруживает, что B еще не создан. , поэтому пройдите процесс создания, B обнаруживает, что он полагается на объект A во время первого шага инициализации, поэтому он пытается получить (A), пробует кеш первого уровня singletonObjects (конечно, не , так как A не был полностью инициализирован), и пытается использовать кеш второго уровня EarlySingletonObjects (Нет), попробуйте трехуровневый кеш singletonFactories, поскольку A заранее раскрывает себя через ObjectFactory, поэтому B может получить объект A через ObjectFactory.getObject (хотя A не был полностью инициализирован, это лучше, чем ничего), B принимает После достижения объекта A успешно завершены фазы инициализации 1, 2 и 3. После полной инициализации он поместит себя в первый- кеш уровня singletonObjects.
Вернувшись к A в это время, A может получить объект B и успешно завершить свои собственные фазы инициализации 2 и 3. Наконец, A также завершает инициализацию и входит в кеш первого уровня singletonObjects, и к счастью, потому что B получил объект ссылка на A, поэтому объект A, который теперь содержит B, завершил инициализацию.
Когда вы знаете этот принцип, вы должны знать, почему Spring не может решить проблему « метод построения A зависит от объекта экземпляра B, а метод построения B зависит от объекта экземпляра A »! Поскольку предпосылкой добавления трехуровневого кэша singletonFactories является выполнение конструктора, циклическая зависимость конструктора не может быть разрешена.
В следующем примере вы продемонстрируете внедрение конструктора и внедрение набора.
Напишите класс А класс Б
public class A {
private B b;
public A() {
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
public void method(){
System.out.println("A方法调用");
}
}
public class B {
private A a;
public B(){
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
public void method(){
System.out.println("B方法调用");
}
}
написать xml
<bean id="b" class="com.lixiang.demo.B">
<property name="a" ref="a"></property>
</bean>
<bean id="a" class="com.lixiang.demo.A">
<property name="b" ref="b"></property>
</bean>
тест
Изменить XML-конфигурацию
<bean id="b" class="com.lixiang.demo.B">
<!--将a改成用构造器注入-->
<constructor-arg name="a" ref="a"></constructor-arg>
</bean>
<bean id="a" class="com.lixiang.demo.A">
<property name="b" ref="b"></property>
</bean>
корректировка кода
public class B {
private A a;
public B(A a){
this.a = a;
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
public void method(){
System.out.println("B方法调用");
}
}
тест
Можно обнаружить, что внедрение с помощью конструктора является ненормальным.
Spring вводит механизм «предварительного раскрытия bean-компонентов»: при создании объекта A он сначала создаст пустой объект A и добавит его в пул кеша.
То есть «предварительно выставить Bean», а затем продолжить создание объекта B и внедрить его в объект A. При создании объекта B, поскольку объект A уже находится в пуле буферов, объект A можно получить напрямую, а затем объект B вводится в объект A для завершения инициализации Bean.
Хорошо, теперь весь процесс создания Бина завершен. Мы рассматриваем конкретную реализацию следующих трех методов.
Во-первых, это метод createBeanInstance().
Затем метод populateBean()
Наконец, метод initializeBean()
Итак, с процессом создания Spring bean-компонентов разобрались.
Не забудьте поставить лайк + подписаться!