Spring源碼解讀 - IOC xml配置解析(一)- bean標籤的解析

一、前言

最近在看spring源碼,發現之前看的很多細節已經忘了,於是決定在看源碼的過程中也把主要的流程用博客記載下來,希望自己能堅持下來吧。

spring已經發展很久,整個體系已經變得很龐大了。爲了能更好的把源碼看下去,我決定從最基礎也是最核心的IOC開始切入,並且從最原始的xml解析開始看。面對這樣一個龐大的體系,我認爲從最原始的方式開始學習,才能更好的看懂它的設計和實現思路。

這一系列文章會默認你對於spring的使用已經熟悉,並且不抗拒讀源碼。因爲很多的文字會在源碼片段上註釋-對於源碼解析的文章,我暫時也找不到更好的表述方法了。

二、一個簡單的示例

首先我們配置一個bean

<bean class="com.xiaoxizi.spring.service.AccountServiceImpl" 
      id="accountService" scope="singleton" primary="true"/>

對應的類:

public class AccountServiceImpl implements AccountService {
    @Override
    public String queryAccount(String id) {
        return null;
    }
}

測試類:

@Test
public void test1() {
    applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    AccountService bean = applicationContext.getBean(AccountService.class);
    System.out.println(bean);
}

運行結果:

com.xiaoxizi.spring.service.AccountServiceImpl@3e78b6a5

三、源碼解析

1. beanDefinition註冊流程

我們知道,spring容器啓動的邏輯在refresh()方法裏面。所以,話不多說,直接點進refresh()邏輯,具體位置是 org.springframework.context.support.AbstractApplicationContext#refresh

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh();
        // 本篇博文主要講這個邏輯
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);
        ...
}

本篇博文主要講xml解析的邏輯,暫時我們只關注 obtainFreshBeanFactory()

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    return getBeanFactory();
}

繼續往下跟refreshBeanFactory(),實際上方法位置在org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory

@Override
protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        // 創建一個beanFactory
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory);
        // 加載所有的BeanDefinitions,實際解析xml的位置
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

繼續往下 org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // 這裏使用了委託模式,把BeanDefinition的解析委託給了 BeanDefinitionReader
    // 由於我們當前是解析xml,所以是委託給Xml...Reader。合理想象,註解方式將會委託給Anno...Reader
    // 需要注意的是,我們把beanFactory引用傳遞給了Reader,之後會用到
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
	// ... 爲BeanDefinitionReader設置了一些不重要的屬性,略過。
    // 加載BeanDefinition
    loadBeanDefinitions(beanDefinitionReader);
}
// XmlBeanDefinitionReader對應構造器,注意 beanFactory 是作爲 BeanDefinitionRegistry 傳入的
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
    super(registry);
}

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
        // 可以看到,是委託給Reader來加載BeanDefinition的
        reader.loadBeanDefinitions(configResources);
    }
    // 配置文件位置實際上就是我們 new ClassPathXmlApplicationContext("xxx") 時傳入的
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        // 可以看到,是委託給Reader來加載BeanDefinition的
        reader.loadBeanDefinitions(configLocations);
    }
}

經過一系列解析、包裝、加載邏輯之後… org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    throws BeanDefinitionStoreException {

    try {
        // 配置文件的輸入流被加載成了Document --> XML解析知識,詳細可搜素 SAX解析
        Document doc = doLoadDocument(inputSource, resource);
        // 解析並註冊BeanDefinitions
        int count = registerBeanDefinitions(doc, resource);
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + count + " bean definitions from " + resource);
        }
        return count;
    }
	// 異常處理,省略...
}

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    // 又來了,熟悉的委託模式,Document的解析被委託給了BeanDefinitionDocumentReader
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    // 委託documentReader解析註冊BeanDefinition,注意這裏傳入了一個 XmlReaderContext
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}
// 可以看到XmlReaderContext的構造器傳入了當前類-XmlBeanDefinitionReader
// 而當前類持有BeanDefinitionRegistry,所以XmlReaderContext中持有了一個BeanDefinitionRegistry
public XmlReaderContext createReaderContext(Resource resource) {
    return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                                this.sourceExtractor, this, getNamespaceHandlerResolver());
}

