【spring 源碼】IOC 之bean實例的建立

在對refreshBeanFactory()詳細解析以前,先來看看spring的applicationContext的繼承樹: java

這裏有兩個重要的接口,一個是BeanFactory,顧名思義是bean的工廠、容器,bean建立好了就回放入這個容器裏,咱們要用的bean實例都是從bean工廠提供的。另外一個接口是ResourceLoader,它的實現類實現了讀取配置文件,將配置封裝成bean實例,而後將bean放入bean工廠。

BeanFactory 簡介

BeanFactory繼承樹
ApplicationContext 繼承了 ListableBeanFactory,這個 Listable 的意思就是,經過這個接口,咱們能夠獲取多個 Bean,最頂層 BeanFactory 接口的方法都是獲取單個 Bean 的。 ApplicationContext 繼承了 HierarchicalBeanFactory,Hierarchical 單詞自己已經能說明問題了,也就是說咱們能夠在應用中起多個 BeanFactory,而後能夠將各個 BeanFactory 設置爲父子關係。 AutowireCapableBeanFactory 這個名字中的 Autowire 你們都很是熟悉,它就是用來自動裝配 Bean 用的,可是仔細看上圖,ApplicationContext 並無繼承它,不過不用擔憂,不使用繼承,不表明不可使用組合,若是你看到 ApplicationContext 接口定義中的最後一個方法 getAutowireCapableBeanFactory() 就知道了。 ConfigurableListableBeanFactory 也是一個特殊的接口,看圖,特殊之處在於它繼承了第二層全部的三個接口,而 ApplicationContext 沒有。這點以後會用到。

ResourceLoader簡介

ResourceLoader並不能將其當作是Spring獨有的功能,spring Ioc只是藉助於ResourceLoader來實現資源加載。也提供了各類各樣的資源加載方式:node

  • DefaultResourceLoader 首先檢查資源路徑是否以classpath:前綴打頭,若是是,則嘗試構造ClassPathResource類 型資源並返回。不然, 嘗試經過URL,根據資源路徑來定位資源spring

  • FileSystemResourceLoader 它繼承自Default-ResourceLoader,但覆寫了getResourceByPath(String)方法,使之從文件系統加載資源並以 FileSystemResource類型返回數組

    • ResourcePatternResolver 批量查找的ResourceLoader

spring與ResourceLoader之間的關係緩存

全部ApplicationContext的具體實現類都會直接或者間接地實現AbstractApplicationContext,AbstactApplicationContext 依賴了了DeffaultResourceLoader, ApplicationContext 繼承了ResourcePatternResolver,所到頭來ApplicationContext的具體實現類都會具備DefaultResourceLoader 和 PathMatchingResourcePatterResolver的功能。這也就是會什麼ApplicationContext能夠實現統一資源定位。bash

接下來進入正題: refreshBeanFactory() 刷新beanfactory // AbstractRefreshableApplicationContext.java - 120app

@Override
protected final void refreshBeanFactory() throws BeansException {
   // 若是 ApplicationContext 中已經加載過 BeanFactory 了,銷燬全部 Bean,關閉 BeanFactory
   // 注意,應用中 BeanFactory原本就是能夠多個的,這裏可不是說應用全局是否有 BeanFactory,而是當前ApplicationContext 是否有 BeanFactory
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      // 初始化一個 DefaultListableBeanFactory,爲何用這個,咱們立刻說。
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      // 用於 BeanFactory 的序列化
      beanFactory.setSerializationId(getId());
      
      // 設置 BeanFactory 的兩個配置屬性:是否容許 Bean定義覆蓋、是否容許bean之間的循環引用
      //bean定義覆蓋的屬性默認是true,重複了會直接覆蓋
      customizeBeanFactory(beanFactory);
      // 加載 Bean 到 BeanFactory 中
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   } catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}
複製代碼

整個過程主要分爲了三個步驟:ide

1.resource定位

 2.beanDefinition的載入。

 3.向註冊中心註冊beanDefinition的過程(將beanDefinition放入緩存)。
複製代碼

resource定位

AbstractXmlApplicationContext#loadBeanDefinitions(beanFactory) 加載 Bean:

