1. 程式人生 > >SPRING原理解析-Ioc容器初始化

SPRING原理解析-Ioc容器初始化

       IoC容器的初始化就是含有BeanDefinition資訊的Resource的定位、載入、解析、註冊四個過程,最終我們配置的bean,以beanDefinition的資料結構存在於IoC容器即記憶體中。這裡並不涉及bean的依賴注入,只是bean定義的載入。但有例外,在使用Ioc容器時有一個預例項化的配置,即bean定義中的設定了lazyinit屬性,那麼這個bean在Ioc容器初始化時就預先載入,不需要等到Ioc整個初始化後,第一次getBean時才會觸發。其中refresh()啟動對Ioc容器的初始化。
      第一過程是Resource定位過程。這個Resource定位指的是BeanDefinition的資源定位,它由ResourceLoader通過統一的Resource介面來完成,這個Resource對各種形式的BeanDefinition的使用提供了統一介面。對於這些BeanDefinition的存在形式,相信大家都不會感到陌生。比如說,在檔案系統中的Bean定義資訊可以使用FileSystemResource來進行抽象;在類路徑中可以使用前面提到的ClassPathResource來使用,等等。這個過程類似於容器尋找資料的過程,就像用水桶裝水先要把水找到一樣。 
       第二個關鍵的部分是BeanDefinition的載入,該載入過程把使用者定義好的Bean表示成IoC容器內部的資料結構,而這個容器內部的資料結構就是BeanDefinition,下面可以看到這個資料結構的詳細定義。總地說來,這個BeanDefinition實際上就是POJO物件在IoC容器中的抽象,這個BeanDefinition定義了一系列的資料來使得IoC容器能夠方便地對POJO物件也就是Spring的Bean進行管理。即BeanDefinition就是Spring的領域物件。下面我們會對這個載入的過程進行詳細的分析,便於大家對整個過程有比較清楚的瞭解。 
      第三個過程是向IoC容器註冊這些BeanDefinition的過程。這個過程是通過呼叫BeanDefinitionRegistry介面的實現來完成的,這個註冊過程把載入過程中解析得到的BeanDefinition向IoC容器進行註冊。可以看到,在IoC容器內部將BeanDefinition注入到一個HashMap中去,Ioc容器是通過這個HashMap來持有這些BeanDefinition資料的。
BeanDefinition的Resource的定位


在AbstractBeanDefinitionReader.loadBeanDefinitions()方法中,呼叫了defaultResourceLoader的getResource()方法,這裡即為resouce的定位,如下
public Resource getResource(String location) {  
        Assert.notNull(location, "Location must not be null");  
        if (location.startsWith(CLASSPATH_URL_PREFIX)) {  
            return new  
ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());  
        }  
        else {  
            try {  
                // Try to parse the location as a URL...  
                URL url = new URL(location);  
                return new UrlResource(url);  
            }  
            catch (MalformedURLException ex) {  
                // No URL -> resolve as resource path.  
                return getResourceByPath(location);  
            }  
        }  
    }    

getResourceByPath的實現如下:
protected Resource getResourceByPath(String path) {  
        return new ClassPathContextResource(path, getClassLoader());  
    } 

beanDefinition的載入
beanDefinition的載入在XmlBeanDefinitionReader中的loadBeanDefinitions()方法實現:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {  
        Assert.notNull(encodedResource, "EncodedResource must not be null");  
        if (logger.isInfoEnabled()) {  
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());  
        }  
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();  
        if (currentResources == null) {  
            currentResources = new HashSet<EncodedResource>(4);  
            this.resourcesCurrentlyBeingLoaded.set(currentResources);  
        }  
        if (!currentResources.add(encodedResource)) {  
            throw new BeanDefinitionStoreException(  
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");  
        }  
        try {  
            InputStream inputStream = encodedResource.getResource().getInputStream();  
            try {  
                InputSource inputSource = new InputSource(inputStream);  
                if (encodedResource.getEncoding() != null) {  
                    inputSource.setEncoding(encodedResource.getEncoding());  
                }  
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());  
            }  
            finally {  
                inputStream.close();  
            }  
        }  
        catch (IOException ex) {  
            throw new BeanDefinitionStoreException(  
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);  
        }  
        finally {  
            currentResources.remove(encodedResource);  
            if (currentResources.isEmpty()) {  
                this.resourcesCurrentlyBeingLoaded.remove();  
            }  
        }  
    }  