細看DocumentReader解析過程 org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions

protected void doRegisterBeanDefinitions(Element root) {
    // 代理的邏輯,我們暫時不看
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);
    if (this.delegate.isDefaultNamespace(root)) {
        // ...
    }
	// 解析xml之前的鉤子,暫時是空實現
    preProcessXml(root);
    // 解析邏輯
    parseBeanDefinitions(root, this.delegate);
    // 解析xml之前的鉤子,暫時是空實現
    postProcessXml(root);
    this.delegate = parent;
}

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    // 判斷是否是默認的命名空間
    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 {
                   	// 可以看到代理主要進行自定義標籤的解析 - CustomElement
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        // 可以看到代理主要進行自定義標籤的解析 - CustomElement
        delegate.parseCustomElement(root);
    }
}

這裏解釋一下 自定義標籤、自定義命名空間、默認標籤、默認命名空間的含義

<!-- 標籤前面有 xxx:即是spring的自定義標籤,我們也可以自己定義一個xiaozize:的標籤-之後會講到 -->
<context:component-scan base-package="com.xiaoxizi.spring"/>
<!-- 該標籤對應的命名空間在xml文件頭部beans標籤中聲明 -->
<beans xmlns:context="http://www.springframework.org/schema/context" ... />

<!-- 默認標籤沒有 xx: 前綴 -->
<bean class="com.xiaoxizi.spring.service.AccountServiceImpl" 
      id="accountService" scope="singleton" primary="true"/>
<!-- 對應的命名空間也在xml文件頭部beans標籤中聲明 -->
<beans xmlns="http://www.springframework.org/schema/beans" ... />

我們先看默認標籤的解析過程

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    // 解析import標籤,其實就是一個遞歸解析import導入的xml的過程
    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);
    }
}

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 具體的解析過程,將會把bean標籤解析並封裝到BeanDefinition中
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        // 對bean標籤解析出來的BeanDefinition進行裝飾,用的很少
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // Register the final decorated instance.
            // 註冊BeanDefinition
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        ...
    }
}

2. bean標籤解析

再正真解析bean標籤前,我們先看一下springbean標籤都有哪些屬性和默認子標籤

<bean
      class="com.xiaoxizi.spring.service.AccountServiceImpl"
      id="accountService"
      name="aaa"
      scope="singleton"
      abstract="false"
      parent="parent"
      autowire="byType"
      autowire-candidate="true"
      primary="true"
      depends-on="depends"
      init-method="init"
      destroy-method="destroy"
      factory-bean="factoryBean"
      factory-method="factoryMethod"
      lazy-init="false"
      >
    <description>一些描述</description>
    <constructor-arg ref="bean" value="固定值" type="參數類型" name="參數名稱" index="索引"/>
    <property name="key1" value="固定值" ref="beanRef"/>
    <meta key="key1" value="固定值"/>
    <qualifier type="bean類型" value="限定的bean的名稱"/>
    <lookup-method name="方法名" bean="bean名稱"/>
    <replaced-method name="方法名" replacer="bean名稱">
        <arg-type>參數類型,用於缺人唯一的方法</arg-type>
    </replaced-method>
</bean>

逐一說一下bean標籤中屬性作用:

