Spring是怎麼創建一個Bean的?

1.前言

大致分析了下Spring的getBean過程,但主要關注doCreateBean。

2.整體流程

調用鏈:preInstantiateSingletons->getBean->doGetBean->getSingleton-> singletonFactory.getObject()->createBean->doCreateBean

圖中,需要特別注意getBeancreateBeancreateBeanInstancepopulateBeaninitializeBean
在這裏插入圖片描述

3.preInstantiateSingletons 提前初始化非lazy的單實例

SpringIOC源碼學習總結可知,在Spring容器refresh的階段會調用finishBeanFactoryInitialization->preInstantiateSingletons,然後提前實例化非lazy的單實例。

3.1 getBean 得到/創建bean

調用鏈:createBean->doGetBean->getSingleton
在這裏插入圖片描述

if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
						//doGetBean
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

3.2 getSingleton(String beanName, ObjectFactory<?> singletonFactory)獲得單實例

調用鏈:getSingleton->singletonFactory.getObject()->createBean

在這裏插入圖片描述

  1. 先嚐試中singletonObjects拿到Spring已經緩存的bean實例。
  2. 標記該bean【正創建中】
  3. 調用singletonFactory.getObject()->createBean創建bean。
  4. 添加到緩存。
  5. 去掉【正創建中】標記。
  6. 添加到緩存。

getSingleton源碼

	synchronized (this.singletonObjects) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null) {
			//標記爲該bean【正在創建】
			beforeSingletonCreation(beanName);
			...
			//創建bean,重點關注
			singletonObject = singletonFactory.getObject();
			...
			//添加到緩存
			if (newSingleton) {
				addSingleton(beanName, singletonObject);
			}
		}
		return singletonObject;
	}finally {
		...
		//去除 【正在創建】 的標記
		afterSingletonCreation(beanName);
	}

3.2.1 createBean`創建bean

調用鏈:createBean->doCreateBean

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
		throws BeanCreationException {
		...
	RootBeanDefinition mbdToUse = mbd;
	//從BeanDefinition中取出Class信息
	Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		//爲bean設置<bean>標籤中的<replaced-method>方法。實現方法重載
		mbdToUse.prepareMethodOverrides();
		...
		//有機會創建代理對象,暫不理會這裏
		Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
		...
		//創建bean實例, 重點關注
		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
		...
		return beanInstance;
}

3.3 getObjectForBeanInstance 處理FactoryBean

在這裏插入圖片描述
1.如果該bean是普通bean,則直接返回。如果該bean是FactoryBean,且getBean("&id")形式來獲取,則返回該bean.
2.否則是需要取FactoryBean#getObject對應的實例。因此調用該bean的getObject方法返回實例bean,並放入factoryBeanObjectCache緩存。

關於FactoryBean可查看FactoryBean的作用

4.doCreateBean(beanName, mbdToUse, args)

終於到了今天的主角doCreateBean

這裏就不貼代碼了, 太多了。需要看源碼直接看AbstractAutowireCapableBeanFactory#doCreateBean 直接看下流程圖:
在這裏插入圖片描述

流程:

  1. 創建實例:createBeanInstance(beanName, mbd, args)創建一個帶有bean實例的BeanWrapper
  2. 收集註解:通過applyMergedBeanDefinitionPostProcessors調用BeanPostProcessor收集@Resource、@Autowired、@PreConstruct等註解。
  3. 提前暴露Bean:如果是需要提前暴露bean,則添加一個暴露沒有注入屬性的bean的方法到緩存addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));。此處是爲了解決循環依賴,可查看:spring中怎麼解決循環依賴
  4. 注入屬性:populateBean除了處理標籤中的property,還利用BeanPostProcessor去處理【2】中收集的@Resource、@Autowired等,完成依賴注入。—CommonAnnotationBeanPostProcessor以及AutowiredAnnotationBeanPostProcessor
  5. 初始化bean(完成注入後的操作):initializeBean,這裏的鉤子就比較多:
    a. invokeAwareMethods:響應BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口。
    b. applyBeanPostProcessorsBeforeInitialization:遍歷BeanPostProcess調用BeanPostProcessor#postProcessBeforeInitialization
    c. invokeInitMethods:
    - InitializeBean.afterPropertiesSet:如果該bean有實現InitializeBean接口,則調用。
    - invokeCustomInitMethod(initMethod):如果有<bean>有配置init-method則調用。
    d. applyBeanPostProcessorsAfterInitialization:遍歷BeanPostProcess調用BeanPostProcessor#postProcessAfterInitialization

小結
1.a爲常用的Aware接口,b、d爲常用的BeanPostProcessor接口,c爲常用InitializingBean接口。
2.重點類BeanpostProcessor:CommonAnnotationBeanPostProcessorAutowiredAnnotationBeanPostProcessor

總結

createBean 過程總之就是獲得BeanDefinition的信息,通過反射創建實例,再注入依賴,然後initializeBean完成特殊處理,最後放入緩存中。 在整個過程中,留下了許多鉤子,我們可以使用Aware接口,BeanPostProcessor去做特殊處理。

最後再放一張詳細的圖:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章