// AbstractXmlApplicationContext.java L80post

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // 給這個 BeanFactory 實例化一個 XmlBeanDefinitionReader
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
 
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
 
   // 初始化 BeanDefinitionReader,其實這個是提供給子類覆寫的,
   initBeanDefinitionReader(beanDefinitionReader);
   // 重點來了,繼續往下
   loadBeanDefinitions(beanDefinitionReader);
}
複製代碼

讀取配置的操做在 XmlBeanDefinitionReader 中,其負責加載配置、解析。ui

// AbstractXmlApplicationContext.java 120

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    //若是調用的構造器是new ClassPathXmlApplicationContext(String path, Class<?> clazz),獲取其路徑下的資源
   Resource[] configResources = getConfigResources();
   if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
   }
   //若是構造器調用的的是 new ClassPathXmlApplicationContext(String configLocation),獲取其資源
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
   }
}
複製代碼

這裏兩個分支都是在加載bean實例,過程相似,只分析reader.loadBeanDefinitions(configResources);

// AbstractBeanDefinitionReader.java L177

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
	Assert.notNull(resources, "Resource array must not be null");
	int counter = 0;
	for (Resource resource : resources) {
	    //這裏用了典型的模板模式,實際調用的是XmlBeanDefinitionReader#loadBeanDefinitions
		counter += loadBeanDefinitions(resource);
	}
	return counter;
}
複製代碼
XmlBeanDefinitionReader

// XmlBeanDefinitionReader.java L314

/**
	 * Load bean definitions from the specified XML file.
	 * 從xml里加載bean實例
	 */
	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		...
		//從threadlocal中獲取資源set
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<EncodedResource>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		//將resource放入setif (!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());
				}
				//此處調用388行的doLoadBeanDefinitions()
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				inputStream.close();
			}
		} 
		//此處省略處理異常、清楚鏈表和threadload相關代碼
		...
	}
複製代碼

接下來繼續看//XmlBeanDefinitionReader.java L388的實現

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
		    //校驗xml文件的是xsd 仍是 dtd格式的,而後調用 DefaultDocumentLoader#doLoadDocument將流解析成document對象
			Document doc = doLoadDocument(inputSource, resource);
			//將document和資源封裝成bean實例
			return registerBeanDefinitions(doc, resource);
		}
		//省略若干異常處理
		...
	}
複製代碼

// XmlBeanDefinitionReader.java L505

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //構建DefaultBeanDefinitionDocumentReader的實例由documentReader負責解析xml並封裝到beandefinition裏
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//BeanDefinitionRegistry 負責將bean實例註冊到bean工廠
		int countBefore = getRegistry().getBeanDefinitionCount();
		//下面會詳細展開說明
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
    
複製代碼

通過艱難險阻,磕磕絆絆,咱們終於到了核心邏輯的底部doRegisterBeanD巳finitions(root) , 至少咱們在這個方法中看到了但願。 若是說之前一直是XML 加載解析的準備階段,那麼doRegisterBeanDefinitions 算是真正地 開始進行解析了,咱們期待的核心部分真正開始了:

==DefaultBeanDefinitionDocumentReader==

//DefaultBeanDefinitionDocumentReader.java L116

protected void doRegisterBeanDefinitions(Element root) {
	
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);

        //處理profile
		if (this.delegate.isDefaultNamespace(root)) {
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					return;
				}
			}
		}
        //模板方法模式,讓子類去實現,利於用戶本身拓展
		preProcessXml(root);
		
		parseBeanDefinitions(root, this.delegate);
		
        //模板方法模式,讓子類去實現,利於用戶本身拓展
		postProcessXml(root);

		this.delegate = parent;
	}
複製代碼

繼續跟蹤parseBeanDefinitions(root, this.delegate); //DefaultBeanDefinitionDocumentReader.java L157

/**
	 * Parse the elements at the root level in the document:
	 * "import", "alias", "bean".
	 * @param root the DOM root element of the document
	 */
	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	    //判斷當前元素是不是beans節點
		if (delegate.isDefaultNamespace(root)) {
		    //beans
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
			    //循環beans下面的節點
				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 {
		    //自定義的元素
			delegate.parseCustomElement(root);
		}
	}
複製代碼