屬性 作用
class 指明bean所屬的類
id bean在ioc容器中的唯一標識,如果不填將取別名中的第一個
name bean的別名
scope bean的scope,一般日常開發都是使用默認的singleton單例,還有prototype多例。事實上web環境還有request和session。而且我們也可以自定義scope(之後會講到的)
abstract 是否是抽象的,抽象的bean不會被實例化,只能被繼承,用的很少
parent 指定父bean,可以結合abstract一起使用,當然parent指向的bean並不一定要是抽象的
autowire 被自動裝配的模式,有byType,byName等可選
autowire-candidate 是否能被其他bean自動裝配,false的話該bean將不能被其他bean注入,讀者可以自行嘗試一下
primary 如果自動裝配時匹配到多個bean,標記爲primary的bean將被優先注入。
depends-on 依賴,依賴的bean將會先被實例化
init-method bean實例化之後將會調用的方法
destroy-method bean銷燬時將會調用的方法,需要主要的是,只有單例的bean,IOC容器才持有其引用,IOC容器銷燬 --> bean銷燬時纔會觸發這個方法。
factory-bean 工廠bean的名稱,需要與factory-method結合使用,創建bean時將會調用factory-bean.factory-method()來獲取當前類實例。
factory-method 工廠bean方法,其實@Bean註解就是通過factory-bean、factory-method屬性的功能實現的。
lazy-init 是否是懶加載的

bean標籤的默認子標籤的作用:

子標籤 作用
description 沒啥作用,就是個描述而已
constructor-arg 構造器注入時使用的標籤,標明每個參數需要的值。用得少,因爲可能會導致不能處理的循環依賴
property 爲bean中的屬性注入值,常用
meta bean的元數據信息,業務開發中比較少用,之後跟源碼過程中能看到使用的地方
qualifier 與@Qualifier作用一致,當注入時出現多個匹配的bean時,將會注入qualifier限定的bean
lookup-method 可以理解爲覆蓋/重寫方法,把當前bean中的指定方法委託給指定的bean執行(例:把當前beanA.test() 方法委託給 beanB.test(),方法名必須一致),應用場景比較少。
replaced-method 與lookup-method類似,只是該標籤多了子標籤用來準確定位方法,當待委託的方法有多個重名方法(重載)時可以使用。

繼續往下看解析過程org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    String id = ele.getAttribute(ID_ATTRIBUTE);
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
	// 處理別名
    List<String> aliases = new ArrayList<>();
    if (StringUtils.hasLength(nameAttr)) {
        String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        aliases.addAll(Arrays.asList(nameArr));
    }
	// 默認id爲beanName
    String beanName = id;
    if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
        // 如果不配置id,將會取第一個別名當做beanName
        beanName = aliases.remove(0);
        // ...
    }

    if (containingBean == null) {
        // 校驗beanName、alias是否重複
        // 實際上有一個set用來存所以用過的name,避免重複 BeanDefinitionParserDelegate#usedNames
        checkNameUniqueness(beanName, aliases, ele);
    }
	// 要解析元素了,解析xml獲取一個beanDefinition
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    if (beanDefinition != null) {
        // ... 跳過一些邏輯
        String[] aliasesArray = StringUtils.toStringArray(aliases);
        // 把beanDefinition封裝成Holder
        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }
    return null;
}

解析方法parseBeanDefinitionElement中我們將把xml bean標籤中的信息解析並封裝到 一個BeanDefinition中,而之後(初始化流程),我們將會根據BeanDefinition中的屬性來創建bean實例。現在,先讓我們看一下這個BeanDefinition的結構:

// 默認使用的是 GenericBeanDefinition
public class GenericBeanDefinition extends AbstractBeanDefinition {
    // 這個子類中只有一個parentName屬性,明顯對應bean標籤的parent屬性
    @Nullable
    private String parentName;
}
// 找父類,基本可以看到屬性和bean標籤的內容是一一對應的,不過meta子標籤的信息還在父類裏面
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
		implements BeanDefinition, Cloneable {
    @Nullable
	private volatile Object beanClass;

	@Nullable
	private String scope = SCOPE_DEFAULT;

	private boolean abstractFlag = false;

	@Nullable
	private Boolean lazyInit;

	private int autowireMode = AUTOWIRE_NO;

	private int dependencyCheck = DEPENDENCY_CHECK_NONE;

	@Nullable
	private String[] dependsOn;

	private boolean autowireCandidate = true;

	private boolean primary = false;
	// qualifier子標籤信息
	private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();

	@Nullable
	private Supplier<?> instanceSupplier;

	private boolean nonPublicAccessAllowed = true;

	private boolean lenientConstructorResolution = true;

	@Nullable
	private String factoryBeanName;

	@Nullable
	private String factoryMethodName;
	// constructor-arg 子標籤的信息
	@Nullable
	private ConstructorArgumentValues constructorArgumentValues;
	// property子標籤的信息
	@Nullable
	private MutablePropertyValues propertyValues;
	// lookup-method、replaced-method子標籤的信息
	private MethodOverrides methodOverrides = new MethodOverrides();
	
	@Nullable
	private String initMethodName;

	@Nullable
	private String destroyMethodName;
    
    @Nullable
	private String description;
	// 加載這個beanDefinition的資源 -> 哪個xml
	@Nullable
	private Resource resource;
}
// BeanMetadataAttributeAccessor extends AttributeAccessorSupport
public abstract class AttributeAccessorSupport implements AttributeAccessor, Serializable {
	// 保存beanDefinition的元數據信息
	/** Map with String keys and Object values. */
	private final Map<String, Object> attributes = new LinkedHashMap<>();
}

好,我們繼續往下解析bean標籤

public AbstractBeanDefinition parseBeanDefinitionElement(
    Element ele, String beanName, @Nullable BeanDefinition containingBean) {
	// ...
    // 獲取class屬性
    String className = null;
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
        className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }
    // 獲取parent屬性
    String parent = null;
    if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
        parent = ele.getAttribute(PARENT_ATTRIBUTE);
    }

    try {
        // 創建了一個BeanDefinition,感興趣的同學可以跟一下,實際上就是創建了一個
        // GenericBeanDefinition 並且把 parent set 進去了
        AbstractBeanDefinition bd = createBeanDefinition(className, parent);
		// 解析bean標籤上的屬性 scope, autowrite等
        // 感興趣的同學可以跟一下,就是把值從xml中解析出來塞到beanDefinition而已
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        // description屬性
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
		
        // 解析meta子標籤
        parseMetaElements(ele, bd);
        // 解析lockup-method子標籤
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
        // 解析replaced-method子標籤
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
		// 解析constructor-arg子標籤
        parseConstructorArgElements(ele, bd);
        // 解析property子標籤
        parsePropertyElements(ele, bd);
        // 解析qualifier子標籤
        parseQualifierElements(ele, bd);

        bd.setResource(this.readerContext.getResource());
        bd.setSource(extractSource(ele));

        return bd;
    } 
	// ...
    return null;
}

// 因爲解析的流程其實都差不多,這邊簡單挑幾個有代表性的看一下
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
    NodeList nl = ele.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        // 循環所有bean標籤的子標籤,找到mate標籤
        if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
            Element metaElement = (Element) node;
            String key = metaElement.getAttribute(KEY_ATTRIBUTE);
            String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
            // 封裝成BeanMetadataAttribute
            BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
            attribute.setSource(extractSource(metaElement));
            // 記住我們的beanDefinition是繼承BeanMetadataAttributeAccessor的
            // 所以這裏其實也是放到beanDefinition中了
            attributeAccessor.addMetadataAttribute(attribute);
        }
    }
}

