Spring XML Bean 定義的加載和註冊

前言

本篇文章主要介紹 Spring IoC 容器怎麼加載 bean 的定義元信息。java

下圖是一個大體的流程圖:node

第一次畫圖,畫的有點爛。😂git

正文

首先定義一個簡單的 POJO,以下:github

public class User {

	private Long id;
	private String name;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "User{" +
				"id=" + id +
				", name='" + name + '\'' +
				'}';
	}
}

再編寫一個 XML 文件。web

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="user" class="com.leisurexi.ioc.domain.User">
		<property name="id" value="1"/>
		<property name="name" value="leisurexi"/>
	</bean>
    
</beans>

最後再來一個測試類。spring

@Test
public void test(){
	DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
	XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
	reader.loadBeanDefinitions("META-INF/spring-bean.xml");
	User user = beanFactory.getBean("user", User.class);
	System.out.println(user);
}

上面這段代碼比較簡單,無非就是聲明 bean 工廠,而後經過指定的 XML 文件加載 bean 的定義元信息,最後經過 bean 工廠獲取 beanexpress

DefaultListableBeanFactory

首先咱們來了解一下 DefaultListableBeanFactory ,下面是該類的類圖及層次結構。數組

  • AliasRegistry:定義對 alias 的簡單增刪改等操做。
  • SimpleAliasRegistry:主要使用 map 做爲 alias 的緩存,並對接口 AliasRegistry 進行實現。
  • SingletonBeanRegistry:定義了對單例 bean 的註冊及獲取。
  • BeanFactory:定義獲取單個 beanbean 的各類屬性。
  • DefaultSingletonBeanRegistry:對接口 SingletonBeanRegistry 各函數的實現。
  • HierarchicalBeanFactory:繼承 BeanFactory,也就是在 BeanFactory 定義的功能的基礎上增長了對 parentBeanFactory 的支持。
  • BeanDefinitionRegistry:定義了對 BeanDefinition 的各類增刪改操做。
  • FactoryBeanRegistrySupport:DefaultSingletonBeanRegistry 基礎上增長了對 FactoryBean 的特殊處理功能。
  • ConfigurableBeanFactory:提供配置 BeanFactory 的各類方法。
  • ListableBeanFactory:繼承 BeanFactory 提供了獲取多個 bean 的各類方法。
  • AbstractBeanFactory:綜合 FactoryBeanRegistrySupportConfigurableBeanFactory 的功能。
  • AutowireCapableBeanFactory:提供建立 bean、自動注入、初始化以及應用 bean 的後處理器。
  • AbstractAutowireCapableBeanFactory:綜合 AbstractBeanFactory 並對接口 AutowireCapableBeanFactory 進行實現。
  • ConfigurableListableBeanFactory:BeanFactory 配置清單,指定忽略類型及接口等。
  • DefaultListableBeanFactory:綜合上面全部功能,主要是對 bean 註冊後的處理。

能夠看到上面的接口大多數是定義了一些功能或在父接口上擴展了一些功能,DefaultListableBeanFactory 實現了全部接口,大多數默認狀況下咱們所使用的 beanFactory 就是 DefaultListableBeanFactory緩存

1.AbstractBeanDefinitionReader#loadBeanDefinitions 方法

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
    // 獲取 resourceLoader,這邊是 PathMatchingResourcePatternResolver
	ResourceLoader resourceLoader = getResourceLoader();
	if (resourceLoader == null) {
		throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
	}
	// 判斷 resourceLoader 是不是 ResourcePatternResolver,咱們這邊是符合的
	if (resourceLoader instanceof ResourcePatternResolver) {
		// Resource pattern matching available.
		try {
            // 根據路徑獲取所欲符合的配置文件並封裝成 Resource 對象
			Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
            // 根據 Resource 加載 bean definition,並返回數量,見下面詳解
			int count = loadBeanDefinitions(resources);
			if (actualResources != null) {
				Collections.addAll(actualResources, resources);
			}
			if (logger.isTraceEnabled()) {
				logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
			}
			return count;
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"Could not resolve bean definition resource pattern [" + location + "]", ex);
		}
	}
	else {
		// Can only load single resources by absolute URL.
        // 只能經過絕對路徑加載單個資源
		Resource resource = resourceLoader.getResource(location);
        // 根據 Resource 加載 bean definition,並返回數量,見下面詳解
		int count = loadBeanDefinitions(resource);
		if (actualResources != null) {
			actualResources.add(resource);
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
		}
		return count;
	}
}

上面方法主要是將資源文件轉換爲 Resource 對象,而後調用 loadBeanDefinitions(Resource...) 加載 BeanDefinition安全

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
	Assert.notNull(resources, "Resource array must not be null");
	int count = 0;
	for (Resource resource : resources) {
        // 加載 BeanDefinition,見下文詳解
		count += loadBeanDefinitions(resource);
	}
	return count;
}

該方法主要就是遍歷 resources 而後調用 XmlBeanDefinitionReader#loadBeanDefinitions(Resource)

2.XmlBeanDefinitionReader#loadBeanDefinitions

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    // 將 Resource 封裝成 EncodedResource,也就是對資源指定編碼和字符集
	return loadBeanDefinitions(new EncodedResource(resource));
}