從上面的代碼,咱們能夠看到,對於每一個配置來講,分別進入到 parseDefaultElement(ele, delegate); 和 delegate.parseCustomElement(ele); 這兩個分支了。

parseDefaultElement(ele, delegate) 表明解析的節點是 、、、 這幾個。 這裏的四個標籤之因此是 default 的,是由於它們是處於這個 namespace 下定義的:www.springframework.org/schema/bean…

而對於其餘的標籤,將進入到 delegate.parseCustomElement(element) 這個分支。如咱們常常會使用到的 、、、等。

回過神來,看看處理 default 標籤的方法:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
		    //import
			importBeanDefinitionResource(ele);
		}
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
		    //alias
			processAliasRegistration(ele);
		}
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		    //bean  建立bean的入口
			processBeanDefinition(ele, delegate);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// beans
			doRegisterBeanDefinitions(ele);
		}
	}
複製代碼

這裏咱們不會對每個標籤進行解析,僅僅對最重要的進行跟蹤 解析的入口: //DefaultBeanDefinitionDocumentReader.java L294

/**
	 * Process the given bean element, parsing the bean definition
	 * and registering it with the registry.
	 */
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	    // 將 <bean /> 節點中的信息提取出來,而後封裝到一個 BeanDefinitionHolder 中,細節往下看
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
		    //對自定義屬性進行解析。忽略
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// 註冊bean
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// bean註冊完成後發送事件,可是這裏是空實現,留給用戶擴展
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}
複製代碼

將xml信息載入到beanDefinition中。

==BeanDefinitionParserDelegate==

// BeanDefinitionParserDelegate L437

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
		String id = ele.getAttribute(ID_ATTRIBUTE);
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

		List<String> aliases = new ArrayList<String>();
		//將 name 屬性的定義按照 」逗號、分號、空格「切分,造成一個別名列表數組,
		if (StringUtils.hasLength(nameAttr)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			aliases.addAll(Arrays.asList(nameArr));
		}

		String beanName = id;
		//// 若是沒有指定id, 那麼用別名列表的第一個名字做爲beanName
		if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
			beanName = aliases.remove(0);
		}
        
		if (containingBean == null) {
			checkNameUniqueness(beanName, aliases, ele);
		}
        // 根據 <bean ...>...</bean> 中的配置建立 BeanDefinition,而後把配置中的信息都設置到實例中,
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		// 到這裏,整個 <bean /> 標籤就算解析結束了,一個 BeanDefinition 就造成了。
		if (beanDefinition != null) {
			if (!StringUtils.hasText(beanName)) {
			 // 若是都沒有設置 id 和 name,那麼此時的 beanName 就會爲 null,進入下面這塊代碼產生生成相應的別名,能夠忽略
				try {
					if (containingBean != null) {
						beanName = BeanDefinitionReaderUtils.generateBeanName(
								beanDefinition, this.readerContext.getRegistry(), true);
					}
					else {
						beanName = this.readerContext.generateBeanName(beanDefinition);
						
						String beanClassName = beanDefinition.getBeanClassName();
						if (beanClassName != null &&
								beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
								!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
							aliases.add(beanClassName);
						}
					}
				}
				catch (Exception ex) {
					error(ex.getMessage(), ele);
					return null;
				}
			}
			String[] aliasesArray = StringUtils.toStringArray(aliases);
			// 返回 BeanDefinitionHolder
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}

		return null;
	}
複製代碼

==// BeanDefinitionParserDelegate.java#parseBeanDefinitionElement L522==

/**
	 * Parse the bean definition itself, without regard to name or aliases. May return
	 * {@code null} if problems occurred during the parsing of the bean definition.
	 */
	public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, BeanDefinition containingBean) {

        //parseState裏維護了一個棧,保證先進後出
		this.parseState.push(new BeanEntry(beanName));

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

		try {
			String parent = null;
			if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
			    //取出parent屬性
				parent = ele.getAttribute(PARENT_ATTRIBUTE);
			}
			//用class 和parent建立一個GenericBeanDefinition
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);
             // 設置 BeanDefinition 的一堆屬性,這些屬性定義在 AbstractBeanDefinition 中
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

             /**
               * 下面的一堆是解析 <bean>......</bean> 內部的子元素,
               * 解析出來之後的信息都放到 bd 的屬性中
               */
         
            // 解析 <meta />
			parseMetaElements(ele, bd);
			// 解析 <lookup-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;
		}
		finally {
			this.parseState.pop();
		}

		return null;
	}
