spring源码解读(二)spring容器如何加载xml配置文件到容器中

上一篇介绍了如何下载spring源码,编译,及修改源码+注解的使用

spring容器的基本使用及xml配置属性的说明;
这篇文章来介绍下spring容器时如何加载解析xml配置到spring容器中的

首先从测试代码中看到

package com.wsj.spring;

import com.wsj.spring.bean.LookUpSayHello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    public static void main(String[] args) {
        ApplicationContext app=new ClassPathXmlApplicationContext("test.xml");
        LookUpSayHello bean = app.getBean(LookUpSayHello.class);
        bean.sayHello();
    }
}

使用ClassPathXmlApplicationContext加载xml文件,下面跟进去,最终调到这个构造方法 

/**
	 * Create a new ClassPathXmlApplicationContext with the given parent,
	 * loading the definitions from the given XML files.
	 * @param configLocations array of resource locations
	 * @param refresh whether to automatically refresh the context,
	 * loading all bean definitions and creating all singletons.
	 * Alternatively, call refresh manually after further configuring the context.
	 * @param parent the parent context
	 * @throws BeansException if context creation failed
	 * @see #refresh()
	 */
	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
		//这里可以根据不同的子类实现
		// 根据提供的路径,处理成配置文件数组(以分号、逗号、空格、tab、换行符分割)
		setConfigLocations(configLocations);
		if (refresh) {
			/**
			 * 这里简单说下为什么是 refresh(),而不是 init() 这种名字的方法。
			 * 因为 ApplicationContext 建立起来以后,其实我们是可以通过调用 refresh() 这个方法重建的,这样会将原来的 ApplicationContext 销毁,
			 * 然后再重新执行一次初始化操作。
			 */
			refresh();// 核心方法
		}
	}

点击进入refresh()方法,会看到跳转到父类(其实时爷爷的爷爷类)

AbstractApplicationContext中,
@Override
	public void refresh() throws BeansException, IllegalStateException {
		// 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			// 准备工作,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			// 这步比较关键,这步完成后,配置文件就会解析成一个个Bean定义(BeanDefinition),注册到 BeanFactory 中,
			// 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了,
			// 注册也只是将这些信息都保存到了注册中心(说到底核心是一个 beanName-> beanDefinition 的 map)
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);
			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);
				//完成BeanFactoryPostProcessors 和BeanDefinitionRegistryPostProcessor的实例化,执行相应的postProcessor方法
				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);
				//实例化所有beanPostProcessors实例到容器中
				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
				//国际化代码
				// Initialize message source for this context.
				initMessageSource();
				//spring事件代码
				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();
				//可以自定义的onRefresh()
				// Initialize other special beans in specific context subclasses.
				onRefresh();
				//事件的监听
				// Check for listener beans and register them.
				registerListeners();
				//初始化单实例bean。spring ioc的核心代码入口
				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);
				//完成容器的刷新
				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

下面来详细介绍下obtainFreshBeanFactory()方法 :

/**
	 * Tell the subclass to refresh the internal bean factory.
	 * @return the fresh BeanFactory instance
	 * @see #refreshBeanFactory()
	 * @see #getBeanFactory()
	 */
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //主要看这里刷新beanFactory()
		refreshBeanFactory();
		return getBeanFactory();
	}

这里调到了子类:AbstractRefreshableApplicationContext的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.
	 */
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		//判断beanfactory是否存在,如果存在销毁容器
		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);
		}
	}

这里首先判断beanFactory是否存在,如果不为空就清空容器 并销毁容器。 其实也就是非空判断(判断方法)

/**
	 * Determine whether this context currently holds a bean factory,
	 * i.e. has been refreshed at least once and not been closed yet.
	 */
	protected final boolean hasBeanFactory() {
		synchronized (this.beanFactoryMonitor) {
			return (this.beanFactory != null);
		}
	}

 

这里主要要看的是loadBeanDefinitions(beanFactory)这个方法,从名字就能看出是加载beandefitions的。这里调用的是子类:AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)

/**
	 * Loads the bean definitions via an XmlBeanDefinitionReader.
	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
	 * @see #initBeanDefinitionReader
	 * @see #loadBeanDefinitions
	 */
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// 创建xmlbeanDefinitionRead解析器 用于读取xml解析为BeanDefinition 加载的beanDefinitionMap中
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		//把xmlApplicationContext设置到xmlBeanDefinitionRead中,让beanDefinitionRead 包含springContent
		beanDefinitionReader.setResourceLoader(this);
		//设置要用于解析xml的SAX实体解析器
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// 允许子类提供阅读器的自定义初始化,
		//然后继续实际加载bean定义。
		initBeanDefinitionReader(beanDefinitionReader);
		//加载BeanDefinitions
		loadBeanDefinitions(beanDefinitionReader);
	}

再看loadBeanDefinitions(beanDefinitionReader);这里是使用XmlBeanDefinitionReader解析器解析文件

