Spring技术内幕笔记(三)------IoC容器初始化过程#BeanDefinition的载入和解析

IoC容器的初始化过程

在上一篇中我们讨论了对代表BeanDefinition的Resource定位的分析后,接下来就需要了解整个BeanDefinition信息的载入过程。

在AbstractApplicationContext#refresh()方法中,有这样一段代码

ConfiguableListableBeanFactory beanFactory = obtainFreshBeanFactory();

这个方法的作用就是通知子类重新启动BeanFactory,源码中是这样说的

Tell the subclass to refresh the internal bean factory.

那么接下来就让我们看看这个方法内部是如何实现的

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

在当前方法中调用了refreshBeanFactory(),从方法名上可以看到,这个才是真正重新启动BeanFactory的方法。接下来会调用getBeanFactory()返回我们已经重新启动好的BeanFactory()。当然在AbstractApplicationContext中是没有实现这两个方法的,他们的具体实现我们需要到AbstractApplicationContext的子类中寻找。

AbstractApplicationContext的子类AbstractRefreshableApplicationContext实现了refreshBeanFactory()和getBeanFactory()方法。

1.refreshBeanFactory()

/**
	  This implementation performs an actual refresh of this context's underlying
	  bean factory, shutting down the previous bean factory (if any) and
	  initializing a fresh bean factory for the next phase of the context's lifecycle.
	  这个实现了一个对当前上下文中的beanFactory刷新的工作,关闭以前的BeanFactory和为上下文的下一阶段初始化一个新的BeanFactory
	  
	 */
	@Override
	protected final void refreshBeanFactory() throws BeansException {
	    //如果当前上下文已经持有一个还未关闭BeanFactory,那么首先销毁Bean,然后关闭BeanFactory
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
		    //创建出一个新的BeanFactory
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			//将当前上下文的id作为BeanFactory的序列化id
			beanFactory.setSerializationId(getId());
			//自定义当前上下文的内部beanFactory
			customizeBeanFactory(beanFactory);
			//加载配置文件中的BeanDefinition
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

通过以上简单的分析,我们可以知道,AbstractRefreshableApplicationContext#refreshBeanFactory()的作用是对当前ApplicationContext中的BeanFactory进行重启,重新加载BeanFactory。

到这里就是我们今天的重点了,BeanDefinition的载入
loadBeanDefinition(DefaultListableBeanFactory beanFactor),通常通过委托给一个或多个BeanDefinitionReader,将BeanDefinition加载到给定的BeanFactory中。今天我们主要看一下AbstractXmlApplicationContext中实现的loadBeanDefintions方法
2. AbstractXMLApplicationContext#loadBeanDefinition(DefaultListableBeanFactory beanFactory)

	/**
	  Loads the bean definitions via an XmlBeanDefinitionReader.
	  通过XmlBeanDefinitionReader来加载bean的定义
	  @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
	  @see #initBeanDefinitionReader
	  @see #loadBeanDefinitions
	 */
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		//给给定的BeanFactory创建一个新的XmlBeanDefinitionReader
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		//给当前上下文环境配置bean定义读取器
		// resource loading environment.
		//资源加载环境
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		//允许子类提供自定义个的bean定义读取器
		// then proceed with actually loading the bean definitions.
		//然后继续加载Bean定义
		initBeanDefinitionReader(beanDefinitionReader);
		loadBeanDefinitions(beanDefinitionReader);
	}
	
	/**
	 * Load the bean definitions with the given  XmlBeanDefinitionReader.
	  在给定的XmlBeanDefintionReader中加载bean定义
	  <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
	  method; hence this method is just supposed to load and/or register bean definitions.
	  bean工厂的生命周期由refreshBeanFactory方法处理,因此这个方法只进行bean定义的加载和/或注册。
	  @param reader the XmlBeanDefinitionReader to use
	  要使用到的读取器
	  @throws BeansException in case of bean registration errors
	  @throws IOException if the required XML document isn't found
	  @see #refreshBeanFactory
	  @see #getConfigLocations
	  @see #getResources
	  @see #getResourcePatternResolver
	 */
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
	    //使用Resource的方式来获取文件资源位置
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			reader.loadBeanDefinitions(configResources);
		}
		//使用String的形式获得配置文件的位置
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			reader.loadBeanDefinitions(configLocations);
		}
	}