複製代碼

到這裏,咱們已經完成了根據 配置建立了一個 BeanDefinitionHolder 實例。 咱們回到解析的入口: //DefaultBeanDefinitionDocumentReader.java L294

/**
	 * Process the given bean element, parsing the bean definition
	 * and registering it with the registry.
	 */
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	    // 將 <bean /> 節點中的信息提取出來,而後封裝到一個 BeanDefinitionHolder 中,細節往下看
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
		    //對自定義屬性進行解析。忽略
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// 註冊bean
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// bean註冊完成後發送事件,可是這裏是空實現,留給用戶擴展
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}
複製代碼

接下來是註冊bean實例的過程——

註冊bean到緩存中

//BeanDefinitionReaderUtils#registerBeanDefinition L143

/**
	 * Register the given bean definition with the given bean factory.
	 */
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		String beanName = definitionHolder.getBeanName();
		//調用BeanDefinitionRegistry實現類註冊以beanName爲key,bean實例做爲value存入緩存中
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// 若是還有別名的話,也要根據別名通通註冊一遍,否則根據別名就找不到 Bean 了
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
    			// alias -> beanName 保存它們的別名信息,這個很簡單,用一個 map 保存一下就能夠了,
                // 獲取的時候,會先將 alias 轉換爲 beanName,而後再查找
				registry.registerAlias(beanName, alias);
			}
		}
	}
複製代碼

這裏BeanDefinitionRegistry的實現類用DefaultListableBeanFactory 方法比較長,爲了便於閱讀,我將一些異常處理等非核心方法省略 //DefaultListableBeanFactory#registerBeanDefinition L793

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
        ...

		BeanDefinition oldBeanDefinition;
        // 從beanDefinitionMap緩存中取,以後會看到,全部的 Bean 註冊後會放入這個 beanDefinitionMap 中
		oldBeanDefinition = this.beanDefinitionMap.get(beanName);
		if (oldBeanDefinition != null) {
		    //allowBeanDefinitionOverriding  customizeBeanFactory(beanFactory) 裏能夠設置這個屬性,默認是trueif (!isAllowBeanDefinitionOverriding()) {
			//不容許覆蓋,拋異常
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound.");
			}
			...打印相關日誌
			//將beanName做爲key,beanDefinition做爲value放入map中,覆蓋舊值
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
		    //該beanName未被建立
		    //判斷alreadyCreated鏈表是否爲空===判斷是否已經有其餘的 Bean 開始初始化
		    // 注意,"註冊Bean" 這個動做結束,Bean 依然尚未初始化,咱們後面會有大篇幅說初始化過程,
            // 在 Spring 容器啓動的最後,會 預初始化 全部的 singleton beans
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				//爲了集合迭代的穩定性,不能直接操做元集合(不能增長和刪除元素個數)
				synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition);
					//新建一個臨時集合來實現新增的beanname
					//beanDefinitionNames  預註冊的bean實例的beanName集合
					List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					//manualSingletonNames  手動註冊單例的beanName集合
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			else {
				// 這是正常的開始
                // 將 BeanDefinition 放到這個 map 中,這個 map 保存了全部的 BeanDefinition
				this.beanDefinitionMap.put(beanName, beanDefinition);
				
				// 這是個 ArrayList,因此會按照 bean 配置的順序保存每個註冊的 Bean 的名字
				this.beanDefinitionNames.add(beanName);
				
				// 這是個 LinkedHashSet,表明的是手動註冊的 singleton bean,
                // 注意這裏是 remove 方法,到這裏的 Bean 固然不是手動註冊的
                // 手動指的是經過調用如下方法註冊的 bean :registerSingleton(String beanName, Object singletonObject)
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (oldBeanDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
	}
複製代碼

總結一下,到這裏已經初始化了 Bean 容器, 配置也相應的轉換爲了一個個 BeanDefinition,而後註冊了各個 BeanDefinition 到了註冊中心

相關文章
相關標籤/搜索