1. 程式人生 > >【Spring原始碼閱讀】BeanDefinition原理與載入流程

【Spring原始碼閱讀】BeanDefinition原理與載入流程

BeanDefinition介面定義及其相關子類實現

在Spring容器初始化過程中,Spring會將配置檔案中配置的Java類封裝成一個個BeanDefinition。
BeanDefinition儲存了具體代表Bean的類,並通過實現了AttributeAccessor介面定義了讀寫屬性配置的相關方法。在基於xml配置Spring容器中,我們為某個Bean配置了具體的屬性值,這些都根據name-value對對映到BeanDefinition的元資料集合attributes中,在例項化成具體Bean的時候通過AttributeAccessor介面實現的方法完成屬性注入。
在這個基礎上BeanDefinition定義一個Bean基於Spring容器中的相關屬效能力,比如是否為單例,是否支援懶載入,是否支援自動裝配、為自動裝配首選等。
BeanDefinition作為介面定義,有眾多的實現類,常用的如下圖所示:
image

  1. AbstractBeanDefinition:BeanDefinition的較為完整、具體的抽象實現類,定義了三大子類ChildBeanDefinition,RootBeanDefinition,GenericBeanDefinition的基礎公用實現,具體實現了BeanDefinition屬性付覆寫,解析獲取BeanDefinition對應的BeanClass和Spring相關屬效能力的讀寫等(比如是否為單例,支援自動裝配等)
  2. RootBeanDefinition/ChildBeanDefinition:BeanDefinition存在父子關係,子BeanDefinition用ChildBeanDefinition表示,父BeanDefinition或沒有父子關係的用RootBeanDefinition表示。
  3. GenericBeanDefinition: 相比於RootBeanDefinition和ChildBeanDefinition在定義的時候就必須硬編碼,GenericBeanDefinition的優點可以動態的為GenericBeanDefinition設定parent。
  4. AnnotatedBeanDefinition:讀取基於註解定義的Bean。

Spring解析載入BeanDefinition流程

最終載入資源前的複雜過程

在最終開始載入資源,會經過一系列繁瑣的準備工作,核心載入邏輯在下一節doLoadBeanDefinitions載入邏輯實現描述,下面先是前期的準備工作:
在Spring初始化容器過程,第一步首先建立BeanFactory的時候,會呼叫AbstractApplicationContext的obtainFreshBeanFactory

方法,裡面會呼叫AbstractApplicationContext類中實現的refreshBeanFactory方法,在這個方法裡,呼叫了loadBeanDefinitions(beanFactory)來解析BeanDefinition,具體如下所示

protected final void refreshBeanFactory() throws BeansException {
	if (hasBeanFactory()) {
		destroyBeans();
		closeBeanFactory();
	}
	try {
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		beanFactory.setSerializationId(getId());
		customizeBeanFactory(beanFactory);
		// 載入Beanfinitions
		loadBeanDefinitions(beanFactory);
		synchronized (this.beanFactoryMonitor) {
			this.beanFactory = beanFactory;
		}
	}
	catch (IOException ex) {
		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
	}
}

具體實現如下:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	// 先建立一個基於xml的BeanDefinitionReader。
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

	// 設定上下文環境和資源載入器,環境主要根據profile進行配置
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	beanDefinitionReader.setResourceLoader(this);
	//設定xml解析工具
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

	// 在初始化裡呼叫了reader.setValidating(this.validating),其中this.validating預設為true,表示需要驗證xml檔案格式
	initBeanDefinitionReader(beanDefinitionReader);
	// 嘗試獲取配置檔案位置後,呼叫XmlBeanDefinitionReader內方法進行載入
	loadBeanDefinitions(beanDefinitionReader);
}

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
	Resource[] configResources = getConfigResources();
	if (configResources != null) {
		reader.loadBeanDefinitions(configResources);
	}
	String[] configLocations = getConfigLocations();
	if (configLocations != null) {
		reader.loadBeanDefinitions(configLocations);
	}
}

XmlBeanDefinitionReader的loadBeanDefinitions方法定義在抽象父類AbstractBeanDefinitionReader中:

public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {
    // 先呼叫這個方法    
	public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
		Assert.notNull(locations, "Location array must not be null");
		int counter = 0;
		//根據傳入的位置陣列遍歷載入,對載入的BeanDefinition個數進行累加返回
		for (String location : locations) {
			counter += loadBeanDefinitions(location);
		}
		return counter;
	}
	// 隨後呼叫這個方法
	public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(location, null);
	}
	
	//最後呼叫這個方法
	public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			// 根據根據模式配置載入路徑資源
			try {
			    // 解析資源通配拿到資源陣列
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				// 內部是一個for迴圈遍歷,最終呼叫int loadBeanDefinitions(Resource resource)方法
				int loadCount = loadBeanDefinitions(resources);
				if (actualResources != null) {
					for (Resource resource : resources) {
						actualResources.add(resource);
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
				}
				return loadCount;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			// 只能根據絕對路徑載入單個
			Resource resource = resourceLoader.getResource(location);
				// 具體載入操作,呼叫int loadBeanDefinitions(Resource resource)
			int loadCount = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
			}
			return loadCount;
		}
	}
	// 
	public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
		Assert.notNull(resources, "Resource array must not be null");
		int counter = 0;
		for (Resource resource : resources) {
			counter += loadBeanDefinitions(resource);
		}
		return counter;
	}
	
	// 最終呼叫方法入口,對資源進行編碼
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}
	// 傳入編碼後的資源,呼叫下面函式
	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());
		}

        // 初始載入為null,初始化一個HashSet,存入資源入參
		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);
				// 首次載入為null
				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();
			}
		}
	}
}

