Back
Featured image of post Spring源码IOC篇-容器的初始化(一)

Spring源码IOC篇-容器的初始化(一)

1、通过XML配置文件去初始化容器

我们以xml的方式去初始化去初始化bean,来看看在整个初始化流程中究竟做了什么?

<?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">

    <!-- 控制反转, 让 bean1 被 Spring 容器管理 -->
    <bean id="bean1" class="com.zp.example02.TestBeanLoad.Bean1"/>

    <!-- 控制反转, 让 bean2 被 Spring 容器管理 -->
    <bean id="bean2" class="com.zp.example02.TestBeanLoad.Bean2">
        <!-- 依赖注入, 建立与 bean1 的依赖关系 -->
        <property name="bean1" ref="bean1"/>
    </bean>
</beans>
public class TestBeanLoad {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext context =
				new ClassPathXmlApplicationContext("a02.xml");
		//
		for (String name : context.getBeanDefinitionNames()) {
			System.out.println(name);
		}



		System.out.println(context.getBean(Bean2.class).getBean1());

	}


	

	static class Bean1 {
	}

	static class Bean2 {

		private Bean1 bean1;

		public void setBean1(Bean1 bean1) {
			this.bean1 = bean1;
		}

		public Bean1 getBean1() {
			return bean1;
		}
	}
}

容器的初始化过程主要通过refresh()进行初始化的,同样refresh()方法也是整个初始化流程的算法骨架模板方法

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {

			// Prepare this context for refreshing.

			//1.为更新准备上下文,设定一些标志
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.

			//2.通知子类刷新内部bean工厂,初始化BeanFactory并进行XML文件的解析、读取
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.

			//3.准备在此上下文中使用的Bean工厂
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.

				//4.允许在上下文子类中对bean工厂进行后处理
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.

				//5.调用beanFactory的后处理器
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.

				//6.注册bean后处理器,在调用getBean的时候回调这些bean后处理器方法
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.

				//7.为上下文初始化Message源
				initMessageSource();

				// Initialize event multicaster for this context.

				//8.初始化事件多播器
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				//9.留给子类初始化其他的bean
				onRefresh();

				// Check for listener beans and register them.
				//10.注册事件监听
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.

				//11.初始化所有的单例bean(非懒惰加载)
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				//12.发布事件通知
				finishRefresh();
			}

2、prepareRefresh()

该方法主要做一些容器初始化前的准备工作,为刷新准备这个上下文,设置它的启动日期和活动标志,以及执行任何属性源的初始化。具体的代码逻辑如下:

protected void prepareRefresh() {
		// Switch to active.(容器启动时间)
		this.startupDate = System.currentTimeMillis();
		//设置容器的关闭标志位
		this.closed.set(false);

		//设置容器开启的标志位
		this.active.set(true);

		if (logger.isDebugEnabled()) {
			if (logger.isTraceEnabled()) {
				logger.trace("Refreshing " + this);
			}
			else {
				logger.debug("Refreshing " + getDisplayName());
			}
		}

		// Initialize any placeholder property sources in the context environment.(子类扩展点用于存在一些环境参数)
		initPropertySources();

		// Validate that all properties marked as required are resolvable:& (校验需要的环境参数)
		// see ConfigurablePropertyResolver#setRequiredProperties
		getEnvironment().validateRequiredProperties();

		// Store pre-refresh ApplicationListeners...(添加刷新前的应用监听器集合)
		if (this.earlyApplicationListeners == null) {
			this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
		}
		else {
			// Reset local application listeners to pre-refresh state.
			this.applicationListeners.clear();
			this.applicationListeners.addAll(this.earlyApplicationListeners);
		}

		// Allow for the collection of early ApplicationEvents,
		// to be published once the multicaster is available...(收集早期的事件监听)
		this.earlyApplicationEvents = new LinkedHashSet<>();
	}

3、obtainFreshBeanFactory()

该方法是整个启动流程中十分重要的方法,通知子类刷新内部bean工厂,初始化BeanFactory并进行XML文件的解析、读取。BeanFactory的创建和Bean定义信息的注册、解析都是通过此步骤来完成的。通过一张时序图来看下次方法运行的整体流程,再逐一进行刨析。

  • AbstractRefreshableApplicationContext中的refreshBeanFactory