public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	Assert.notNull(encodedResource, "EncodedResource must not be null");
	if (logger.isTraceEnabled()) {
		logger.trace("Loading XML bean definitions from " + encodedResource);
	}
	// 當前正在加載的 EncodedResource
	Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
	if (currentResources == null) {
		currentResources = new HashSet<>(4);
		this.resourcesCurrentlyBeingLoaded.set(currentResources);
	}
    // 若是當前 EncodedResource 以及存在,表明出現了循環加載,拋出異常
	if (!currentResources.add(encodedResource)) {
		throw new BeanDefinitionStoreException(
				"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
	}
	try {
        // 獲取 Resource 的輸入流
		InputStream inputStream = encodedResource.getResource().getInputStream();
		try {
            // 將 inputStream 封裝成 org.xml.sax.InputSource
			InputSource inputSource = new InputSource(inputStream);
            // 若是 encodedResource 的編碼不爲空,設置 inputSource 的編碼
			if (encodedResource.getEncoding() != null) {
				inputSource.setEncoding(encodedResource.getEncoding());
			}
            // 加載 BeanDefinition (方法以 do 開頭,真正處理的方法)
			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
		}
		finally {
            // 關閉流
			inputStream.close();
		}
	}
	catch (IOException ex) {
		throw new BeanDefinitionStoreException(
				"IOException parsing XML document from " + encodedResource.getResource(), ex);
	}
	finally {
        // 當前資源以及加載完畢,從 currentResources 中移除
		currentResources.remove(encodedResource);
		if (currentResources.isEmpty()) {
			this.resourcesCurrentlyBeingLoaded.remove();
		}
	}
}

上面主要將 Resource 封裝成 EncodedResource ,也就是制定資源的編碼和字符集。而後獲取 Resource 的輸入流 InputStream ,並封裝成 InputSource 設置其編碼,最終調用 doLoadBeanDefinitions 開始真正的加載流程。

3.XmlBeanDefinitionReader#doLoadBeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
		throws BeanDefinitionStoreException {
	try {
        // 根據 inputSource 和 resource 加載 XML 文件,並封裝成 Document,見下文詳解
		Document doc = doLoadDocument(inputSource, resource);
        // 用 doc 去解析和註冊 bean definition,見下文詳解
		int count = registerBeanDefinitions(doc, resource);
		if (logger.isDebugEnabled()) {
			logger.debug("Loaded " + count + " bean definitions from " + resource);
		}
		return count;
	}
	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);
	}
}

上面代碼拋開異常處理,邏輯很是簡單,就是用 inputSourceresource 加載 XML 文件,並封裝成 Document 對象,而後去註冊 BeanDefinition

4.XmlBeanDefinitionReader#doLoadDocument

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
	return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
			getValidationModeForResource(resource), isNamespaceAware());
}

// 獲取 XML 文件的驗證模式
protected int getValidationModeForResource(Resource resource) {
    // 若是手動指定了驗證模式則使用指定的驗證模式
	int validationModeToUse = getValidationMode();
	if (validationModeToUse != VALIDATION_AUTO) {
		return validationModeToUse;
	}
    // 若是未指定則使用自動檢測,其實就是判斷文件是否包含 DOCTYPE,若是
	int detectedMode = detectValidationMode(resource);
	if (detectedMode != VALIDATION_AUTO) {
		return detectedMode;
	}
	// Hmm, we didn't get a clear indication... Let's assume XSD,
	// since apparently no DTD declaration has been found up until
	// detection stopped (before finding the document's root tag).
    // 若是沒有找到驗證,默認使用 XSD 模式,由於 DTD 已經不維護了
	return VALIDATION_XSD;
}

// DefaultDocumentLoader.java
// 這裏就是使用 DocumentLoader 去加載 XML 文件。首先建立 DocumentBuilderFactory,再經過 DocumentBuilderFactory 建立 DocumentBuilder,進而解析 inputSource 來返回 Document 對象
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
			ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

	DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
	if (logger.isTraceEnabled()) {
		logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
	}
	DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
	return builder.parse(inputSource);
}

detectValidationMode() 方法其實就是讀取文件內容,判斷是否包含 DOCTYPE,若是包含就是 DTD 不然就是 XSD。

獲取 XML 配置文件的驗證模式。XML 文件的驗證模式是用來保證 XML 文件的正確性,常見的驗證模式有 DTD 和 XSD。

DTD XML 格式示例:

STD XML 格式示例:

5.XmlBeanDefinitionReader#registerBeanDefinitions

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    // 獲取 DefaultBeanDefinitionDocumentReader
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    // 獲取註冊中心,再靠註冊中心獲取註冊以前以及註冊過的 BeanDefinition 數量
	int countBefore = getRegistry().getBeanDefinitionCount();
    // 解析並註冊 BeanDefinition,見下文詳情
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    // 獲取註冊事後 BeanDefinition 數量減去註冊以前的數量,獲得的就是本次註冊的數量
	return getRegistry().getBeanDefinitionCount() - countBefore;
}

這裏的 getRegistry() 方法返回的就是 DefaultListableBeanFactory ,由於就只有它實現了 BeanDefinitionRegistry 接口。

DefaultListableBeanFactory 中定義了存放 BeanDefinition 的緩存,因此 getBeanDefinitionCount() 方法返回的就是 beanDefinitionMap 的數量。

// 存放 BeanDefinition 的緩存,key 爲 bean 的名稱,value 就是其 BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

6.DefaultBeanDefinitionDoucumentReader#registerBeanDefinitions

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	this.readerContext = readerContext;
    // 提取 root,註冊 BeanDefinition (理論上 Spring 的配置文件,root 都應該是 beans 標籤)
	doRegisterBeanDefinitions(doc.getDocumentElement());
}