注:上面的程式碼中提到,獲得xml檔案,得到IO的InputSOurce準備進行讀取,具體的讀取在doLoadBeanDefinitions()方法中找到,這是從特定的xml檔案中實際載入BeanDefinition的地方;解析的一步在documentLoader中,也就是defaultDocumentLoader按照xml解析得到Document物件,而第二步,defaultBeanDefinitionDocumentreader按照Spring的bean規則把從Document物件解析為BeanDefinition,最後BeanDefinition物件集合到封裝類BeanDefinitionHolder中。

其中,doLoadBeanDefinitions()方法在該類中實現如下

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)  
            throws BeanDefinitionStoreException {  
        try {  
            int validationMode = getValidationModeForResource(resource);  
            Document doc = this.documentLoader.loadDocument(  
                    inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());  
            return registerBeanDefinitions(doc, resource);  
        }  
        catch(){  
        //異常處理   
}  
} 

其中,registerBeanDefinitions()方法在該類中實現如下(解析第二步):

//按照Spring的Bean語義要求將Bean定義資源解析並轉換為容器內部資料結構
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//得到BeanDefinitionDocumentReader來對xml格式的BeanDefinition解析
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//獲得容器中註冊的Bean數量
		int countBefore = getRegistry().getBeanDefinitionCount();
//解析過程入口,這裡使用了委派模式,BeanDefinitionDocumentReader只是個介面,//具體的解析實現過程有實現類DefaultBeanDefinitionDocumentReader完成
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//統計解析的Bean數量
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
//建立BeanDefinitionDocumentReader物件,解析Document物件
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
		return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
	}
DefaultBeanDefinitionDocumentReader 對Bean定義的Document物件解析:

BeanDefinitionDocumentReader介面通過registerBeanDefinitions方法呼叫其實現類DefaultBeanDefinitionDocumentReader對Document物件進行解析,解析的程式碼如下:

//根據Spring DTD對Bean的定義規則解析Bean定義Document物件
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		//獲得XML描述符
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		//獲得Document的根元素
		Element root = doc.getDocumentElement();
		//具體的解析過程由BeanDefinitionParserDelegate實現,
//BeanDefinitionParserDelegate中定義了Spring Bean定義XML檔案的各種元素
		BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
		//在解析Bean定義之前,進行自定義的解析,增強解析過程的可擴充套件性
		preProcessXml(root);
		//從Document的根元素開始進行Bean定義的Document物件
		parseBeanDefinitions(root, delegate);
		//在解析Bean定義之後,進行自定義的解析,增加解析過程的可擴充套件性