/**该实现执行此上下文的底层bean工厂的实际刷新,关闭以前的bean工厂(如果有的话)并为上下文生命周期的下一个阶段初始化新的bean工厂**/
@Override
	protected final void refreshBeanFactory() throws BeansException {
		//如果存在BeanFactory则销毁之前的BeanFactory
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}

		try {

			//创建beanFactory
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());

			//子类自由配置工厂,例如:是否允许覆盖同名称的不同定义对象、循环依赖、手动添加一些Bean定义信息等
			customizeBeanFactory(beanFactory);

			//初始化docmentReader,并进行XMl文件读取及解析,默认命名空间的解析,自定义标签的解析
			//这一步实际上就从XML配置文件里的bean信息给读取到了Factory里了
			loadBeanDefinitions(beanFactory);

			this.beanFactory = beanFactory;
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

先在这里看下loadBeanDefinitions()执行结束之后的结果:

从图中可以知道,loadBeanDefinitions()方法运行完后,在beanFactory变量里面存放着一个ConcurrentHashMap变量,用于存放着这两个Bean的KV键值对,说明这两个Bean的定义信息以及属性信息已经被加载到BeanFactory当中了。

  • AbstractXmlApplicationContext中的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
		// 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,

		// then proceed with actually loading the bean definitions.(拓展点允许子类提供读取器的自定义初始化:模板方法)
		initBeanDefinitionReader(beanDefinitionReader);

		//加载bean的定义信息
		loadBeanDefinitions(beanDefinitionReader);
	}

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			reader.loadBeanDefinitions(configResources);
		}
        //这里就是我们在ClassPathXmlApplicationContext中传入的a02.xml
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			reader.loadBeanDefinitions(configLocations);
		}
	}

​ 首先在refreshBeanFactory()方法中已经初始化了DefaultListableBeanFactory,对于读取XML配置文件,还需要使用XmlBeanDefinitionReader。所以在上述loadBeanDefinitions()中就需要初始化XmlBeanDefinitionReader。在DefaultListableBeanFactory和XmlBeanDefinitionReader后就可以进行配置文件的读取了。要注意的地方时,在XmlBeanDefinitionReader初始化时就已经把DefaultListableBeanFactory给注册进去了,所以在XmlBeanDefinitionReader读取的BeanDefinition都会注册到DefaultListableBeanFactory中,也就是经过上述的loadingBeanDefinitions(),类型DefaultListableBeanFactory的变量beanFactory就已经包含了所有解析好的配置了。

  • AbstractBeanDefinitionReader中的loadBeanDefinitions()
@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;
	}
/**
	 * 从指定的资源位置加载bean定义,位置也可以是位置模式,前提是这个bean定义读取器的ResourceLoader是一个ResourcePatternResolver(资源路径解析器)
	 *.....
	 */
	public int loadBeanDefinitions(String location, @Nullable Set<Resource> 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 {
                
				//资源定位
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				
                //加载bean定义信息的
                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);
			int count = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isTraceEnabled()) {
				logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
			}
			return count;
		}
	}
  • PathMatchingResourcePatternResolver中的getResource()方法
@Override
	public Resource[] getResources(String locationPattern) throws IOException {
		Assert.notNull(locationPattern, "Location pattern must not be null");
		//如果是类路径下的资源
		if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
			// a class path resource (multiple resources for same name possible)
			//类路径资源(可能有同名的多个资源),通配资源
			if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
				// a class path resource pattern
				return findPathMatchingResources(locationPattern);
			}
			else {
				// all class path resources with the given name
				//具有给定名称的所有类路径资源
				return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
			}
		}
		else {
			// Generally only look for a pattern after a prefix here,
			// and on Tomcat only after the "*/" separator for its "war:" protocol.
			//通常只在前缀后面查找一个模式,并且在Tomcat之后只有“* /”分隔符之后的“war:”协议
			int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
					locationPattern.indexOf(':') + 1);
			if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
				// a file pattern
				return findPathMatchingResources(locationPattern);
			}
			else {
				// a single resource with the given name
				//给定具有名称的单个资源
				return new Resource[] {getResourceLoader().getResource(locationPattern)};
			}
		}
	}

此步骤主要对应AbstractBeanDefinitionReader中定位资源的实现,根据我们传入的规则定位到要加载的配置文件位置。

  • XmlBeanDefinitionReader中的loadBeanDefinitions()方法