protected void doRegisterBeanDefinitions(Element root) {
	// Any nested <beans> elements will cause recursion in this method. In
	// order to propagate and preserve <beans> default-* attributes correctly,
	// keep track of the current (parent) delegate, which may be null. Create
	// the new (child) delegate with a reference to the parent for fallback purposes,
	// then ultimately reset this.delegate back to its original (parent) reference.
	// this behavior emulates a stack of delegates without actually necessitating one.
	BeanDefinitionParserDelegate parent = this.delegate;
    // 專門處理解析
	this.delegate = createDelegate(getReaderContext(), root, parent);
	// 校驗 root 節點的命名空間是否爲默認的命名空間 (默認命名空間http://www.springframework.org/schema/beans)
	if (this.delegate.isDefaultNamespace(root)) {
        // 處理 profile 屬性
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		if (StringUtils.hasText(profileSpec)) {
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
			profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			// We cannot use Profiles.of(...) since profile expressions are not supported
			// in XML config. See SPR-12458 for details.
            // 校驗當前節點的 profile 是否符合當前環境定義的,若是不是則直接跳過,不解析該節點下的內容
			if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
							"] not matching: " + getReaderContext().getResource());
				}
				return;
			}
		}
	}
	// 解析前處理,留給子類實現
	preProcessXml(root);
    // 解析註冊 BeanDefinition,見下文詳解
	parseBeanDefinitions(root, this.delegate);
    // 解析後處理,留給子類實現
	postProcessXml(root);

	this.delegate = parent;
}

profile 主要是用於多環境開發,例如:

集成到 Web 環境時,在 web.xml 中加入如下代碼:

<coontext-param>
	<param-name>Spring.profiles.active</param-name>
    <param-value>dev</param-value>
</coontext-param>

preProcessXml()postProcessXml() 採用的 模板方法模式,子類能夠繼承 DefaultBeanDefinitionDoucumentReader 來重寫這兩個方法,這也是解析先後的擴展點。

7.DefaultBeanDefinitionDoucumentReader#parseBeanDefinitions

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    // 校驗 root 節點的命名空間是否爲默認的命名空間,這裏爲何再次效驗,由於調用解析前調用了preProcessXml() 方法,可能會對節點作修改
	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)) {
                    // 默認命名空間節點的處理,例如 <bean id="..." class="..."/>
					parseDefaultElement(ele, delegate);
				}
				else {
                    // 自定義命名空間節點的處理,例如 <context:compoent-scan/>、<aop:aspectj-autoproxy>
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	else {
        // 自定義命名空間節點的處理
		delegate.parseCustomElement(root);
	}
}


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)) {
		doRegisterBeanDefinitions(ele);
	}
}

上面 parseDefaultElement 方法中對 bean 標籤的處理方法 processBeanDefinition 最爲重要,下面來着重分析一下。

7-1.DefaultBeanDefinitionDocumentReader#processBeanDefinition

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 將 ele 解析成 BeanDefinitionHolder,見下面詳解
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
        // 若存在默認標籤下的子節點下再也不有自定義屬性,須要再次對自定義標籤再進行解析(基本不用,不作深刻分析)
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		try {
			// Register the final decorated instance.
            // 註冊最終的 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.
        // 發出響應事件,通知相關監聽器,這個 bean 已經註冊完
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}

上面代碼主要步驟以下:

  1. Element 解析成 BeanDefinitionHolder
  2. 若存在默認標籤下的子節點下有自定義屬性,須要再次對自定義標籤再進行解析。
  3. 註冊 BeanDefinition
  4. 發出響應事件,通知相關監聽器,這個 bean 已經註冊完,具體詳情能夠查看 ReaderEventListener#componentRegistered() 方法。能夠經過如下方式去註冊這個監聽器:

7-1-1.BeanDefinitionParseDelegate#parseBeanDefinitionElement

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
    // 解析元素,封裝成 BeanDefinitionHolder
	return parseBeanDefinitionElement(ele, null);
}

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    // 獲取 id 屬性
	String id = ele.getAttribute(ID_ATTRIBUTE);
    // 獲取 name 屬性
	String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
	List<String> aliases = new ArrayList<>();
    // 將 name 屬性全部的名稱按照逗號或者分號(,;)分割成數組放入別名集合 aliases
	if (StringUtils.hasLength(nameAttr)) {
		String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
		aliases.addAll(Arrays.asList(nameArr));
	}
	// beanName 默認使用 id
	String beanName = id;
    // 沒有指定 id 屬性 && 指定了 name 屬性
	if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
        // 若是沒有指定id,beanName 等於第一個別名,剩下的依然做爲別名使用
		beanName = aliases.remove(0);
		if (logger.isTraceEnabled()) {
			logger.trace("No XML 'id' specified - using '" + beanName +
					"' as bean name and " + aliases + " as aliases");
		}
	}

	if (containingBean == null) {
        // 驗證 beanName 和 aliases 是否在同一個 <beans> 下已經存在
		checkNameUniqueness(beanName, aliases, ele);
	}
	// 將元素解析成 GenericBeanDefinition
	AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
	if (beanDefinition != null) {
        // 若是不存在 beanName 會根據 Spring 的命名規則生成一個
		if (!StringUtils.hasText(beanName)) {
			try {
				if (containingBean != null) {
					beanName = BeanDefinitionReaderUtils.generateBeanName(
							beanDefinition, this.readerContext.getRegistry(), true);
				}
				else {
					beanName = this.readerContext.generateBeanName(beanDefinition);
					// Register an alias for the plain bean class name, if still possible,
					// if the generator returned the class name plus a suffix.
					// This is expected for Spring 1.2/2.0 backwards compatibility.
					String beanClassName = beanDefinition.getBeanClassName();
					if (beanClassName != null &&
							beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
					!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
							aliases.add(beanClassName);
					}
				}
				if (logger.isTraceEnabled()) {
					logger.trace("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);
        // 用 beanDefinition 和 beanName 以及 aliasesArray 構建 BeanDefinitionHolder
		return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
	}
	return null;
}


public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, @Nullable BeanDefinition containingBean) {

	this.parseState.push(new BeanEntry(beanName));

	String className = null;
    // 獲取 class 屬性
	if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
		className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
	}
	String parent = null;
    // 獲取 parent 屬性
	if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
		parent = ele.getAttribute(PARENT_ATTRIBUTE);
	}

	try {
        // 建立用於承載屬性的 AbstractBeanDefinition 類型的 GenericBeanDefinition
		AbstractBeanDefinition bd = createBeanDefinition(className, parent);
		// 解析默認 bean 的各類屬性,見下方 parseBeanDefinitionAttributes 方法詳解
		parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        // 提取 description
		bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
		// 解析元數據,見下方 parseMetaElements 方法詳解
		parseMetaElements(ele, bd);
        // 解析 lookup-method 屬性,不多使用,不具體介紹
		parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
        // 解析 replaced-method 屬性,不多使用,不具體介紹
		parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
		// 解析 constructot-arg 屬性,見下方 parseConstructorArgElements 方法詳解
		parseConstructorArgElements(ele, bd);
        // 解析 property 屬性,見下方 parsePropertyElements 方法詳解
		parsePropertyElements(ele, bd);
        // 解析 qualifier 屬性,見下方 parseQualifierElements 方法詳解
		parseQualifierElements(ele, bd);
		
		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();
	}

	return null;
}

上面代碼主要將 bean 標籤,解析爲 BeanDefinitionHolder 返回,主要步驟以下:

  1. 解析 idname 屬性,將 name 按照 , 或者 ; 分割做爲別名 (alias)。
  2. 解析剩下的屬性,並封裝成 GenericBeanDefinition
    • 調用 parseBeanDefinitionAttributes 方法解析 bean 標籤的全部屬性。
    • 調用 parseMetaElements 方法解析元數據信息。
    • 調用 parseLookupOverrideSubElements 方法解析 lookup-method 子標籤。
    • 調用 parseReplacedMethodSubElements 方法解析 replaced-method 子標籤。
    • 調用 parseConstructorArgElements 方法解析 constructor-arg 子標籤。
    • 調用 parsePropertyElements 方法解析 property 子標籤。
    • 調用 parseQualifierElements 方法解析 qualifier 子標籤。
  3. 判斷 beanName 是否存在,不存在會根據 Spring 的命名規則生成一個。
  4. 使用 beanDefinitionbeanNamealiasesArray 構建 BeanDefinitionHolder 返回。

咱們這邊能夠簡單看一下 BeanDefinitionHolder 的屬性,以下:

public class BeanDefinitionHolder implements BeanMetadataElement {
	
    // bean 的定義元信息
	private final BeanDefinition beanDefinition;
	// bean 的名稱
	private final String beanName;
	// bean 的別名數組
	@Nullable
	private final String[] aliases;

    ...省略其它代碼
}

BeanDefinitionHolder 其實就是對 BeanDefinition 的包裝。

parseBeanDefinitionAttributes

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
			@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
	// 解析 singleton 屬性
	if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
        // singleton 屬性已經不支持了,使用了會直接拋出異常,請使用 scope 屬性替代
		error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
	}
    // 解析 scope 屬性
	else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
		bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
	}
	else if (containingBean != null) {
		// Take default from containing bean in case of an inner bean definition.
		bd.setScope(containingBean.getScope());
	}
	// 解析 abstract 屬性
	if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
		bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
	}
	// 解析 lazy 屬性
	String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
    // 若沒有設置或者設置成其餘字符都會被設置爲默認值 false
	if (isDefaultValue(lazyInit)) {
		lazyInit = this.defaults.getLazyInit();
	}
	bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
	// 解析 autowire 屬性
	String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
	bd.setAutowireMode(getAutowireMode(autowire));
	// 解析 depends-on 屬性
	if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
		String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
		bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
	}
	// 解析 autowire-candidate 屬性
	String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
	if (isDefaultValue(autowireCandidate)) {
		String candidatePattern = this.defaults.getAutowireCandidates();
		if (candidatePattern != null) {
			String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
			bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
		}
	}
	else {
		bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
	}
	// 解析 primary 屬性
	if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
		bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
	}
	// 解析 init-mehtod 屬性
	if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
		String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
		bd.setInitMethodName(initMethodName);
	}
    // 若是 beans 標籤指定了 default-init-method 屬性,則會給全部此標籤下的 bean 都指定該 init-method
	else if (this.defaults.getInitMethod() != null) {
		bd.setInitMethodName(this.defaults.getInitMethod());
		bd.setEnforceInitMethod(false);
	}
	// 解析 destory-method 屬性
	if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
		String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
		bd.setDestroyMethodName(destroyMethodName);
	}
    // 若是 beans 標籤指定了 default-destory-method 屬性,則會給全部此標籤下的 bean 都指定該 destory-method
	else if (this.defaults.getDestroyMethod() != null) {
		bd.setDestroyMethodName(this.defaults.getDestroyMethod());
		bd.setEnforceDestroyMethod(false);
	}
	// 解析 factory-method 屬性
	if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
		bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
	}
    // 解析 factory-bean 屬性
	if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
		bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
	}
	
	return bd;
}