postProcessXml(root);
	}
	//建立BeanDefinitionParserDelegate,用於完成真正的解析過程
	protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root) {
		BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
		//BeanDefinitionParserDelegate初始化Document根元素
		delegate.initDefaults(root);
		return delegate;
	}
	//使用Spring的Bean規則從Document的根元素開始進行Bean定義的Document物件
	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		//Bean定義的Document物件使用了Spring預設的XML名稱空間
		if (delegate.isDefaultNamespace(root)) {
			//獲取Bean定義的Document物件根元素的所有子節點
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				//獲得Document節點是XML元素節點
				if (node instanceof Element) {
					Element ele = (Element) node;
				//Bean定義的Document的元素節點使用的是Spring預設的XML名稱空間
					if (delegate.isDefaultNamespace(ele)) {
						//使用Spring的Bean規則解析元素節點
						parseDefaultElement(ele, delegate);
					}
					else {
//沒有使用Spring預設的XML名稱空間,則使用使用者自定義的解//析規則解析元素節點
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			//Document的根節點沒有使用Spring預設的名稱空間,則使用使用者自定義的
			//解析規則解析Document根節點
			delegate.parseCustomElement(root);
		}
	}
	//使用Spring的Bean規則解析Document元素節點
	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		//如果元素節點是<Import>匯入元素,進行匯入解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		//如果元素節點是<Alias>別名元素,進行別名解析
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		//元素節點既不是匯入元素,也不是別名元素,即普通的<Bean>元素,
//按照Spring的Bean規則解析元素
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
	}
	//解析<Import>匯入元素,從給定的匯入路徑載入Bean定義資源到Spring IoC容器中
	protected void importBeanDefinitionResource(Element ele) {
		//獲取給定的匯入元素的location屬性
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
		//如果匯入元素的location屬性值為空,則沒有匯入任何資源,直接返回
if (!StringUtils.hasText(location)) {
			getReaderContext().error("Resource location must not be empty", ele);
			return;
		}
		//使用系統變數值解析location屬性值
		location = SystemPropertyUtils.resolvePlaceholders(location);
		Set<Resource> actualResources = new LinkedHashSet<Resource>(4);
//標識給定的匯入元素的location是否是絕對路徑
		boolean absoluteLocation = false;
		try {
			absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
		}
		catch (URISyntaxException ex) {
			//給定的匯入元素的location不是絕對路徑
		}
		//給定的匯入元素的location是絕對路徑
		if (absoluteLocation) {
			try {
				//使用資源讀入器載入給定路徑的Bean定義資源
				int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
				}
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error(
						"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
			}
		}
		else {
			//給定的匯入元素的location是相對路徑
			try {
				int importCount;
				//將給定匯入元素的location封裝為相對路徑資源
				Resource relativeResource = getReaderContext().getResource().createRelative(location);
				//封裝的相對路徑資源存在
				if (relativeResource.exists()) {
					//使用資源讀入器載入Bean定義資源
					importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
					actualResources.add(relativeResource);
				}
				//封裝的相對路徑資源不存在
				else {
					//獲取Spring IoC容器資源讀入器的基本路徑
					String baseLocation = getReaderContext().getResource().getURL().toString();
					//根據Spring IoC容器資源讀入器的基本路徑載入給定匯入
					//路徑的資源
					importCount = getReaderContext().getReader().loadBeanDefinitions(
							StringUtils.applyRelativePath(baseLocation, location), actualResources);
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
				}
			}
			catch (IOException ex) {
				getReaderContext().error("Failed to resolve current resource location", ele, ex);
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
						ele, ex);
			}
		}
		Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
		//在解析完<Import>元素之後,傳送容器匯入其他資源處理完成事件
		getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
	}
	//解析<Alias>別名元素,為Bean向Spring IoC容器註冊別名
	protected void processAliasRegistration(Element ele) {
		//獲取<Alias>別名元素中name的屬性值
String name = ele.getAttribute(NAME_ATTRIBUTE);
//獲取<Alias>別名元素中alias的屬性值
		String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
		boolean valid = true;
		//<alias>別名元素的name屬性值為空
if (!StringUtils.hasText(name)) {
			getReaderContext().error("Name must not be empty", ele);
			valid = false;
		}
		//<alias>別名元素的alias屬性值為空
		if (!StringUtils.hasText(alias)) {
			getReaderContext().error("Alias must not be empty", ele);
			valid = false;
		}
		if (valid) {
			try {
				//向容器的資源讀入器註冊別名
				getReaderContext().getRegistry().registerAlias(name, alias);
			}
			catch (Exception ex) {
				getReaderContext().error("Failed to register alias '" + alias +
						"' for bean with name '" + name + "'", ele, ex);
			}
			//在解析完<Alias>元素之後,傳送容器別名處理完成事件
			getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
		}
	}
	//解析Bean定義資源Document物件的普通元素
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		// BeanDefinitionHolder是對BeanDefinition的封裝,即Bean定義的封裝類
//對Document物件中<Bean>元素的解析由BeanDefinitionParserDelegate實現	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
	//向Spring IoC容器註冊解析得到的Bean定義,這是Bean定義向IoC容器註冊的入口			BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			//在完成向Spring IoC容器註冊解析得到的Bean定義之後,傳送註冊事件
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