doLoadBeanDefinitions校驗、解析xml檔案流程

通過上面一系列的資源路徑檢索和檢查,最終進入了下面開始真正的載入資源的邏輯

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
		throws BeanDefinitionStoreException {
	try {
	    // 有3種驗證方式,VALIDATION_AUTO、VALIDATION_DTD、VALIDATION_XSD,如果為第一種,會根據xml檔案的頭部是DTD型別還是XSD型別格式檔案解析
		int validationMode = getValidationModeForResource(resource);
		/*
		    java載入xml文件的基本流程,封裝了以下步驟:
		    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		    DocumentBuilder builder = factory.newDocumentBuilder();
		    builder.setEntityResolver(entityResolver);
		    builder.parse(inputSource)
		*/
		Document doc = this.documentLoader.loadDocument(
				inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
		// 走到這一步,說明xml檔案的格式校驗,Java解析工作已完成,下面進一步解析xml檔案的具體內容,建立BeanDefinition。
		return registerBeanDefinitions(doc, resource);
	}
	catch (BeanDefinitionStoreException ex) {
		throw ex;
	}
	catch (SAXParseException ex) {
		throw new XmlBeanDefinitionStoreException(resource.getDescription(),
				"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
	}
	catch (SAXException ex) {
		throw new XmlBeanDefinitionStoreException(resource.getDescription(),
				"XML document from " + resource + " is invalid", ex);
	}
	catch (ParserConfigurationException ex) {
		throw new BeanDefinitionStoreException(resource.getDescription(),
				"Parser configuration exception parsing XML from " + resource, ex);
	}
	catch (IOException ex) {
		throw new BeanDefinitionStoreException(resource.getDescription(),
				"IOException parsing XML document from " + resource, ex);
	}
	catch (Throwable ex) {
		throw new BeanDefinitionStoreException(resource.getDescription(),
				"Unexpected exception parsing XML document from " + resource, ex);
	}
}

registerBeanDefinitions 解析文件建立BeanDefinition

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    // 建立文件讀取器DefaultBeanDefinitionDocumentReader進行解析文件
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	documentReader.setEnvironment(getEnvironment());
	// 獲取當前已經存在的BeanDefinition數量,便於後面解析完後再一次獲取數量,兩次相減得到這次解析的數量
	int countBefore = getRegistry().getBeanDefinitionCount();
	// 具體註冊操作
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	// 獲取這次解析的BeanDefinition數量
	return getRegistry().getBeanDefinitionCount() - countBefore;
}
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    	this.readerContext = readerContext;
    	logger.debug("Loading bean definitions");
    	// 獲取文件根節點
    	Element root = doc.getDocumentElement();
    	// 真正註冊流程
    	doRegisterBeanDefinitions(root);
    }
    
    protected void doRegisterBeanDefinitions(Element root) {
        // 判斷xml配置檔案中是否指定了環境
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		if (StringUtils.hasText(profileSpec)) {
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			if (!getEnvironment().acceptsProfiles(specifiedProfiles)) {
				return;
			}
		}

		// 建立委託解析物件,在解析xml過程中,預設為Beans名稱空間
		// 但實際可能會有多個名稱空間
		// 如<aop:cofnig>等,或巢狀的如<bean class = "xxx" p:arg1="xxx">
		// 對於非預設名稱空間的,會委託給BeanDefinitionParserDelegate進行解析。
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(this.readerContext, root, parent);

        // 解析前處理,留給子類拓展
		preProcessXml(root);
		// 具體解析操作
		parseBeanDefinitions(root, this.delegate);
		// 解析後處理,留給子類拓展
		postProcessXml(root);

		this.delegate = parent;
	}
    
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        // 如果根節點是預設名稱空間,直接走Beans名稱空間處理
		if (delegate.isDefaultNamespace(root)) {
			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)) {
						parseDefaultElement(ele, delegate);
					}
					else { 
					    // 對於巢狀的,由委託類遞迴解析
				        delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
		    // 交給委託物件處理自定義的名稱空間如aop,p等或使用者自定義的名稱空間
			delegate.parseCustomElement(root);
		}
	}
	// 預設的解析流程如下,如果節點是import,alias,beans,bead等型別,則呼叫相關的方法依次進行解析配置裡的屬性
	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標籤,核心解析邏輯
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		// 巢狀beans標籤
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}
	
	// 對於自定義的名稱空間,會走以下流程,先根據元素獲取名稱空間地址,再根據地址找到相應的配置檔案找到對應的對映處理器,再交由相關的處理器進行解析
	// 如<aop:xxx>則交給AopNamespaceHandler,<context:xxx>則交給ContextNamespaceHandler等。
	public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}
}

至此,xml配置檔案解析成相應的BeanDefinition流程如上所示,對於具體的將xml標籤裡的一個個諸如class,id等元素解析為具體的BeanDefinition屬性,中間流程過於繁雜,但原理相對還是比較清晰的,這裡就不細述了。