上面方法完成了對全部 bean 標籤屬性的解析。值得注意的地方是若是同時指定了 bean 標籤的 init-methodbeans 標籤的 default-init-method 屬性,那麼優先使用前者,destory-mehtod 標籤也是同樣。

你們能夠去看一下 AbstractBeanDefinition 中定義的屬性就一目瞭然了,這裏限於篇幅緣由就不展現了。

parseMetaElements

這裏先回顧一下元數據 meta 屬性的使用。

這個屬性並不會體如今 user 的屬性當中,而是一個額外的聲明,當須要使用裏面的信息時能夠經過 BeanDefinition#getAttribute(key) 來獲取。

public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
    // 獲取全部子節點
	NodeList nl = ele.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
        // 提取 meta 
		if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
			Element metaElement = (Element) node;
			String key = metaElement.getAttribute(KEY_ATTRIBUTE);
			String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
            // 使用 key、value 構造 BeanMetadataAttribute
			BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
			attribute.setSource(extractSource(metaElement));
            // 記錄信息
			attributeAccessor.addMetadataAttribute(attribute);
		}
	}
}

parseConstructorArgElements

public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
    // 獲取全部子節點
	NodeList nl = beanEle.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
        // 提取 constructor-arg
		if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
            // 解析 constructor-arg
			parseConstructorArgElement((Element) node, bd);
		}
	}
}
// <constructor-arg index="0" type="" value=""/>
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
    // 提取 index 屬性
	String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
    // 提取 type 屬性
	String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
    // 提取 name 屬性
	String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    // index 不爲空
	if (StringUtils.hasLength(indexAttr)) {
		try {
            // 轉換爲 int 類型
			int index = Integer.parseInt(indexAttr);
			if (index < 0) {
				error("'index' cannot be lower than 0", ele);
			}
			else {
				try {
					this.parseState.push(new ConstructorArgumentEntry(index));
                    // 解析屬性值,見下面詳解
					Object value = parsePropertyValue(ele, bd, null);
                    // 使用 ConstructorArgumentValues.ValueHolder 類型來封裝解析出來的元素
					ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
                    // 若是 type 不爲空,設置 ValueHolder 的 type
					if (StringUtils.hasLength(typeAttr)) {
						valueHolder.setType(typeAttr);
					}
                    // 若是 name 不爲空,設置 ValueHolder 的 name
					if (StringUtils.hasLength(nameAttr)) {
						valueHolder.setName(nameAttr);
					}
					valueHolder.setSource(extractSource(ele));
                    // 判斷 index 是否重複,重複則拋出異常
					if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {							error("Ambiguous constructor-arg entries for index " + index, ele);
					}
                    // 將 index 和 valueHolder 以 key-value的形式 添加到 BeanDefinition 的 ConstructorArgumentValues 當中
					else {
						bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
					}
				}
				finally {
					this.parseState.pop();
				}
			}
		}
		catch (NumberFormatException ex) {
			error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
		}
	}
	else {
		try {
            // 這裏就是 constructor-arg 標籤中沒有指定 index 屬性
			this.parseState.push(new ConstructorArgumentEntry());
			Object value = parsePropertyValue(ele, bd, null);
			ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
			if (StringUtils.hasLength(typeAttr)) {
				valueHolder.setType(typeAttr);
			}
			if (StringUtils.hasLength(nameAttr)) {
				valueHolder.setName(nameAttr);
			}
			valueHolder.setSource(extractSource(ele));
            // 將 valueHolder 添加 BeanDefinition 的 GenericArgumentValue 中
            // 這裏和上面的 IndexedArgumentValue 相似,只不過上面是 Map,這裏是 List
			bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
		}
		finally {
			this.parseState.pop();
		}
	}
}

上面代碼首先提取 constructor-arg 標籤中必要的屬性 (indextypename)。

  • 若是指定了 index 屬性:
    1. 解析 constructor-arg 的子元素。
    2. 使用 ConstructorArgumentsValues.ValueHolder 類型來封裝解析出來的元素。
    3. typenameindex 屬性一併封裝在 ConstructorArgumentsValues.ValueHolder 類型中,並添加到當前 BeanDefinitionConstructorArgumentValues 中的 LinkedHashMap 類型的屬性indexedArgumentValues 中。
  • 若是有指定 index 屬性:
    1. 解析 constructor-arg 的子元素。
    2. 使用 ConstructorArgumentsValues.ValueHolder 類型來封裝解析出來的元素。
    3. typenameindex 屬性一併封裝在 ConstructorArgumentsValues.ValueHolder 類型中,並添加到當前 BeanDefinitionConstructorArgumentValues 中的 ArrayList 類型的屬性genericArgumentValues 中。

parsePropertyValue

