詳實(shí)明了的IOC容器的介紹,啟動(dòng)流程以及Bean的實(shí)例化和依賴注入

文章目錄

    前言
    項(xiàng)目環(huán)境
    核心要點(diǎn)
    IOC容器的啟動(dòng)過程
        1. 資源定位,找到配置文件
        2.BeanDefinition的載入和解析,將配置文件解析成BeanDefiniton
        3. BeanDefinition的注冊(cè),將BeanDefinition向Map中注冊(cè)`beanDefinitionMap`
    Bean的實(shí)例化和依賴注入
        Bean的實(shí)例化
    流程分析
        實(shí)例化Bean
        Bean的依賴注入(屬性注入)
    總結(jié)

前言

今天我們來認(rèn)識(shí)一下Spring IOC容器,本文主要介紹SpringIOC容器的核心要點(diǎn)以及其啟動(dòng)流程和實(shí)例化流程。
項(xiàng)目環(huán)境

Springframework 4.3.12
核心要點(diǎn)

Spring IOC是什么?他有什么作用呢?我們通過了解學(xué)習(xí),Spring IOC是一個(gè)容器,用于生成和管理Bean的實(shí)例,以及實(shí)例之間的依賴關(guān)系,然后注入相關(guān)的依賴。這里我們可以把IOC容器想象成一個(gè)餐館。我們?nèi)ゲ宛^點(diǎn)菜的話,不需要關(guān)心菜的生成過程,不需要關(guān)心菜的原材料從哪里來。我們只需要最終做好的菜。這里的菜就是我們的需要的Bean。不同的菜對(duì)應(yīng)不同的Bean。沒有IOC 容器的情況下,如果需要一個(gè)Bean的話,就需要自己來new一個(gè)對(duì)象的實(shí)例,比如A類依賴了B類,那么就需要在A類中new一個(gè)B類的實(shí)例對(duì)象,這就好像我們要自己在家動(dòng)手做菜一樣。有了IOC容器之后,如果A類依賴B類,只需要通過IOC容器幫我們創(chuàng)建A類的實(shí)例和B類的實(shí)例,然后IOC容器會(huì)將B類的實(shí)例注入到A類中。這就很像餐館把菜做好之后送給我們一樣。既然IOC容器這么6,那么他是如何實(shí)現(xiàn)這一切的呢?
還是回到餐館那個(gè)例子,做菜的話就需要與原材料和菜譜,同樣的IOC容器想要管理各個(gè)業(yè)務(wù)對(duì)象以及他們之間的依賴關(guān)系,就需要通過某種途徑來記錄和管理這些信息,而BeanDefinition對(duì)象就承擔(dān)了這個(gè)責(zé)任。IOC容器中每一個(gè)Bean都會(huì)有一個(gè)對(duì)應(yīng)的BeanDefinition實(shí)例,該實(shí)例負(fù)責(zé)保存bean對(duì)象的所有必要信息,包括Bean對(duì)象的class類型,是否是抽象類,構(gòu)造方法和參數(shù),以及其他屬性等,這里的BeanDefinition就相當(dāng)于原材料。而BeanDefinitionRegistry對(duì)象和BeanFactory對(duì)象就相當(dāng)于菜譜,告訴我們?nèi)绾螌⒃牧霞庸こ上鄳?yīng)的菜肴。
下面我們就來看看這些比較核心的類和接口。
在這里插入圖片描述
在這里插入圖片描述

認(rèn)識(shí)上面的幾個(gè)核心接口和類,對(duì)我們下面看Bean的啟動(dòng)過程和實(shí)例化過程有很大的幫助。

需要說明的是,在Spring中,ApplicationContext是IOC容器的承載體,而BeanFactory是操作這個(gè)容器的工具,兩者關(guān)系緊密,相互協(xié)作,refresh方法實(shí)現(xiàn)了ApplicationContext和BeanFactory相互協(xié)作的過程,不同之處主要在于子類 AbstractRefreshableApplicationContext 和 GenericApplicationContext 中實(shí)現(xiàn),兩者使用的 BeanFactory 都為 DefaultListableBeanFactory,它構(gòu)建在BeanFactory之 上,屬于更?級(jí)的容器,除了具有BeanFactory的所有能?之外,還提供對(duì)事件監(jiān)聽機(jī)制以及國(guó)際化的?持等。它管理的bean,在容器啟動(dòng) 時(shí)全部完成初始化和依賴注?操作。
IOC容器的啟動(dòng)過程