public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
    NodeList nl = beanEle.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        // 循環所有bean標籤的子標籤,找到lockup-method標籤
        if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
            Element ele = (Element) node;
            String methodName = ele.getAttribute(NAME_ATTRIBUTE);
            String beanRef = ele.getAttribute(BEAN_ELEMENT);
            // 封裝成LookupOverride
            LookupOverride override = new LookupOverride(methodName, beanRef);
            override.setSource(extractSource(ele));
            overrides.addOverride(override);
        }
    }
}
// 我們這裏看一下MethodOverrides的結構
// 首先MethodOverrides是在beanDefinition創建的時候就初始化的
private MethodOverrides methodOverrides = new MethodOverrides();
public class MethodOverrides {
	// 其實就是一個MethodOverride列表
	private final Set<MethodOverride> overrides = new CopyOnWriteArraySet<>();
	// ...
}
// 繼續看看MethodOverride
public abstract class MethodOverride implements BeanMetadataElement {
	// 被代理/重寫的方法名
	private final String methodName;
	// 是否是重載的方法 - 重載的方法處理起來要複雜點
	private boolean overloaded = true;
	// ...
}
// MethodOverride只有兩個子類,LookupOverride 和 ReplaceOverride,看名字大家都知道對應哪個標籤了
public class LookupOverride extends MethodOverride {
    // 提供重寫邏輯的bean的名稱
	private final String beanName;
    // 可以看到,這個屬性我們在解析xml的時候沒有用到。
    // 這個應該是用來支持註解@Lockup的,因爲這個功能用的很少,我也沒去深究,不過字段的含義還是很好理解的
	private Method method;
}
public class ReplaceOverride extends MethodOverride {
	// 提供重寫邏輯的bean的名稱
	private final String methodReplacerBeanName;
	// 之前有說過replaced-method是用於目標方法有重載的情況,這個參數類型列表就是用來區分重載的方法的
    // 也是replaced-method標籤的子標籤arg-type中定義的
	private List<String> typeIdentifiers = new LinkedList<>();
}

那麼致此,我們的一個默認的bean標籤就解析完畢了,並且把所有的信息封裝到了一個BeanDefinition實例中,然後這個beanDefinition將會註冊到我們的IOC容器中去,爲下一步生成實例做準備。org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 具體的解析過程,將會把bean標籤解析並封裝到BeanDefinition中
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        // 對bean標籤解析出來的BeanDefinition進行裝飾,用的很少,但此處的是個spi很重要
        // 這裏下一期再講
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // Register the final decorated instance.
            // 註冊BeanDefinition
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        ...
    }
}

我們主要看一下注冊beanDefinition的過程

public static void registerBeanDefinition(
    BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
    throws BeanDefinitionStoreException {
    String beanName = definitionHolder.getBeanName();
    // 註冊bean
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            // 註冊別名
            registry.registerAlias(beanName, alias);
        }
    }
}

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    throws BeanDefinitionStoreException {
	// ... 這裏去除掉了大部分的分支判斷和異常處理邏輯,有興趣的同學可以自行看一下
    // registerBeanDefinition的主要邏輯其實是以下兩段
    // 將當前beanDefinition放入兩個容器
    this.beanDefinitionMap.put(beanName, beanDefinition);
    this.beanDefinitionNames.add(beanName);
    // 這裏是把當前bean從 manualSingletonNames 中刪除,簡單看了下邏輯
    // 對於通過 DefaultListableBeanFactory#registerSingleton(String beanName, Object singletonObject)
    // 直接註冊到IOC容器中的單例bean,因爲沒有對應的beanDefinition,name相應的beanName會被記錄到這個set
    // 而如果我們解析xml中獲取到了相應的beanDefinition,就會將其從set中移除
    // 這個邏輯可以不看,不是主流程
    removeManualSingletonName(beanName);
}

四、總結

spring xml bean標籤的解析就完整了,其實簡單來講,就是通過一系列手段,拿到xml bean標籤中配置的各種屬性,封裝成一個BeanDefinition對象,然後把這個對象存到我們IOC容器的 beanDefinitionMapbeanDefinitionNames中。這兩個容器在之後的bean實例創建的過程中將會用到。

第一次寫這種源碼類的博客(其實算是第一次寫博客? 發現自己幾年前居然寫過,emmm…),感覺寫的很乾,希望自己能堅持下來並且有進步吧。

下一篇將會講spring 自定義標籤的解析以及應用~

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