通過上述Spring IoC容器對載入的Bean定義Document解析可以看出,我們使用Spring時,在Spring配置檔案中可以使用<Import>元素來匯入IoC容器所需要的其他資源,Spring IoC容器在解析時會首先將指定匯入的資源載入進容器中。使用<Ailas>別名時,Spring IoC容器首先將別名元素所定義的別名註冊到容器中。

對於既不是<Import>元素,又不是<Alias>元素的元素,即Spring配置檔案中普通的<Bean>元素的解析由BeanDefinitionParserDelegate類的parseBeanDefinitionElement方法來實現,程式碼如下:

//解析<Bean>元素的入口
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
		return parseBeanDefinitionElement(ele, null);
	}
//解析Bean定義資原始檔中的<Bean>元素,這個方法中主要處理<Bean>元素的id,name
//和別名屬性
	public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
		//獲取<Bean>元素中的id屬性值
		String id = ele.getAttribute(ID_ATTRIBUTE);
		//獲取<Bean>元素中的name屬性值
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
		////獲取<Bean>元素中的alias屬性值
		List<String> aliases = new ArrayList<String>();
		//將<Bean>元素中的所有name屬性值存放到別名中
if (StringUtils.hasLength(nameAttr)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
			aliases.addAll(Arrays.asList(nameArr));
		}
		String beanName = id;
		//如果<Bean>元素中沒有配置id屬性時,將別名中的第一個值賦值給beanName
		if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
			beanName = aliases.remove(0);
			if (logger.isDebugEnabled()) {
				logger.debug("No XML 'id' specified - using '" + beanName +
						"' as bean name and " + aliases + " as aliases");
			}
		}
		//檢查<Bean>元素所配置的id或者name的唯一性,containingBean標識<Bean>
		//元素中是否包含子<Bean>元素
		if (containingBean == null) {
			//檢查<Bean>元素所配置的id、name或者別名是否重複
			checkNameUniqueness(beanName, aliases, ele);
		}
		//詳細對<Bean>元素中配置的Bean定義進行解析的地方
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
			if (!StringUtils.hasText(beanName)) {
				try {
					if (containingBean != null) {
//如果<Bean>元素中沒有配置id、別名或者name,且沒有包含子//<Bean>元素,為解析的Bean生成一個唯一beanName並註冊
						beanName = BeanDefinitionReaderUtils.generateBeanName(
								beanDefinition, this.readerContext.getRegistry(), true);
					}
					else {
//如果<Bean>元素中沒有配置id、別名或者name,且包含了子//<Bean>元素,為解析的Bean使用別名向IoC容器註冊
						beanName = this.readerContext.generateBeanName(beanDefinition);
						//為解析的Bean使用別名註冊時,為了向後相容									//Spring1.2/2.0,給別名新增類名字尾
						String beanClassName = beanDefinition.getBeanClassName();
						if (beanClassName != null &&
								beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
								!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
							aliases.add(beanClassName);
						}
					}
					if (logger.isDebugEnabled()) {
						logger.debug("Neither XML 'id' nor 'name' specified - " +
								"using generated bean name [" + beanName + "]");
					}
				}
				catch (Exception ex) {
					error(ex.getMessage(), ele);
					return null;
				}
			}
			String[] aliasesArray = StringUtils.toStringArray(aliases);
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}
		//當解析出錯時,返回null
		return null;
	}