介紹完了IOC容器的核心類和要點(diǎn),接下來我們看看IOC容器的啟動(dòng)過程,其啟動(dòng)過程主要有如下三個(gè)步驟:

  1. 資源定位,找到配置文件

這里定位資源有兩種方式,一種是通過ClassPathXmlApplicationContext類來解析Spring的配置文件的形式,就是通過配置文件來定義Bean的情況,另外,一種情況就是通過注解的方式來定義Bean的情況,這種情況是通過AnnotationConfigApplicationContext類解析的,主要是掃描項(xiàng)目的classPath下定義的注解。下面我們首先介紹下通過ClassPathXmlApplicationContext。這個(gè)類的核心作用是作為一個(gè)解析Xml的入口,其調(diào)用鏈?zhǔn)牵? ClassPathXmlApplicationContext類的構(gòu)造器
------>AbstractApplicationContext類的refresh方法
----->調(diào)用AbstractRefreshableApplicationContext類的refreshBeanFactory方法
---->XmlWebApplicationContext類的loadBeanDefinitions方法
----> AbstractBeanDefinitionReader類的loadBeanDefinitions方法
---->XmlBeanDefinitionReader類的loadBeanDefinitions方法
---->XmlBeanDefinitionReader類的doLoadBeanDefinitions方法
---->XmlBeanDefinitionReader類的registerBeanDefinitions方法
---->DefaultBeanDefinitionDocumentReader類的registerBeanDefinitions方法
調(diào)用層次很深,我們就直接跳到核心的方法來看。下面我們就來看看registerBeanDefinitions方法

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	this.readerContext = readerContext;
	logger.debug("Loading bean definitions");
	//讀取XML文件
	Element root = doc.getDocumentElement();
	//載入并注冊(cè)BeanDefinition
	doRegisterBeanDefinitions(root);
}

然后,registerBeanDefinitions方法只是讀取到根節(jié)點(diǎn)root之后,就另外一個(gè)核心方法doRegisterBeanDefinitions方法,然后,doRegisterBeanDefinitions方法又把邏輯轉(zhuǎn)給了parseBeanDefinitions方法,這個(gè)parseBeanDefinitions方法首先獲取所有的子節(jié)點(diǎn) ,然后遍歷解析子節(jié)點(diǎn),載入BeanDefinition又交給了parseDefaultElement方法和parseCustomElement方法。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	if (delegate.isDefaultNamespace(root)) {
		//獲取子節(jié)點(diǎn)
		NodeList nl = root.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			if (node instanceof Element) {
				Element ele = (Element) node;
				if (delegate.isDefaultNamespace(ele)) {
					//解析節(jié)點(diǎn)
					parseDefaultElement(ele, delegate);
				}
				else {
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	else {
		delegate.parseCustomElement(root);
	}
}

2.BeanDefinition的載入和解析,將配置文件解析成BeanDefiniton

說完了配置文件的解析之后,接下來,我們來看看BeanDefinition的載入和解析。我們直接找到parseDefaultElement方法。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	//省略部分非核心代碼
	//如果節(jié)點(diǎn)是bean節(jié)點(diǎn),說明是一個(gè)Bean
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		processBeanDefinition(ele, delegate);
	}
}

這個(gè)方法按照節(jié)點(diǎn)名,調(diào)用不同的處理方法,在此處我們只看節(jié)點(diǎn)為bean時(shí)調(diào)用的方法processBeanDefinition方法。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		try {
			// Register the final decorated instance.(注冊(cè)BeanDefinition)
			BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
		}
		catch (BeanDefinitionStoreException ex) {
			getReaderContext().error("Failed to register bean definition with name '" +
					bdHolder.getBeanName() + "'", ele, ex);
		}
		// Send registration event.
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}

我們重點(diǎn)看BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());這個(gè)方法,這個(gè)方法才是真正的將傳入BeanDefinitionRegistry類,載入并解析BeanDefinition,然后對(duì)BeanDefinition進(jìn)行注冊(cè)。
3. BeanDefinition的注冊(cè),將BeanDefinition向Map中注冊(cè)beanDefinitionMap

接下來就到了我們的重頭戲,注冊(cè)BeanDefinition到beanDefinitionMap中,其中key就是Bean的id,其中beanDefinitionMap是一個(gè)定義在DefaultListableBeanFactory類中全局的線程安全的map,用于存放解析到的BeanDefinition。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);