/**
 * 从指定的XML文件加载bean定义信息
*/
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);
	}

	Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

	if (!currentResources.add(encodedResource)) {
		throw new BeanDefinitionStoreException(
				"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
	}

	try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
		InputSource inputSource = new InputSource(inputStream);
		if (encodedResource.getEncoding() != null) {
			inputSource.setEncoding(encodedResource.getEncoding());
		}
		//获取到读取xml配置文件的InputStream流后,进行BeanDefinitions的加载
		return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
	}
        ....
}
/*
    真正从xml配置文件中加载Bean定义信息
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
		throws BeanDefinitionStoreException {

	try {
	    //获取xml的配置信息并封装为Document对象
		Document doc = doLoadDocument(inputSource, resource);
		return this.registerBeanDefinitions(doc, resource);
	}
	catch (BeanDefinitionStoreException ex) {
	    ...
	}
}

在XmlBeanDefinitionReader中loadBeanDefinitions()方法和doLoadBeanDefinitions()方法主要对我们对应的配置文件路径转化为读取并转化为Docment对象。

  • XmlBeanDefinitionReader中的registerBeanDefinitions()方法
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//从工厂中拿到bean定义信息的个数
		int countBefore = getRegistry().getBeanDefinitionCount();
        //通过documentReader来解析注册bean定义信息
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//本次加载ben定义信息的个数
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

此步骤主要通过创建BeanDefinitionDocumentReader让BeanDefinitionDocumentReader来解析注册bean定义信息。

  • DefaultBeanDefinitionDocumentReader中的doRegisterBeanDefinitions()方法
/**
	 * Register each bean definition within the given root {@code <beans/>} element.
	 *   任何嵌套的<beans>元素都将导致此方法的递归。为了正确传播和保留beans的default属性,需要跟踪当前(父)委托,该委托可以为null。创建新的(子)委托时,需要引用父项以进行回退,然后最终将this.delegate重置为其原始(父)引用。
	 */
	@SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
	//首先会拿到根节点对象 <Beans>标签
	protected void doRegisterBeanDefinitions(Element root) {

		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;
				}
			}
		}
		// 解析 bean 定义之前处理 xml 对象,默认实现是空
		preProcessXml(root);
		//解析bean定义信息
		parseBeanDefinitions(root, this.delegate);
		// 解析 bean 定义之后处理 xml 对象默认实现是空
		postProcessXml(root);

		this.delegate = parent;
	}

该步骤主要作为根节点解析的入口。

  • DefaultBeanDefinitionDocumentReader中的parseBeanDefinitions()方法
/**
	 * 解析文档中根级别的元素
	 * 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) {
		/**
		 *
		 * 若 element 的 namespace 是 default namespace,将进入 parse default element 流程;
		 * 比如当前 element 是普通的 <bean/>
		 * 若 element 的 namespace 不是 default namespace,将进入 parse custom element 流程;
		 * 比如当前 element 是 <context:annotation-config/> 或者是 <context:component-scan/>
		 *
		* */
		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 {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

首先,判断 root 元素的 namespace 对应的是不是 default namespace,若不是,将进入parse custom element,这里我们关注常规流程,既是当 root 元素的 namespace 是 default namespace 的流程。

  • DefaultBeanDefinitionDocumentReader中的parseDefaultElement()方法
		private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
			//处理import元素
			if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
				importBeanDefinitionResource(ele);
			}
			//处理别名元素
			else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
				processAliasRegistration(ele);
			}
			//处理bean标签
			else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
				//如果此根标签是bean的话
				processBeanDefinition(ele, delegate);
			}
	
			//处理Beans
			else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
				// recurse
				doRegisterBeanDefinitions(ele);
			}
		}

我们主要进行分析处理bean标签。

  • DefaultBeanDefinitionDocumentReader中的processBeanDefinition()方法
/**
	 * 解析bean的定义信息
	 * Process the given bean element, parsing the bean definition
	 * and registering it with the registry.
	 */
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		//生成BeanDefinitionHolder也就是这一步进行元素属性 id、name 、class等的识别以及生成Bean定义信息
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			//对 bean definition 进行相关的修饰操作
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance.
				//向工厂中注册最终修饰后的B
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// Send registration event.
			// 发送注册事件
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

通过processBeanDefinition()方法,就将我们xml配置的标签解析成BeanDefinition并且在最终将BeanDefinition注册到工厂中。到此为止完成了容器的创建和BeanDefinition的解析与注册。