public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
	String elementName = (propertyName != null ?
			"<property> element for property '" + propertyName + "'" :
			"<constructor-arg> element");

	// Should only have one child element: ref, value, list, etc.
    // 獲取全部子節點,例如 list、map等
	NodeList nl = ele.getChildNodes();
	Element subElement = null;
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
        // 跳過 description 或者 meta 不處理
		if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
				!nodeNameEquals(node, META_ELEMENT)) {
			// Child element is what we're looking for.
			if (subElement != null) {
				error(elementName + " must not contain more than one sub-element", ele);
			}
			else {
				subElement = (Element) node;
			}
		}
	}
	// 提取 ref 屬性
	boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
    // 提取 value 屬性
	boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
    // 若是同時有 ref 和 value 屬性 || 有 ref 或 value 屬性同時又有子元素,拋出異常
	if ((hasRefAttribute && hasValueAttribute) ||
			((hasRefAttribute || hasValueAttribute) && subElement != null)) {
		error(elementName +
				" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
	}
	// 只有 ref 屬性,使用 RuntimeBeanReference 封裝對應的 ref 名稱 (該 ref 值指向另外一個 bean 的 beanName)
    // RuntimeBeanReference 起到佔位符的做用, ref 指向的 beanName 將在運行時被解析成真正的 bean 實例引用
	if (hasRefAttribute) {
		String refName = ele.getAttribute(REF_ATTRIBUTE);
		if (!StringUtils.hasText(refName)) {
			error(elementName + " contains empty 'ref' attribute", ele);
		}
		RuntimeBeanReference ref = new RuntimeBeanReference(refName);
		ref.setSource(extractSource(ele));
		return ref;
	}
    // 只有 value 屬性,使用 TypedStringValue 封裝
	else if (hasValueAttribute) {
		TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
		valueHolder.setSource(extractSource(ele));
		return valueHolder;
	}
	else if (subElement != null) {
        // 解析子元素
		return parsePropertySubElement(subElement, bd);
	}
	else {
		// Neither child element nor "ref" or "value" attribute found.
        // 沒有子元素,也沒有 ref 和 value,直接拋出異常
		error(elementName + " must specify a ref or value", ele);
		return null;
	}
}

public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
	return parsePropertySubElement(ele, bd, null);
}

public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
    // 校驗是否爲默認的命名空間,若是不是則走解析自定義節點代碼
	if (!isDefaultNamespace(ele)) {
		return parseNestedCustomElement(ele, bd);
	}
    // 解析 bean 節點
	else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
		BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
		if (nestedBd != null) {
			nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
		}
		return nestedBd;
	}
    // 解析 ref 節點
	else if (nodeNameEquals(ele, REF_ELEMENT)) {
		// A generic reference to any name of any bean.
		String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
		boolean toParent = false;
		if (!StringUtils.hasLength(refName)) {
			// A reference to the id of another bean in a parent context.
			refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
			toParent = true;
			if (!StringUtils.hasLength(refName)) {
				error("'bean' or 'parent' is required for <ref> element", ele);
				return null;
			}
		}
		if (!StringUtils.hasText(refName)) {
			error("<ref> element contains empty target attribute", ele);
			return null;
		}
		RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
		ref.setSource(extractSource(ele));
		return ref;
	}
    // 解析 idref 節點
	else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
		return parseIdRefElement(ele);
	}
    // 解析 value 節點
	else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
		return parseValueElement(ele, defaultValueType);
	}
    // 解析 null 節點
	else if (nodeNameEquals(ele, NULL_ELEMENT)) {
		// It's a distinguished null value. Let's wrap it in a TypedStringValue
		// object in order to preserve the source location.
		TypedStringValue nullHolder = new TypedStringValue(null);
		nullHolder.setSource(extractSource(ele));
		return nullHolder;
	}
    // 解析 array 節點
	else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
		return parseArrayElement(ele, bd);
	}
    // 解析 list 節點
	else if (nodeNameEquals(ele, LIST_ELEMENT)) {
		return parseListElement(ele, bd);
	}
    // 解析 set 節點
	else if (nodeNameEquals(ele, SET_ELEMENT)) {
		return parseSetElement(ele, bd);
	}
    // 解析 map 節點
	else if (nodeNameEquals(ele, MAP_ELEMENT)) {
		return parseMapElement(ele, bd);
	}
    // 解析 props 節點
	else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
		return parsePropsElement(ele);
	}
    // 未知屬性,拋出異常
	else {
		error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
		return null;
	}
}

從上面的代碼來看,對構造函數中屬性元素的解析,步驟以下:

  1. 略過 description 或者 meta
  2. 提取 constructor-arg 上的 refvalue 屬性,以便於根據規則驗證正確性。其規則爲在 constructor-arg 上不存在一下狀況:
    • 同時存在 refvalue 屬性。
    • 存在 ref 或者 value 屬性,而且又有子元素。

parsePropertyElements

public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
    // 獲取全部子節點
	NodeList nl = beanEle.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
            // 解析 property 節點
			parsePropertyElement((Element) node, bd);
		}
	}
}

// 這裏是解析 property 標籤,<property name="..." value="..."/>
public void parsePropertyElement(Element ele, BeanDefinition bd) {
    // 獲取 name 屬性
	String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
    // name 爲空,拋出異常
	if (!StringUtils.hasLength(propertyName)) {
		error("Tag 'property' must have a 'name' attribute", ele);
		return;
	}
	this.parseState.push(new PropertyEntry(propertyName));
	try {
       	// 出現兩個 name 相同的拋出異常
		if (bd.getPropertyValues().contains(propertyName)) {
			error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
			return;
		}
        // 解析屬性值,跟構造器解析同樣,查看上方代碼
		Object val = parsePropertyValue(ele, bd, propertyName);
        // 用 name 和 val 封裝成 PropertyValue
		PropertyValue pv = new PropertyValue(propertyName, val);
        // 解析元數據,跟 beans 標籤內的 meta 同樣
		parseMetaElements(ele, pv);
		pv.setSource(extractSource(ele));
        // 添加到 BeanDefiniton 的 PropertyValues 屬性中
		bd.getPropertyValues().addPropertyValue(pv);
	}
	finally {
		this.parseState.pop();
	}
}