讓我們來看看registerBeanDefinition方法吧,這個(gè)方法核心的步驟有兩步:

根據(jù)傳入的beanName檢查beanDefinition是否存在,如果存在就是一系列的校驗(yàn),主要是保證BeanDefinition的單例性,就是說IOC容器中每個(gè)Bean的實(shí)例時(shí)單例的。
將傳入的beanDefinition實(shí)例放到beanDefinitionMap中。

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
		throws BeanDefinitionStoreException {
			if (hasBeanCreationStarted()) {
			// Cannot modify startup-time collection elements anymore (for stable iteration)
			//加鎖,保證線程安全
			synchronized (this.beanDefinitionMap) {
			// 將beanDefinition值設(shè)置到beanDefinitionMap中
				this.beanDefinitionMap.put(beanName, beanDefinition);
				List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
				updatedDefinitions.addAll(this.beanDefinitionNames);
				updatedDefinitions.add(beanName);
				this.beanDefinitionNames = updatedDefinitions;
				if (this.manualSingletonNames.contains(beanName)) {
					Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
					updatedSingletons.remove(beanName);
					this.manualSingletonNames = updatedSingletons;
				}
			}
		}
		else {
			// Still in startup registration phase,將beanDefinition值設(shè)置到beanDefinitionMap中
			this.beanDefinitionMap.put(beanName, beanDefinition);
			this.beanDefinitionNames.add(beanName);
			this.manualSingletonNames.remove(beanName);
		}
		this.frozenBeanDefinitionNames = null;
		}

小結(jié),至此,我們對(duì)IOC容器的初始化過程就解析完了,其實(shí)其初始化過程還是比較簡(jiǎn)單的,只是Spring的代碼結(jié)構(gòu)比較深,核心代碼不好找。
Bean的實(shí)例化和依賴注入

說完了IOC容器的初始化過程,接下來,我們來看看IOC容器的實(shí)例化過程。經(jīng)過上一個(gè)階段,所有Bean定義都通過BeanDefinition的方式注冊(cè)到了BeanDefinitionRegistry中。當(dāng)某個(gè)請(qǐng)求通過容器的getBean方法請(qǐng)求某個(gè)對(duì)象,或者因?yàn)橐蕾囮P(guān)系容器需要隱式的調(diào)用getBean方法時(shí),就會(huì)觸發(fā)第二階段的活動(dòng),IOC容器首先檢查所請(qǐng)求的對(duì)象是否已經(jīng)實(shí)例化完成,如果沒有,則會(huì)根據(jù)注冊(cè)的BeanDefinition所提供的信息實(shí)例化請(qǐng)求對(duì)象。并為其注入依賴,當(dāng)該對(duì)象裝配完成后,容器會(huì)立即返回給請(qǐng)求方法。
Bean的實(shí)例化

讓我們從前面提到的getBean方法說起,這里的調(diào)用鏈如下:
AbstractBeanFactory類的getBean方法
----->AbstractBeanFactory類的doGetBean方法
----->AbstractBeanFactory類的createBean方法
------>AbstractAutowireCapableBeanFactory類的doCreateBean方法(這個(gè)方法主要是)
創(chuàng)建Bean的核心邏輯就在AbstractAutowireCapableBeanFactory類的doCreateBean方法中:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
// Instantiate the bean(實(shí)例化Bean)
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
//如果是單例Bean,首先清理FactoryBean緩存
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//使用特定的策略實(shí)例化Bean,如果工廠方法、構(gòu)造器等,將BeanDefinition轉(zhuǎn)換為BeanWrapper
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
// Allow post-processors to modify the merged bean definition. 允許MergedBeanDefinitionPostProcessor修改BeanDefinition
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
“Post-processing of merged bean definition failed”, ex);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
//需要提前暴露Bean的條件:?jiǎn)卫?amp;&允許循環(huán)依賴&&當(dāng)前Bean正在創(chuàng)建中,檢測(cè)到了循環(huán)依賴
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace(“Eagerly caching bean '” + beanName +
“’ to allow for resolving potential circular references”);
}
//對(duì)于需要提前暴露的Bean,以其ObjectFactory的形式放入singletonFactories中,以解決循環(huán)依賴的問題
//ObjectFactory所創(chuàng)建的Bean由getEarlyBeanReference()方法指定
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance. Bean的初始化
Object exposedObject = bean;
try {
//對(duì)Bean進(jìn)行屬性的填充。此外,如果依賴了其他Bean,則會(huì)在這里注入依賴
populateBean(beanName, mbd, instanceWrapper);
//執(zhí)行Bean的初始化方法,如配置的init-method等
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//循環(huán)依賴檢查
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
//省略部分代碼
throw new BeanCurrentlyInCreationException();
}
}
}
}