//詳細對<Bean>元素中配置的Bean定義其他屬性進行解析,由於上面的方法中已經對//Bean的id、name和別名等屬性進行了處理,該方法中主要處理除這三個以外的其他屬性資料
public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, BeanDefinition containingBean) {
		//記錄解析的<Bean>
		this.parseState.push(new BeanEntry(beanName));
//這裡只讀取<Bean>元素中配置的class名字,然後載入到BeanDefinition中去
		//只是記錄配置的class名字,不做例項化,物件的例項化在依賴注入時完成
		String className = null;
		if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
			className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
		}
		try {
			String parent = null;
			//如果<Bean>元素中配置了parent屬性,則獲取parent屬性的值
			if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
				parent = ele.getAttribute(PARENT_ATTRIBUTE);
			}
			//根據<Bean>元素配置的class名稱和parent屬性值建立BeanDefinition
			//為載入Bean定義資訊做準備
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//對當前的<Bean>元素中配置的一些屬性進行解析和設定,如配置的單態(singleton)屬性等
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
	//為<Bean>元素解析的Bean設定description資訊	bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
			//對<Bean>元素的meta(元資訊)屬性解析
			parseMetaElements(ele, bd);
			//對<Bean>元素的lookup-method屬性解析
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			//對<Bean>元素的replaced-method屬性解析
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
			//解析<Bean>元素的構造方法設定
			parseConstructorArgElements(ele, bd);
			//解析<Bean>元素的<property>設定
			parsePropertyElements(ele, bd);
			//解析<Bean>元素的qualifier屬性
			parseQualifierElements(ele, bd);
			//為當前解析的Bean設定所需的資源和依賴物件
			bd.setResource(this.readerContext.getResource());
			bd.setSource(extractSource(ele));
			return bd;
		}
		catch (ClassNotFoundException ex) {
			error("Bean class [" + className + "] not found", ele, ex);
		}
		catch (NoClassDefFoundError err) {
			error("Class that bean class [" + className + "] depends on not found", ele, err);
		}
		catch (Throwable ex) {
			error("Unexpected failure during bean definition parsing", ele, ex);
		}
		finally {
			this.parseState.pop();
		}
		//解析<Bean>元素出錯時,返回null
		return null;
	}

只要使用過Spring,對Spring配置檔案比較熟悉的人,通過對上述原始碼的分析,就會明白我們在Spring配置檔案中<Bean>元素的中配置的屬性就是通過該方法解析和設定到Bean中去的。

注意:在解析<Bean>元素過程中沒有建立和例項化Bean物件,只是建立了Bean物件的定義類BeanDefinition,將<Bean>元素中的配置資訊設定到BeanDefinition中作為記錄,當依賴注入時才使用這些記錄資訊建立和例項化具體的Bean物件。

上面方法中一些對一些配置如元資訊(meta)、qualifier等的解析,我們在Spring中配置時使用的也不多,我們在使用Spring的<Bean>元素時,配置最多的是<property>屬性,因此我們下面繼續分析原始碼,瞭解Bean的屬性在解析時是如何設定的。

5.BeanDefinitionParserDelegate解析<property>元素:

BeanDefinitionParserDelegate在解析<Bean>呼叫parsePropertyElements方法解析<Bean>元素中的<property>屬性子元素,通過原始碼分析,我們可以瞭解在Spring配置檔案中,<Bean>元素中<property>元素的相關配置是如何處理的:

a. ref被封裝為指向依賴物件一個引用。

b.value配置都會封裝成一個字串型別的物件。

c.ref和value都通過“解析的資料型別屬性值.setSource(extractSource(ele));”方法將屬性值/引用與所引用的屬性關聯起來。

在方法的最後對於<property>元素的子元素通過parsePropertySubElement方法解析,我們繼續分析該方法的原始碼,瞭解其解析過程。

6.解析<property>元素的子元素:

在BeanDefinitionParserDelegate類中的parsePropertySubElement方法對<property>中的子元素解析,通過原始碼分析,我們明白了在Spring配置檔案中,對<property>元素中配置的Array、List、Set、Map、Prop等各種集合子元素的都通過上述方法解析,生成對應的資料物件,比如ManagedList、ManagedArray、ManagedSet等,這些Managed類是Spring物件BeanDefiniton的資料封裝,對集合資料型別的具體解析有各自的解析方法實現,解析方法的命名非常規範,一目瞭然,我們對<list>集合元素的解析方法進行原始碼分析,瞭解其實現過程。

7.解析<list>子元素:

在BeanDefinitionParserDelegate類中的parseListElement方法就是具體實現解析<property>元素中的<list>集合子元素。

經過對Spring Bean定義資原始檔轉換的Document物件中的元素層層解析,Spring IoC現在已經將XML形式定義的Bean定義資原始檔轉換為Spring IoC所識別的資料結構——BeanDefinition,它是Bean定義資原始檔中配置的POJO物件在Spring IoC容器中的對映,我們可以通過AbstractBeanDefinition為入口,榮IoC容器進行索引、查詢和操作。

通過Spring IoC容器對Bean定義資源的解析後,IoC容器大致完成了管理Bean物件的準備工作,即初始化過程,但是最為重要的依賴注入還沒有發生,現在在IoC容器中BeanDefinition儲存的只是一些靜態資訊,接下來需要向容器註冊Bean定義資訊才能全部完成IoC容器的初始化過程。

 beanDefinition的解析
BeanDefinitionParserDelegate是一個重要的輔助類,它實現了對具體Bean元素在Bean定義資訊的解析。
InputSource(InputStream)獲取resource物件,接著使用DocumentLoader來對XML形式的Bean定義資訊進行讀入,轉換成DOM資料。
BeanDefinition的載入分兩部分,首先通過呼叫xml的解析器得到document,物件,但這些document物件並沒有按照Spring的Bean規則進行解析。在完成
通用的xml解析以後,才是按照Spring的Bean規則進行解析的地方,這個地方就是DomcumentReader中完成BeanDefinition的處理。處理的結果由BeanDefinitionHolder物件持有,這個BeanDefinitionHolder物件還持有BeanDefinition使用相關的資訊,比如Bean名字,別名id,name等屬性元素,這些屬性的解析還是比較簡單的。對於其他元素配置的解析,比如各種Bean的屬性配置,通過一個較複雜的解析過程,這個過程是友parseBeanDefinitionElement來完成的。解析完成後,會放到BeanDefinition物件中並設定到BeanDefinitionHolder中去。
beanDefinition的解析是在BeanDefinitionParserDelegate類的parseBeanDefinitionElement(Elementele, BeanDefinition containingBean)方法中完成。該方法首先呼叫了
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);  

獲取bean的id和bean的name。接著,呼叫
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean)  ;

對bean元素進行詳細解析,呼叫parseBeanDefinitionAttributes()解析bean的屬性;
parsePropertyElements(ele, bd)解析bean的property。
bean屬性就是注入scope、isLazyInit、autowire、sigleton等屬性,都是按照類似方法來做:
if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
   // Spring 2.x "scope" attribute
  beanDefinition.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}

呼叫beanDefinition的set方法來實現。
解析propterty方法具體在parsePropertyElements()方法中,主要:
1.  bean定義中如果有同名的propterty,那麼只解析第一個property,對於後續的同名propterty不做任何處理。

2. 需要判斷是ref還是value。

 BeanDefinition的註冊

解析過後的BeanDefinition在IoC容器中的註冊:

讓我們繼續跟蹤程式的執行順序,接下來會到我們第3步中分析DefaultBeanDefinitionDocumentReader對Bean定義轉換的Document物件解析的流程中,在其parseDefaultElement方法中完成對Document物件的解析後得到封裝BeanDefinition的BeanDefinitionHold物件,然後呼叫BeanDefinitionReaderUtils的registerBeanDefinition方法向IoC容器註冊解析的Bean,BeanDefinitionReaderUtils的註冊的原始碼如下:

當呼叫BeanDefinitionReaderUtils向IoC容器註冊解析的BeanDefinition時,真正完成註冊功能的是DefaultListableBeanFactory。

9.DefaultListableBeanFactory向IoC容器註冊解析後的BeanDefinition:

DefaultListableBeanFactory中使用一個HashMap的集合物件存放IoC容器中註冊解析的BeanDefinition,向IoC容器註冊的主要原始碼如下:

//儲存註冊的俄BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
//向IoC容器註冊解析的BeanDefiniton
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");
		//校驗解析的BeanDefiniton
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}
		//註冊的過程中需要執行緒同步,以保證資料的一致性
		synchronized (this.beanDefinitionMap) {
			Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
			//檢查是否有同名的BeanDefinition已經在IoC容器中註冊,如果已經註冊,
			//並且不允許覆蓋已註冊的Bean,則丟擲註冊失敗異常
			if (oldBeanDefinition != null) {
				if (!this.allowBeanDefinitionOverriding) {
					throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
							"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
							"': There is already [" + oldBeanDefinition + "] bound.");
				}
				else {//如果允許覆蓋,則同名的Bean,後註冊的覆蓋先註冊的
					if (this.logger.isInfoEnabled()) {
						this.logger.info("Overriding bean definition for bean '" + beanName +
								"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
					}
				}
			}
			//IoC容器中沒有已經註冊同名的Bean,按正常註冊流程註冊
			else {
				this.beanDefinitionNames.add(beanName);
				this.frozenBeanDefinitionNames = null;
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
			//重置所有已經註冊過的BeanDefinition的快取
			resetBeanDefinition(beanName);
		}
	}
 最後就通過beanDefinitionMap來操作beanDefinition了。

Ioc容器的例項化與依賴注入

Spring用CGLIB對Bean進行例項化,CGLIB是一個常用的位元組碼生成器的類庫,它提供了一系列的API來提供生產和轉換Java的位元組碼的功能。在Spring Aop中也使用CGLIB對java的位元組碼進行增強。在IOc容器中,要了解怎麼使用CGLIB來生成Bean,需要了解SimpleInstantiationStrategy類,該Strategy是Spring用來生成Bean物件的預設類,它提供了兩種例項化java物件的方法,一個是通過beanUtils,它使用jvm的反射功能,一種是通過前面提到的CGLIB(比如Enhance類)來生成。

依賴注入的發生實在BeanWrapper的setPropertyValues中實現的,具體的完成卻是在BeanWrapper的子類BeanWrapperImpl中實現的。
在Bean的建立和物件依賴注入的過程中,需要依據BeanDefinition中的資訊來遞迴地完成依賴注入。

 IoC容器的啟動
FileSystemXmlApplicationContext的建構函式如下,啟動了IoC容器。
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)  
            throws BeansException {  
        super(parent);  
        setConfigLocations(configLocations);  
        if (refresh) {  
            refresh();  
        }  
    }  

refresh()方法在FileSystemXmlApplicationContext的父類AbstractApplicationContext中實現。在refresh()方法中,最終呼叫了refreshBeanFactory()方法。
完整IoC容器啟動分析(以FileSystemXmlApplicationContext為例)
1.  FileSystemXmlApplicationContext構造方法呼叫refresh()方法,這裡是載入beanDefinition的入口
2. refresh()方法在FileSystemXmlApplicationContext的父類
AbstractApplicationContext中的實現,呼叫refreshBeanFactory()方法;
3. refreshBeanFactory()方法在AbstractApplicationContext的子類AbstractRefreshableApplicationContext中實現,呼叫了loadBeanDefinition()方法,啟動對beanDefinition的載入;
4. loadBeanDefinition()在AbstractXmlApplicationContext中實現,呼叫了XmlBeanDefinitionReader的loadBeanDefinitions()方法。
5. XmlBeanDefinitionReader的loadBeanDefinitions()實現了對於承載beanDefnition定義的xml檔案的讀入,以I/O的方式。
6. 讀入後,對beanDefinition進行解析。Bean解析採用SAX工具,先按照XML檔案格式解析,再按照spring bean也有的定義解析,在BeanDefinitionParserDelegate.parseBeanDefinitionElement()實現。
7. 最後對beanDefinition資訊進行註冊。就是將每個beanDefinition以key  =beanName,value = beanDefinition放入一個hashMap中,在DefaultListenableFactoty.RegisterBeanDefinition()中實現。
經過IoC容器的初始化後,IoC容器持有beanDefintion,為依賴注入bean即呼叫getBean()方法奠定了基礎。