上面方法主要是遍歷 property 節點,而後解析屬性值封裝成 PropertyValue 添加到 BeanDefinitionPropertyValues 中。

注意:property 節點相似於 POJO 中的 set 方法,bean 中的屬性必需有 set 方法不然會拋出異常。

parseQualifierElements

public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
    // 獲取子節點
	NodeList nl = beanEle.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) {
            // 解析 qualifier 節點
			parseQualifierElement((Element) node, bd);
		}
	}
}

public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
    // 提取 type
	String typeName = ele.getAttribute(TYPE_ATTRIBUTE);
    // type 爲空拋出異常
	if (!StringUtils.hasLength(typeName)) {
		error("Tag 'qualifier' must have a 'type' attribute", ele);
		return;
	}
	this.parseState.push(new QualifierEntry(typeName));
	try {
		AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
		qualifier.setSource(extractSource(ele));
        // 提取 value
		String value = ele.getAttribute(VALUE_ATTRIBUTE);
        // value 不爲空,設置到 AutowireCandidateQualifier 的 attribute 中
		if (StringUtils.hasLength(value)) {
			qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);
		}
        // 獲取子節點
		NodeList nl = ele.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
            // 若是是有 attribute 節點,進行解析,提取值放入到 AutowireCandidateQualifier 的MetadataAttribute 中
			if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {
				Element attributeEle = (Element) node;
				String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);
				String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);
				if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
					BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
					attribute.setSource(extractSource(attributeEle));
					qualifier.addMetadataAttribute(attribute);
				}
				else {
					error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
					return;
				}
			}
		}
        // 設置 BeanDefinition 的 qualifier
		bd.addQualifier(qualifier);
	}
	finally {
		this.parseState.pop();
	}
}

對於 qualifier 元素的獲取,咱們大多數接觸的更可能是註解的形式,在使用 Spring 框架中進行自動注入時,Spring 容器中匹配的候選 Bean 必需有且只有一個。若是存在多個類型相同的 Bean,且按照類型注入時,Spirng 容許經過 Qualifier 指定注入 Bean 的名稱,這樣歧義就消除了。

7-1-2.BeanDefinitionReaderUtils#registerBeanDefinition

public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

	// Register bean definition under primary name.
    // 獲取 beanName
	String beanName = definitionHolder.getBeanName();
    // 以 key-value 的形式註冊,key 爲 beanName,value 爲 BeanDefinition。見下文詳解
	registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

	// Register aliases for bean name, if any.
    // 註冊 bean 的別名
	String[] aliases = definitionHolder.getAliases();
	if (aliases != null) {
		for (String alias : aliases) {
            // 以 key-value 的形式註冊 bean 的別名,key 爲別名,value 爲 beanName。見下文詳解
			registry.registerAlias(beanName, alias);
		}
	}
}

// DefaultListableBeanFactory.java
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");

	if (beanDefinition instanceof AbstractBeanDefinition) {
		try {
			// 驗證 Bean 的格式是否正確
			((AbstractBeanDefinition) beanDefinition).validate();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
					"Validation of bean definition failed", ex);
		}
	}

	BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
	// 這裏判斷 BeanDefinition 是否存在
	if (existingDefinition != null) {
		// 這裏就是若是 Bean 定義以及存在,判斷是否能夠覆蓋,默認是能夠的
		// Spring Boot 2.1開始這裏會手動設置 allowBeanDefinitionOverriding 的值爲 false
		if (!isAllowBeanDefinitionOverriding()) {
			throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
		}
		else if (existingDefinition.getRole() < beanDefinition.getRole()) {
			// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
			if (logger.isInfoEnabled()) {
				logger.info("Overriding user-defined bean definition for bean '" + beanName +
						"' with a framework-generated bean definition: replacing [" +
						existingDefinition + "] with [" + beanDefinition + "]");
			}
		}
		else if (!beanDefinition.equals(existingDefinition)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Overriding bean definition for bean '" + beanName +
						"' with a different definition: replacing [" + existingDefinition +
						"] with [" + beanDefinition + "]");
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("Overriding bean definition for bean '" + beanName +
						"' with an equivalent definition: replacing [" + existingDefinition +
						"] with [" + beanDefinition + "]");
			}
		}
        // 將 beanName 和 BeanDefinition 以 key-value 形式放入beanDefinitionMap 緩存中
		this.beanDefinitionMap.put(beanName, beanDefinition);
	}
	else {
		// bean 是否已經開始建立
		if (hasBeanCreationStarted()) {
			// Cannot modify startup-time collection elements anymore (for stable iteration)
			synchronized (this.beanDefinitionMap) {
                // 將 beanName 和 BeanDefinition 以 key-value 形式放入beanDefinitionMap 緩存中
				this.beanDefinitionMap.put(beanName, beanDefinition);
				// 這裏將 beanDefinitionNames 寫時複製一份,相似於 CopyOnWriteArrayList
				List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
				updatedDefinitions.addAll(this.beanDefinitionNames);
				updatedDefinitions.add(beanName);
				this.beanDefinitionNames = updatedDefinitions;
				// 從單例 Bean 註冊名稱列表中刪除當前 beanName
				removeManualSingletonName(beanName);
			}
		}
        // bean 不在建立狀態中
		else {
			// Still in startup registration phase
			this.beanDefinitionMap.put(beanName, beanDefinition);
			// 由於 ConcurrentHashMap 是無序的,這裏將 beanName 放入 ArrayList,記錄註冊順序
			this.beanDefinitionNames.add(beanName);
			// 從單例 Bean 註冊名稱列表中刪除當前 beanName
			removeManualSingletonName(beanName);
		}
		this.frozenBeanDefinitionNames = null;
	}
	// 若是存在相同的 beanName 的 BeanDefinition,或者 beanName 已經存在單例對象,則將該 beanName 對應的緩存信息、單例對象清除,由於這些對象都是由老的 BeanDefinition 建立的,須要被覆蓋掉。再用新的 BeanDefinition 來建立這些緩存和單例對象
	if (existingDefinition != null || containsSingleton(beanName)) {
		resetBeanDefinition(beanName);
	}
}