/**
	 * Load the bean definitions with the given XmlBeanDefinitionReader.
	 * 

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. * @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[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } //获取所有要解析的xml文件列表 String[] configLocations = getConfigLocations(); if (configLocations != null) { //让BeanDefinitionRead去加载BeanDefinitions reader.loadBeanDefinitions(configLocations); } }

这里要解析的xml文件列表是ClassPathXmlApplicationContext入口类的构造方法setConfigLocations(configLocations);中存进去的。

这里主要看的还是reader.loadBeanDefinitions(configLocations)这行代码。

这里调用了.XmlBeanDefinitionReader 的父类方法AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String...)

 

@Override
	public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
		Assert.notNull(locations, "Location array must not be null");
		int count = 0;
		//循环配置文件解析
		for (String location : locations) {
			count += loadBeanDefinitions(location);
		}
		return count;
	}

进入loadBeanDefinitions(location);中

@Override
	public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(location, null);
	}

进入loadBeanDefinitions(location, null); 

/**
	 * Load bean definitions from the specified resource location.
	 * 

The location can also be a location pattern, provided that the * ResourceLoader of this bean definition reader is a ResourcePatternResolver. * @param location the resource location, to be loaded with the ResourceLoader * (or ResourcePatternResolver) of this bean definition reader * @param actualResources a Set to be filled with the actual Resource objects * that have been resolved during the loading process. May be {@code null} * to indicate that the caller is not interested in those Resource objects. * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors * @see #getResourceLoader() * @see #loadBeanDefinitions(org.springframework.core.io.Resource) * @see #loadBeanDefinitions(org.springframework.core.io.Resource[]) */ public int loadBeanDefinitions(String location, @Nullable Set actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { //把xml解析成resource数组 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); //BeanDefinitionRead加载BeanDefinitions 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 resource = resourceLoader.getResource(location); //BeanDefinitionRead加载BeanDefinitions 这里是模板方法,调用子类 int count = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location [" + location + "]"); } return count; } }

这给方法可以看出是把xml文件解析成了Resource

接下来还是主要看:loadBeanDefinitions 这里又是调用子类方法,子类可以实现自己的解析加载规则

/**
	 * Load bean definitions from the specified XML file.
	 * @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 {
		//对资源xml文件进行encoded后交
		return loadBeanDefinitions(new EncodedResource(resource));
	}

 这里把resource包装成EncodeResource。

进入loadBeanDefinitions方法中

/**
	 * 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");
		if (logger.isTraceEnabled()) {
			logger.trace("Loading XML bean definitions from " + encodedResource);
		}
		//从ThreadLocation中获取 EncodedResource列表
		Set 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!");
		}
		try {
			//把xml文件读取到流中
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				//执行BeanDefinitions解析
				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();
			}
		}
	}

这里 主要是把resource读取到流中,然后调用doLoadBeanDefinitions(inputSource, encodedResource.getResource());把流里的内容解析成beanDefitions

/**
	 * Actually load bean definitions from the specified XML file.
	 * @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);
			//注册BeanDefinitions
			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);
		}
	}

 这个方法是用于把流文件解析成Document。然后待用registerBeanDefinitions(doc, resource);方法,从dom文件中解析注册beanDefition。

下面看看registerBeanDefinitions(doc, resource);方法:

/**
	 * Register the bean definitions contained in the given DOM document.
	 * Called by {@code loadBeanDefinitions}.
	 * 

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 { //创建BeanDefinition的Document 解析器 委托模式,职责单一 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); //createReaderContext 创建 XmlReaderContext 4* //registerBeanDefinitions documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }

这里又创建了一个 BeanDefinitionDocumentReader 用于解析dom :

这里要看的是:documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

首先看下createReaderContext(resource)方法

/**
	 * Create the {@link XmlReaderContext} to pass over to the document reader.
	 * 创建XML读取上线文
	 * 包含xml资源内容
	 * problemReporter失败记录容器
	 * eventListener事件监听
	 * sourceExtractor源提取器
	 * this
	 * getNamespaceHandlerResolver默认命名空间处理程序解析器
	 */
	public XmlReaderContext createReaderContext(Resource resource) {
		return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
				this.sourceExtractor, this, getNamespaceHandlerResolver());
	}

	/**
	 * Lazily create a default NamespaceHandlerResolver, if not set before.
	 * @see #createDefaultNamespaceHandlerResolver()
	 */
	public NamespaceHandlerResolver getNamespaceHandlerResolver() {
		if (this.namespaceHandlerResolver == null) {
			this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
		}
		return this.namespaceHandlerResolver;
	}

	/**
	 * Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified.
	 * 

The default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}. * @see DefaultNamespaceHandlerResolver#DefaultNamespaceHandlerResolver(ClassLoader) * 这里初始化 默认命名空间处理程序解析器 加载 META-INF/spring.handlers */ protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() { ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader()); return new DefaultNamespaceHandlerResolver(cl); }

 

 先写到这,后面的代码比较关键,抽空补齐

 

 

 

你可能感兴趣的:(spring)