以上两个方法是在XMLApplicationContext中对Bean定义载入工作的准备,真正进行载入工作的还是BeanDefinitionReader,由于我们当前是XMLApplicationContext,所以使用的是XmlBeanDefinitionReader。这个读取器继承了AbstractBeanDefinitionReader,在上述代码中使用了两种不同的形式获取配置文件的位置,一种是直接传一个Resource对线,还有一种是传一个String,使用String这种形式,它是在AbstractBeanDefinitionReader中有所实现,最后还是会通过传过来的String,获取到Resource来调用子类实现的loadBeanDefinitions(Resource resource)方法,所以在这里我们就直接看一下子类中的loadBeanDefinitions(Resource resource)是怎么实现的。

  1. XmlBeanDefinitionReader#loadBeanDefinitionReader
	/**
	  Load bean definitions from the specified XML file.
	  从指定的XMl文件中加载bean 定义 
	  @param resource the resource descriptor for the XML file
	  @return the number of bean definitions found
	  @throws BeanDefinitionStoreException in case of loading or parsing errors
	 */
	@Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}

	/**
	  Load bean definitions from the specified XML file.
	  @param encodedResource the resource descriptor for the XML file,
	  allowing to specify an encoding to use for parsing the file
	  允许指定一个解析文件的编码
	  @return the number of bean definitions found
	  @throws BeanDefinitionStoreException in case of loading or parsing errors
	 */
	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		//断言,资源必须不为null
		if (logger.isTraceEnabled()) {
			logger.trace("Loading XML bean definitions from " + encodedResource);
		}
        //这里有一个同步缓存,使用ThreadLoacl,在没有取到的情况在,将新的EncodeResource存到ThreadLocal中
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		//接下来就是通过输入流读取文件中的配置了,然后调用doLoadBeanFactory()方法
		try {
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				inputStream.close();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
			}
		}
	}

同学们可能注意到了,上面的方法中使用到了ThreadLocal,它会为每个线程提供独立的变量副本,这就说明我们loadBeanDefinition这个方法是一个并发方法,可能会同时加载多个资源文件。

4.XmlBeanDefinitionReader#doLoadBeanDefinition(InputSource source, Resource resource)

/**
	 * Actually load bean definitions from the specified XML file.
	    真正从指定XMl文件加载bean定义
	 * @param inputSource the SAX InputSource to read from
	 * @param resource the resource descriptor for the XML file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 * @see #doLoadDocument
	 * @see #registerBeanDefinitions
	 */
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
		    //取得XMl文件中的document对象
			Document doc = doLoadDocument(inputSource, resource);
			//将document对象解析并转化为容器内部数据结果
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
		}
		·····
	}

根据源码,我们可以知道BeanDefinition的载入分为两个部分,第一部分是通过XML的解析器得到对应的document对象,但是这些document并不没有按照Spring的Bean规则进行解析。完成通用XMl解析之后,我们会通过documentReader对document进行解析。

5.XmlBeanDefinitionReader#registerBeanDefinition(Document doc,Resource resource)

/**
	 * Register the bean definitions contained in the given DOM document.
	 在给定的DOM deocument中注册包含的bean定义
	 * Called by {@code loadBeanDefinitions}.
	 * <p>Creates a new instance of the parser class and invokes
	 创建解析器的实例并调用
	 * {@code registerBeanDefinitions} on it.
	 * @param doc the DOM document
	 * @param resource the resource descriptor (for context information)
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of parsing errors
	 * @see #loadBeanDefinitions
	 * @see #setDocumentReaderClass
	 * @see BeanDefinitionDocumentReader#registerBeanDefinitions
	 */
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
	    //创建一个新的document解析器
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//
		int countBefore = getRegistry().getBeanDefinitionCount();
		//调用解析器的registerBeanDfinitions
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

由于在BeanDefinitionDocumentReader中的registerBeanDefinitions是一个接口方法,所以我们就需要看一下他的实现类中是如何实现这个方法的。
6. DefaultBeanDefinitionDocumnetReader#registerBeanDefinitions()

/**
	 * This implementation parses bean definitions according to the "spring-beans" XSD
	 这个实现根据XSD解析bean定义
	 * (or DTD, historically).
	 * <p>Opens a DOM Document; then initializes the default settings
	 * specified at the {@code <beans/>} level; then parses the contained bean definitions.
	 */
	@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		doRegisterBeanDefinitions(doc.getDocumentElement());
	}


/**
	 * Register each bean definition within the given root {@code <beans/>} element.
	 在给定的根元素<bean>中注册每一个bean定义
	 */
	@SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
	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);

		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);
				// We cannot use Profiles.of(...) since profile expressions are not supported
				// in XML config. See SPR-12458 for details.
				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);
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);

		this.delegate = parent;
	}

在完成了一系列的载入和解析工作之后,在这些动作完成以后,用户定义的BeanDefinition信息都已经在Ioc容器中建立起自己的数据结果和相应的数据表示,接下来就是对其进行注册了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值