// SimpleAliasRegistry.java
public void registerAlias(String name, String alias) {
	Assert.hasText(name, "'name' must not be empty");
	Assert.hasText(alias, "'alias' must not be empty");
	synchronized (this.aliasMap) {
        // 若是別名和 beanName 相同,從緩存中移除
		if (alias.equals(name)) {
			this.aliasMap.remove(alias);
			if (logger.isDebugEnabled()) {
				logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
			}
		}
		else {
			String registeredName = this.aliasMap.get(alias);
            // 若是別名以及註冊過,直接返回
			if (registeredName != null) {
				if (registeredName.equals(name)) {
					// An existing alias - no need to re-register
					return;
				}
                // 若是不容許覆蓋,拋出異常
				if (!allowAliasOverriding()) {
					throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
							name + "': It is already registered for name '" + registeredName + "'.");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
							registeredName + "' with new target name '" + name + "'");
				}
			}
            // 檢查 name 和 alias 是否存在循環引用。例如 A 的別名爲 B,B的別名爲A
			checkForAliasCircle(name, alias);
            // 將 alias 和 name 以 key-value 對放入到 aliasMap 中,進行緩存
			this.aliasMap.put(alias, name);
			if (logger.isTraceEnabled()) {
				logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
			}
		}
	}
}

上面代碼有兩個變量比較重要 beanDefinitionMapbeanDefinitionNames,下面代碼是這兩個屬性在 DefaultListableBeanFactory 中的定義:

/** Map of bean definition objects, keyed by bean name. */
// 緩存 BeanDefinition 的 Map,key 爲 beanName,value 爲 BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 保存 BeanDefinition 的註冊順序,保存的是 beanName
/** List of bean definition names, in registration order. */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
  1. 若是 BeanDefinitionAbstractBeanDefinition 類型,驗證 Bean 的格式是否正確。

    此次效驗主要是對於 AbstractBeanDefinition 屬性中的 methodOverrides 的校驗,校驗 methodOverrides 是否與 工廠方法 並存或者 methodOverrides 中的方法根本不存在。

  2. 判斷該 beanNameBeanDefinition 是否已經註冊過;若是存在判斷是否容許覆蓋,容許的話直接替換,不容許直接拋出異常。

    默認的狀況下是容許的,可是在 Spring Boot 2.1 開始這裏會手動的設置爲不容許。

  3. beanName 對應的 BeanDefinition 之前沒有註冊過,判斷 bean 是否已經開始建立;若是在建立中對 beanDefinitionMap 進行加鎖 (這裏雖然 beanDefinitionMap 是線程安全的 ConcurrentHashMap ,單個操做是線程安全的但多個操做不是,因此這裏手動加鎖),而後將 beanNameBeanDefinitionkey-value 形式放入 beanDefinitionMap 緩存中,而後寫時複製一份 beanDefinitionNames ,將 beaName 緩存進去,記錄 bean 的註冊順序;若是不在建立直接將 BeanDefinitionbeanName 分別放入 beanDefinitionMapbeanDefinitionNames 中。

  4. 最後判斷若是 BeanDefinition 已經註冊過,或者 beanName 已經存在單例對象,則將該 beanName 對應的緩存信息、單例對象清除,由於這些對象都是由老的 BeanDefinition 建立的,須要被覆蓋掉。再用新的 BeanDefinition 來建立這些緩存和單例對象。

總結

本文主要介紹了經過 XML 文件的方式註冊 Bean,咱們能夠從新梳理一下思路:

  1. 解析 XML 文件,構建成 AbstractBeanDefinition (GenericBeanDefinition) 對象來存放全部解析出來的屬性。

  2. AbstractBeanDefinitionbeanNamealiasesArray 構建成 BeanDefinitionHolder 對象。

  3. 最後經過 BeanDefinitionHolderbeanNameBeanDefinition 註冊到 DefaultListableBeanFactory 中,也就是保存起來。

    上文提到的兩個比較重要的屬性 beanDefinitionNamesbeanDefinitionMap ,在後面都會屢次用到,能夠重點關注一下。

最後,我模仿 Spring 寫了一個精簡版,代碼會持續更新,如今是 0.0.1 版本。地址:https://github.com/leisurexi/tiny-spring。訪問新博客地址,觀看效果更佳 https://leisurexi.github.io/

參考

相關文章
相關標籤/搜索