try {
    //注冊(cè)Bean的銷毀方法,如destroy-method
    registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
    throw new BeanCreationException(
        mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;

}

}

從doCreateBean方法我們可以看出Bean生命周期的一個(gè)完整過程,這個(gè)方法里面的流程很長(zhǎng),知識(shí)點(diǎn)很多。下面我們就對(duì)流程進(jìn)行具體的分析。
流程分析
實(shí)例化Bean

實(shí)例化Bean的邏輯是在SimpleInstantiationStrategy類的instantiate方法中實(shí)現(xiàn)的。Bean的實(shí)例化其實(shí)就是將指定的BeanDefinition轉(zhuǎn)換成BeanWrapper,然后通過指定構(gòu)造器和默認(rèn)無參構(gòu)造器,CGLB動(dòng)態(tài)代理等方式來實(shí)例化Bean,這時(shí)實(shí)例化后的Bean是一個(gè)剛實(shí)例化好的,屬性未賦值的空Bean。

@Override
public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
//省略部分代碼
// Don’t override the class with CGLIB if no overrides.
if (bd.getMethodOverrides().isEmpty()) {
//通過構(gòu)造器實(shí)例化Bean
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// Must generate CGLIB subclass.(通過CGLIB的方式生成Bean)
return instantiateWithMethodInjection(bd, beanName, owner);
}
}

Bean的依賴注入(屬性注入)

說完了Bean的實(shí)例化,接下來我們來說下Bean的依賴注入。屬性注入必須用到PropertyValue類,這個(gè)類保存了Bean的所有屬性和依賴信息。
依賴注入的調(diào)用流程是AbstractAutowireCapableBeanFactory類的applyPropertyValues方法。

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
		//類型轉(zhuǎn)換接口
		TypeConverter converter = getCustomTypeConverter();
	if (converter == null) {
		converter = bw;
	}
	BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

String propertyName = pv.getName();
	                //對(duì)于ref來說就是beanName,對(duì)于value 來說就是value
			Object originalValue = pv.getValue();
			Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
			boolean convertible = bw.isWritableProperty(propertyName) &&
					!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
			if (convertible) {
				convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
			}
try {
		//設(shè)置依賴屬性
		bw.setPropertyValues(new MutablePropertyValues(deepCopy));
	}
	catch (BeansException ex) {
		throw new BeanCreationException(
				mbd.getResourceDescription(), beanName, "Error setting property values", ex);
	}

}

	private Object convertForProperty(Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
	if (converter instanceof BeanWrapperImpl) {
		return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
	}
	else {
		PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
		MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
		return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
	}
}

其中TypeConverter 類型轉(zhuǎn)化接口,將傳入的值轉(zhuǎn)化為其需要的類型。
SimpleTypeCoverter 是TypeConverter接口的一個(gè)實(shí)現(xiàn)。其依賴于java.beans中的PropertyEditor,其類似于java GUI中的編程,例如:拖拽一個(gè)button, 然后,設(shè)置其顏色,長(zhǎng)度,寬度,這些都屬于button的屬性,在java.beans中將這些抽象成了一個(gè)PropertyEditor 接口。 setAsText(), 例如button 的高度,值是什么跟屬性的類型密切相關(guān)。
總結(jié)

本文主要介紹了IOC容器的核心概念,以及其啟動(dòng)過程。然后,就是介紹了Bean的實(shí)例化過程,熟悉IOC容器我們需要先了解清楚其核心的幾個(gè)接口,例如:BeanFactory接口,BeanDefinitionRegistry接口等。IOC容器的啟動(dòng)過程無非就是解析配置文件,將屬性值存放到BeanDefinition中。Bean的實(shí)例化是通過反射或者CGLIB的方式來的。Bean中的屬性是存放在PropertyValue中。





作者:碼農(nóng)飛哥
微信公眾號(hào):碼農(nóng)飛哥