Spring 源码分析(2)--SpringIoC源码分析

IoC源码分析篇

参考博客:https://www.cnblogs.com/ITtangtang/articles/3978349.html#a3
xsd学习:https://www.w3school.com.cn/schema/schema_simple_attributes.asp
dtd学习:
https://www.w3school.com.cn/dtd/dtd_intro.asp

注意:本文基于spring-5.0.4.RELEASE版本源码的研究,本文叙述过程比较口水化,敬请包涵、谅解。

1、正文开始

spring官网文档:

https://docs.spring.io/spring/docs/5.0.4.RELEASE/spring-framework-reference/core.html#beans
研究Spring可以结合官网文档,学习效率可能加倍,不过要求你英文水平过关。废话少说,下文进行主题。


首先,我们看源码结构:(不知道如何下载源码并导入IDEA的小伙伴,可以参考Spring源码分析(1)–准备篇

在这里插入图片描述

我们从源码看出,spring项目由多个子模块组成,如:
spring-beans:主要放置BeanFactory、FactoryBean、DefaultListableBeanFactory、InitializingBean、BeanWrapper等,IoC将依赖该模块
spring-context:主要有ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、ApplicationContext、ApplicationContextAware等构成IoC容器基础的上下文
spring-core:spring的核心包,包括一些基础工具,如cglib、断言Assert、Base64Utils等,并提供annotation、convert的支持。
spring-aop:aop模块
spring-web:http相关一些工具类,在spring集成其他框架技术的时候会依赖该模块,如redistemplate等
spring-webmvc:springmvc模块


2、Spring IoC源码分析

源码工程好像没有所谓的spring-ioc?那么我要看IoC要哪里入手呢?
不知道大家还记不记得写过的Spring的hello world的代码,不记得,没关系,下面的代码我们一起回忆一波!


找到spring-test工程,创建Test类,然后输入spring入门hello world代码:
public class Test {

	public static void main(String[] args) {

		ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
		Object bean = context.getBean(Bea.class);

	}

}

application-context.xml配置文件:
application-context配置文件
现在有印象了吧,但之前我们只知道,写配置文件配置好我们需要的Bean,然后把配置文件交给Spring容器,它就可以帮我们自动构建我们需要的Bean对象。但是这个所谓的“自动”过程是怎样的呢?下面开始IoC源码的讲解,请人手一份spring-5.0.4.RELEASE版本的源码,跟着我的思路一起走。

2.1、IoC容器的初始化

spring-IoC时序图
时序图大图查看下载地址:https://www.processon.com/view/5d8a3140e4b011ca2aad49a1
时序图看第一遍不懂没关系,后面总结还会提到。

Spring IoC初始化分三部曲:定位加载注册,相信从别的博客,或者学习视频都能听到这几个名词,站在前辈的肩膀上学习,总能看得更远,更清晰,本文也是从其他博客学习借鉴过来的。下面我们带着对三部曲的疑惑,进入初始化过程:


2.1.1、配置资源的定位

点进Test.java的ClassPathXmlApplicationContext构造器,进入带String参数的构造器

ClassPathXmlApplicationContext:

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}

接下来进入this(new String[] {configLocation}, true, null);

如果你使用的IDE是IntelliJ IDEA,可以将光标放在this上,然后Ctrl +Alt + B 进入对应的实现,该方式的好处是:当调用接口或者抽象的抽象方法的时候,能直接进入对应的实现方法。

ClassPathXmlApplicationContext:

public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		//设置传入进来的父容器、创建Bean资源解析器
		super(parent);  // 代码1

		//将传入进来的配置文件路径(如spring/aaa.xml,spring/bbb.xml)保存起来
		setConfigLocations(configLocations); // 代码2

		if (refresh) {

			// 初始化最核心的方法,就是refresh()
			// 刷新过程将会将传入的资源(配置文件等)转换为BeanDefinition
			// 然后根据BeanDefinition生成Bean,再触发DI注入操作,为Bean的属性赋值
			// 主线
			refresh(); // 代码3 
		}
	}

注:阅读源码一定切忌陷入细节当中,导致头脑混乱。以下过程仅列出主线代码,分支代码详情可以点对应提示的链接观看。

上述代码分三步走,接下来介绍具体的功能:
代码1,调用父类AbstractApplicationContext的构造器,创建一个资源解析器,并将传递进来的parent容器保存起来(如果父容器不为空),然后将父容器的环境合并到当前容器环境中去。
详情可看:super(parent)

代码2,调用父类AbstractRefreshableConfigApplicationContext实现的setConfigLocations(String… locations)方法,将参数"application-context.xml"保存到AbstractRefreshableConfigApplicationContext的成员变量中,该成员变量将在代码3中提到如何使用,先做个铺垫。
详情可看:setConfigLocations(configLocations)

代码3,这一步是初始化过程最重要的一步,本文接下来将大概(将忽略不重要的步骤)介绍初始化的刷新过程。请根据上文提供的时序图,一起来看看refresh执行了哪些操作。

Ctrl +Alt + B 进入refresh对应实现:
由AbstractApplicationContext提供对应的refresh()方法的实现:

AbstractApplicationContext:

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			//调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
			prepareRefresh();

			//获取一个可刷新的BeanFactory,里面包括了一个抽象方法refreshBeanFactory();
			//用来执行子类的刷新,由子类实现
			//主线
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			//为BeanFactory配置容器特性,例如类加载器、事件处理器等
			prepareBeanFactory(beanFactory);

			try {
				//为容器的某些子类指定特殊的BeanPost事件处理器
				postProcessBeanFactory(beanFactory);

				//调用所有注册的BeanFactoryPostProcessor的Bean
				invokeBeanFactoryPostProcessors(beanFactory);

				//为BeanFactory注册BeanPost事件处理器
				//BeanPostProcessor是Bean后置处理器,用于监听容器出发的事件
				registerBeanPostProcessors(beanFactory);

				//初始化信息源,和国际化有关。
				initMessageSource();

				//初始化容器事件传播器
				initApplicationEventMulticaster();

				//调用子类的某些特殊Bean初始化方法
				onRefresh();

				//为事件传播器注册事件监听器
				registerListeners();

				//重点  重点  重点 
				//初始化所有的设置lazy-init=false的bean
				finishBeanFactoryInitialization(beanFactory); //代码4

				//初始化容器的生命周期事件处理器,并发布容器的生命周期事件
				finishRefresh();
			}

			catch (BeansException ex) {
				...
			}

			finally {
				...
			}
		}
	}

AbstractApplicationContext的refresh()是一个模板方法,我们接下来进入“主线”来看接下来如何执行实际上的refresh功能。
其中代码4 ,将触发依赖注入,后续会详细讲述。DI入口1

AbstractApplicationContext:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {

		//调用由子类提供的刷新BeanFactory的方法
		//主线
		refreshBeanFactory();

		//调用子类提供的,获取BeanFactory的方法
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}

obtainFreshBeanFactory()方法使用了委派模式,本身自己不做刷新功能,而是将实际刷新任务委派给子类
AbstractRefreshableApplicationContext的refreshBeanFactory()方法。我们从命名其实可以看出来,带有Refreshable的单词AbstractRefreshableApplicationContext类实际上,提供了刷新功能的实现。

Ctrl +Alt + B 进入refreshBeanFactory方法对应实现,进入AbstractRefreshableApplicationContext的实现:

AbstractRefreshableApplicationContext:

protected final void refreshBeanFactory() throws BeansException {
		//假如存在BeanFactory,则销毁、关闭它
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			//创建新的BeanFactory
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);

			//加载bean定义
			//主线
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			...
		}
	}

在AbstractRefreshableApplicationContext的refreshBeanFactory()方法中,主要做了四件事:
1)销毁关闭存在的BeanFactory
2)创建一个新的BeanFactory,其对应实现为DefaultListableBeanFactory


这里开始,BeanFactory开始登场,首先我们先查看下createBeanFactory方法对应的实现:

AbstractRefreshableApplicationContext:

protected DefaultListableBeanFactory createBeanFactory() {
		return new DefaultListableBeanFactory(getInternalParentBeanFactory());
	}

AbstractRefreshableApplicationContext的createBeanFactory方法事实上创建的默认BeanFactory是DefaultListableBeanFactory,下面我们看下DefaultListableBeanFactory与BeanFactory的关系图:
在这里插入图片描述
由图可看出,BeanFactory有三个子接口ListableBeanFactory、AutowireCapableBeanFactory、HierarchicalBeanFactory,分别代表的功能为ListableBeanFactory表示 Bean 是可列表的、AutowireCapableBeanFactory表示Bean 是可自动装配的、HierarchicalBeanFactory 表示Bean 是有继承关系的,也就是每个Bean 有可能有父 Bean。而DefaultListableBeanFactory实现了上述接口的全部功能。

3)进行Spring IoC初始化三步曲的加载步骤。(接下来2.1.2重点介绍)
4)将其创建好并自定义化后的DefaultListableBeanFactory保存到AbstractRefreshableApplicationContext的成员变量域中,为AbstractRefreshableApplicationContext的getBeanFactory提供值。当前先做铺垫,后面会介绍如何使用。

2.1.2、加载资源文件

Ctrl +Alt + B进入loadBeanDefinitions(beanFactory)对应的实现,AbstractXmlApplicationContext提供如下实现:

AbstractXmlApplicationContext:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		
		//创建一个Xml的BeanDefinition的读取器
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
		
		//为BeanDefinition读取器配置当前上下文的资源加载环境
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		//允许子类提供BeanDefinition的自定义初始化功能
		initBeanDefinitionReader(beanDefinitionReader);
		//执行事实上的加载
		//主线
		loadBeanDefinitions(beanDefinitionReader);
	}

进入主线loadBeanDefinitions方法:

AbstractXmlApplicationContext:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		
		//如果存在Resource资源配置,则进行加载		
		//当前获取的返回值为null,由于没有使用到构造器
		//ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, @Nullable ApplicationContext parent)
		//因此没有为Resource[] configResources赋值
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			reader.loadBeanDefinitions(configResources);
		}

		//getConfigLocations()返回ClassPathXMLApplicationContext第二步
		//setConfigLocations(configLocations)设置进来的xml配置文件的路径(即application-context.xml的路径)
		// 由AbstractRefreshableConfigApplicationContext提供具体实现
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			//主线
			//以下步骤将会把configLocation转化为Resource,其实最终调用的还是reader.loadBeanDefinitions(configResources)的方法
			reader.loadBeanDefinitions(configLocations);
		}
	}

接下来需要重点介绍一下XmlBeanDefinitionReader,我们先看下其继承哪些父类,实现了哪些接口:
在这里插入图片描述
由上图可看出,XmlBeanDefinitionReader继承了AbstractBeanDefinitionReader抽象类,并实现了BeanDefinitionReader接口。事实上,XmlBeanDefinitionReader只实现了int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException方法。其他定义在BeanDefinitionReader接口的抽象方法都由AbstractBeanDefinitionReader提供实现。接下来我们来看看XmlBeanDefinitionReader是如何实现加载BeanDefinition的。

Ctrl +Alt + B进入reader.loadBeanDefinitions(configLocations),该方法由AbstractBeanDefinitionReader提供对应实现:

AbstractBeanDefinitionReader:

    public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
		Assert.notNull(locations, "Location array must not be null");
		int counter = 0;
		for (String location : locations) {
			counter += loadBeanDefinitions(location);
		}
		return counter;
	}
	
	public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(location, null);
	}	

以上步骤加载的资源配置,还是本地资源的配置,该配置并不能让Spring解析识别,在接下来的步骤,将会统一转换为Resource对象:

AbstractBeanDefinitionReader:

    public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
        //获取资源加载器,该资源加载器事实上是ClassPathXmlApplicationContext对象,从哪里得知呢?
		ResourceLoader resourceLoader = getResourceLoader(); //代码5
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}
		
		//由于ClassPathXmlApplicationContext实现ResourcePatternResolver,将执行以下分支
		if (resourceLoader instanceof ResourcePatternResolver) {
			try {
			    //将字符串类型的配置文件转换为Spring能识别的Resource对象资源
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);//代码6
				//进入重载的loadBeanDefinitions(Resource..)方法
				//最终调用由XmlBeanDefinitionReader实现的loadBeanDefinitions(Resource resource)方法
				//主线
				int loadCount = loadBeanDefinitions(resources);
				if (actualResources != null) {
					for (Resource resource : resources) {
						actualResources.add(resource);
					}
				}
				...
				return loadCount;
			}
			catch (IOException ex) {
                ...
			}
		}
		else {
			//仅可以从绝对路径加载单独的resource
			Resource resource = resourceLoader.getResource(location);
			int loadCount = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
            ...
			return loadCount;
		}
	}

代码5:getResourceLoader()返回由AbstractXmlApplicationContext的loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法中beanDefinitionReader.setResourceLoader(this)设置进来的ClassPathXmlApplicationContext对象
详情可参考:getResourceLoader()

代码6:
((ResourcePatternResolver) resourceLoader).getResources(location)为AbstractApplicationContext实现的getResources方法。此时即调用ClassPathXmlApplicationContext对象的getResources方法,其由父类AbstractApplicationContext提供实现,返回值为ClassPathXmlApplicationContext代码1 super(parent)初始化时,调用AbstractApplicationContext()方法创建的PathMatchingResourcePatternResolver对象。
详情可参考:((ResourcePatternResolver) resourceLoader).getResources(location)

继续看主线,主线调用AbstractBeanDefinitionReader重载的loadBeanDefinitions(Resource… resources)方法:
AbstractBeanDefinitionReader:

    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
		Assert.notNull(resources, "Resource array must not be null");
		int counter = 0;
		for (Resource resource : resources) {
            //主线
			counter += loadBeanDefinitions(resource);
		}
		return counter;
	}

接着调用抽象方法loadBeanDefinitions(Resource resource),由XmlBeanDefinitionReader提供实际上的实现:

XmlBeanDefinitionReader:

    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}
	
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		...
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		//防止import标签循环加载资源
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			//获取Resource 的InputStream的IO流
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				//将InputStream包装为InputSource 
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					//假如有编码的配置,则进行设置操作
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				
				//主线
				//这里是具体的读取过程,有do**的名称可看出为具体操作的执行
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				//关闭从Resource中得到的IO流
				inputStream.close();
			}
		}
		catch (IOException ex) {
            ...
		}
	}

继续跟着主线走,进入真正执行加载过程。

XmlBeanDefinitionReader:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			//将InputSource转换为DOM对象,解析过程由documentLoader实现
			Document doc = doLoadDocument(inputSource, resource);
            //主线
			//这里是启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean配置规则,注册开始
			return registerBeanDefinitions(doc, resource);
		}
		catch (BeanDefinitionStoreException ex) {
			...
		}
	}

从以上代码看来,真正的加载过程,其实就是把资源转换为DOM对象,接着调用registerBeanDefinitions方法,开始IoC三步曲的注册过程,即2.1.3的内容。有兴趣了解如何将资源转换为DOM对象的,请查看:doLoadDocument(inputSource, resource)

2.1.3、注册BeanDefinition

XmlBeanDefinitionReader:

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//创建BeanDefinitionDocumentReader来对xml格式的BeanDefinition进行解析
		//其对应的实现为DefaultBeanDefinitionDocumentReader
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

		//获得容器中已注册的Bean数量,由于递归调用该方法,因此需要累加之前已有的BeanDefinition
		int countBefore = getRegistry().getBeanDefinitionCount();    //代码7
		
		//主线
		//解析过程入口,这里使用了委派模式,BeanDefinitionDocumentReader只是个接口,
		//具体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));  //代码8
		//统计解析的Bean数量
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

代码7:getRegistry()事实上获取DefaultListableBeanFactory对象
在创建XmlBeanDefinitionReader对象的时候,传入构造器是DefaultListableBeanFactory对象,此时XmlBeanDefinitionReader的父类AbstractBeanDefinitionReader会将传入的参数保存至成员变量,并在getRegistry调用 的时候,返回该对象。
在这里插入图片描述
代码8:createReaderContext(resource)创建Xml读取器的上下文对象,该类是对资源和XmlBeanDefinitionReader对象的一个保存。我们看下该类的成员变量:
在这里插入图片描述
其父类成员变量:
在这里插入图片描述

继续回到主线,调用BeanDefinitionDocumentReader的registerBeanDefinitions(Document doc, XmlReaderContext readerContext)方法,该方法的具体实现由DefaultBeanDefinitionDocumentReader提供。

DefaultBeanDefinitionDocumentReader:

    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		//获得XML描述符
		this.readerContext = readerContext;  //代码9
		logger.debug("Loading bean definitions");
		//获得Document的根元素
		Element root = doc.getDocumentElement();  //代码10
		//主线
		doRegisterBeanDefinitions(root);
	}

代码9:保存Xml读取器的上下文对象
代码10:获取Document的根元素,相当于获取beans元素,我们可以参考下面的配置文件,其中 <beans/>为配置文件的根元素。在这里插入图片描述
其中包含6个属性,分别是xmlns、xmls:xsi、xsi:schemaLocationss、default-autowire、default-merge、default-lazy-init,并且包含了两个子节点<bean/><beans/>
其中default-autowire、default-merge、default-lazy-init为默认配置属性,就算上文配置文件没有加入此值,也会默认加入这三个值。

继续回到主线,解析注册root节点。

DefaultBeanDefinitionDocumentReader:

    protected void doRegisterBeanDefinitions(Element root) {
		//任何嵌套的<beans>元素都会在这个方法中引起递归。
		// 这种行为模拟了一堆委托,但实际上并不需要一个。
		// 为了正确地传播和保留<beans>default-*属性,请跟踪
		// 当前(父)委托,它可能为空。创建新的(子)委托
		// ,其中包含一个对父类的引用,以便进行回退
		// ,然后最终将this.delegate重置为其原始(父类)引用。
		// 这种行为模拟了一堆委托,但实际上并不需要一个。(有道翻译)

		//具体的解析过程由BeanDefinitionParserDelegate实现
		//BeanDefinitionParserDelegate中定义了Spring Bean定义XML文件的各种元素
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent); //代码11

		//假如配置了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)) {
					if (logger.isInfoEnabled()) {
						logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
								"] not matching: " + getReaderContext().getResource());
					}
					return;
				}
			}
		}

		//在解析Bean定义之前,进行自定义的解析,增强解析过程的可拓展性,默认0实现
		preProcessXml(root);

		//主线
		//使用Spring的Bean规则从Document的根元素开始进行Bean定义的解析
		parseBeanDefinitions(root, this.delegate);
		
		//在解析Bean定义之后,进行自定义的解析,增加解析过程的可拓展性,默认0实现
		postProcessXml(root);

		this.delegate = parent;
	}

以上代码,虽然嘴上(方法名称为doRegisterBeanDefinitions)说着注册注册,但是事实却执行了两件事:

第一件事是代码11的内容,创建了Spring规则的一个委托,这个委托。详细步骤解析可以查看:createDelegate(getReaderContext(), root, parent)

第二件事是开始把根元素解析为BeanDefinition,然后执行真正的注册,这个贯穿全文的BeanDefinition的解析终于要来了。在接下来的代码片段,我们一起揭开BeanDefinition解析过程神秘的面纱。

DefaultBeanDefinitionDocumentReader:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		//Bean定义的Document对象使用了Spring默认的XML命名空间
		if (delegate.isDefaultNamespace(root)) {
			//获取Bean定义的Document对象根元素的所有子节点
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				//获得Document节点是XML元素节点
				if (node instanceof Element) {
					Element ele = (Element) node;
					//Bean定义的Document的元素节点使用的是Spring默认的XML命名空间
					if (delegate.isDefaultNamespace(ele)) {

						//主线
						// 这里只处理 namespace 为 http://www.springframework.org/schema/beans 的标签
						parseDefaultElement(ele, delegate);
					}
					else {
						// 用户自定义命名空间
						//对自定义标签处理 会解析 <context:component-scan base-package="com.zj.scan"/> 或者自定义 dubbo等  
						//重点 重点  重点 注解bean的解析入口
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			//自定义标签解析
			delegate.parseCustomElement(root);
		}
	}

上述代码判断传入的元素是否为Spring默认命名空间(http://www.springframework.org/schema/beans),如果是则进行Spring默认自带的解析过程,否则执行用户自定义解析,用户自定义解析将包括注解、aop等的解析。具体请参考: delegate.parseCustomElement(root)

在这里插入图片描述
上图是一个spring默认命名空间的示例,其中只要是spring默认标签,如<beans><bean><import><alias>标签,就算没有后面的xmlns=“http://www.springframework.org/schema/beans”,也会在执行解析的时候,默认加进来。

继续回到主线,我们来看Spring默认解析过程:

DefaultBeanDefinitionDocumentReader:

    //使用Spring的Bean规则解析Document元素节点
	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		//如果元素节点是<Import>导入元素,进行导入解析
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);  //代码12
		}
		//如果元素节点是<Alias>别名元素,进行别名解析
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);    //代码13
		}
		//元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,按照Spring的Bean规则解析元素
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			//主线
			processBeanDefinition(ele, delegate);   //代码14
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);   //代码15
		}
	}

代码12:遇到<import>元素,则进行导入操作,有兴趣了解具体过程,可查看:
importBeanDefinitionResource(ele)
代码13:遇到<Alias>元素,则进行导入操作,有兴趣了解具体过程,可查看:
processAliasRegistration(ele)
代码14:解析<bean>元素主线,在接下来讲述。
代码15:遇到<beans>元素,则进行开始调用doRegisterBeanDefinitions(ele)进行递归解析

继续主线代码,终于调用到BeanDefinition的解析过程了,期待吧,往下看:

DefaultBeanDefinitionDocumentReader:

    //解析Bean定义资源Document对象的普通元素
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {

		//将Element元素--->BeanDefinition的过程在此
		//对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);  //代码16

		if (bdHolder != null) {
			//修饰BeanDefinition
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
			
				//主线
				//向Spring IOC容器注册解析得到的Bean定义,这是Bean定义向IOC容器注册的入口
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			
			//在完成向Spring IOC容器注册解析得到的Bean定义之后,发送注册事件
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

代码16:将XML元素解析为BeanDefinition,并将其封装在BeanDefinitionHolder中。篇幅比较长,具体详情可以参考:
delegate.parseBeanDefinitionElement(ele)

返回主线,继续注册过程。BeanDefinitionReaderUtils工具类的registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法具体实现如下:

BeanDefinitionReaderUtils:

//将解析的BeanDefinitionHold注册到容器中
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		//获取解析的BeanDefinition的名称
		String beanName = definitionHolder.getBeanName();
		
		//主线
		//向IOC容器注册BeanDefinition
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		//如果解析的BeanDefinition有别名,向容器为其注册别名
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}

[final] 注册的最终实现过程:
事实上BeanDefinitionReaderUtils只是帮忙调用BeanDefinitionRegistry 来完成注册。而BeanDefinitionRegistry 则是DefaultListableBeanFactory完成注册过程。

DefaultListableBeanFactory:

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

		oldBeanDefinition = this.beanDefinitionMap.get(beanName);
		if (oldBeanDefinition != null) {
			//若通过beanName获取的BeanDefinition已存在,且不允许覆盖,则抛错
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound.");
			}
			//以下是执行一些警告日志的输出,非关注重点
			else if (...) {
				...
			}
			//若通过beanName获取的BeanDefinition已存在,并且允许覆盖,则执行添加操作
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			if (hasBeanCreationStarted()) {
				synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
						
					}
				}
			}
			else {

				//如果通过beanName获取不到值,说明当前的BeanDefinition为新的值,直接执行添加操作
				this.beanDefinitionMap.put(beanName, beanDefinition);
				//保存beanName
				this.beanDefinitionNames.add(beanName);
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		//假如覆盖了旧的BeanDefinition或者单例容器
		if (oldBeanDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
	}

DefaultListableBeanFactory的registerBeanDefinition为初始化三步曲最后一步注册的实际实现,从以上过程看,其实初始化过程就是对本地配置文件资源(application-context.xml)的定位,然后加载成document对象,最终解析成BeanDefinition并注册到IoC容器中去。

2.1.4、IoC初始化总结

上述的过程讲述了如何将资源(一般是XML配置文件)解析为DOM元素,再解析成BeanDefinition对象的一个过程。我们看回到时序图,回首定位加载注册初始化三部曲:
spring-IoC时序图
时序图大图查看下载地址:https://www.processon.com/view/5d8a3140e4b011ca2aad49a1
ClassPathXmlApplicationContext执行了如下任务:
1)ClassPathXmlApplicationContext构造器出发,首先设置保存父容器假如存在的话,并初始化一个ResourcePatternResolver对象(对应实现为PathMatchingResourcePatternResolver)
2)setConfigLocations方法再将传入的字符串(配置文件路径)保存到父类AbstractRefreshableConfigApplicationContext的成员变量中。
3)启动刷新过程。接着一大堆复杂逻辑,其实都是从刷新出发。刷新的话,到AbstractApplicationContext类。

AbstractApplicationContext主要提供了一个refresh的模板方法,而其中最重要的是obtainFreshBeanFactory方法,来获取一个可刷新的BeanFactory。其委派了子类AbstractRefreshableApplicationContext执行实际上的刷新和创建BeanFactory的操作。

AbstractRefreshableApplicationContext创建了DefaultListableBeanFactory作为BeanFactory对象的实例,并调用AbstractXMLApplicationContext的加载方法开始加载配置。

AbstractXMLApplicationContext创建了XmlBeanDefinitionReader对象,用来读取XML配置文件。并调用XmlBeanDefinitionReader父类AbstractBeanDefinitionReader的loadBeanDefinitions执行配置文件的加载。而最近加载由XMLBeanDefinitionReader来完成实现。

XMLBeanDefinitionReader的加载方法,完成由Resource资源转换InputSource资源,再转换为Document对象,然后开始注册过程。注册过程首先创建BeanDefinitionDocumentReader对象,其真正实现为DefaultBeanDefinitionDocumentReader。

DefaultBeanDefinitionDocumentReader创建BeanDefinitionParserDelegate对象,并委派其进行Document对象的解析。

BeanDefinitionParserDelegate对象使用Spring规则和用户自定义规则,对配置文件进行解析。最终将解析好的BeanDefinition通过BeanDefinitionReaderUtils工具类,注册到DefaultListableBeanFactory中去。

2.2、DI注入过程

DI注入有两种方式触发,
第一种是在IoC初始化过程中,会将所有设置lazy-init=false的Bean自动执行注入。
第二种,是在调用context.getBean(beanName)的时候,手动出发注入操作。

2.2.1 DI入口1:

我们回顾一下IoC初始化的时候,调用AbstractApplicationContext的refresh方法执行刷新的时候,其中有一个标注了重点的方法,就是触发DI注入的入口之一了。

AbstractApplicationContext:

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			//调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
			prepareRefresh();

			//获取一个可刷新的BeanFactory,里面包括了一个抽象方法refreshBeanFactory();
			//用来执行子类的刷新,由子类实现
			//主线
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			//为BeanFactory配置容器特性,例如类加载器、事件处理器等
			prepareBeanFactory(beanFactory);

			try {
				//为容器的某些子类指定特殊的BeanPost事件处理器
				postProcessBeanFactory(beanFactory);

				//调用所有注册的BeanFactoryPostProcessor的Bean
				invokeBeanFactoryPostProcessors(beanFactory);

				//为BeanFactory注册BeanPost事件处理器
				//BeanPostProcessor是Bean后置处理器,用于监听容器出发的事件
				registerBeanPostProcessors(beanFactory);

				//初始化信息源,和国际化有关。
				initMessageSource();

				//初始化容器事件传播器
				initApplicationEventMulticaster();

				//调用子类的某些特殊Bean初始化方法
				onRefresh();

				//为事件传播器注册事件监听器
				registerListeners();

				//重点  重点  重点 
				//初始化所有的设置lazy-init=false的bean
				finishBeanFactoryInitialization(beanFactory); //代码17

				//初始化容器的生命周期事件处理器,并发布容器的生命周期事件
				finishRefresh();
			}

			catch (BeansException ex) {
				...
			}

			finally {
				...
			}
		}
	}

代码17的finishBeanFactoryInitialization方法,将触发DI注入,下面看看起如何实现:

AbstractApplicationContext:

	//完成此上下文的bean工厂的初始化,初始化所有剩余的单例bean。
	protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {

		//为上下文初始化转化一个服务(conversionService)
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}

		//当bean没有 post-processor的时候注册一个默认的嵌入值解析器
		if (!beanFactory.hasEmbeddedValueResolver()) {
			beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
		}

		//尽早初始化LoadTimeWeaverAware bean,以便尽早注册它们的转换器。
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
		for (String weaverAwareName : weaverAwareNames) {
			getBean(weaverAwareName);
		}

		//为了类型匹配,停止使用临时类加载器
		beanFactory.setTempClassLoader(null);

		//缓存容器中所有注册的BeanDefinition元数据,以防被修改
		beanFactory.freezeConfiguration();

		// Instantiate all remaining (non-lazy-init) singletons.
		//实例化所有剩下的单例 (非懒加载的)
		beanFactory.preInstantiateSingletons();
	}

DefaultListableBeanFactory的preInstantiateSingletons方法:

DefaultListableBeanFactory:

public void preInstantiateSingletons() throws BeansException {
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Pre-instantiating singletons in " + this);
		}


		//遍历一个副本以允许init方法注册新的BeanDefinition
		//虽然这可能不是常规工厂引导的一部分,但它在其他方面工作得很好。
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);


		//触发所有非懒加载单例bean的初始化
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				//假如当前的beanName为一个FactoryBean,则加入& 字符,以取得FactoryBean本身
				if (isFactoryBean(beanName)) {
					//getBean调用,触发依赖注入
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						final FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						//执行一些安全权限方面的操作,详情可以了解AccessController和SecurityManager的工作原理
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
											((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
							//验证通过,则进行注入操作
							getBean(beanName);
						}
					}
				}
				else {
					getBean(beanName);
				}
			}
		}


		//触发所有适用的bean初始化后的回调事件
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}

DefaultListableBeanFactory的preInstantiateSingletons方法,其实就是将其成员变量this.beanDefinitionNames变量,该变量保存着所有当前DefaultListableBeanFactory对象添加的BeanDefinition的名称。
后续通过遍历beanDefinitionNames,然后调用getBean方法,来执行实例的构建和其属性的注入。getBean方法将在2.2.2中重点讲述。

2.2.2 DI入口2:

还记得上文中Spring的Helloworld代码吗?不记得没关系,下面再回顾一下:

public class Test {

	public static void main(String[] args) {

		ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
		Object bean = context.getBean(Bean.class);

	}

}

刚才的初始化过程是从ClassPathXmlApplicationContext的构造器开始的,那DI注入过程的入口呢?从上文得知ClassPathXmlApplicationContext负责BeanDefinition的创建,但是并没有真正的实例,相信各位已经知道了,没错了,那么getBean()方法将执行DI注入的过程了。不过还有一点需要注意,DI注入过程不仅限于getBean()方法才执行,在Bean定义资源中为<Bean>元素配置了lazy-init属性,即让容器在解析注册Bean定义时进行预实例化,也可触发DI注入。下面开始DI注入过程的源码分析:

点击进行getBean的实现。

AbstractApplicationContext:

	public Object getBean(String name) throws BeansException {
		assertBeanFactoryActive();
		return getBeanFactory().getBean(name);
	}

AbstractBeanFactory:

	public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}

AbstractBeanFactory:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		// 转换bean的名称,由于传入的name有可能是获取FactoryBean的解引用(&+beanName,会去除&)
		// 假如是别名,则获取别名关联的beanName
		final String beanName = transformedBeanName(name);  //代码18
		Object bean;

		// 首先检查单例的缓存中是查找bean,也是为了防止多次创建单例模式的bean
		Object sharedInstance = getSingleton(beanName);  //代码19

		// 判断sharedInstance对象不为空,且args为空,原因是args仅用在创建新实例的时候使用,而非检索现有实例,方法注释写的
		if (sharedInstance != null && args == null) {
			...
			//获取Bean实例
			//情况1:普通bean,则返回直接普通bean实例
			//情况2:FactoryBean,分两种。一种是name里面带&,则返回FactoryBean本身,否则,返回由FactoryBean创建的实例
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);  //代码20
		}

		else {

			// 假如原型实例已创建,则报错,防止循环引用
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}


			// 检查当前容器和父容器是否存在BeanDefinition
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// 没有找到,则在父容器中查找
				String nameToLookup = originalBeanName(name);
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {

					// 有明确的参数的时候委派给父容器获取bean
					// 此时会让父级容器根据指定名称和参数查找bean
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else {

					// 没有参数的时候  委派给标准的getBean方法
					// 此时会让父级容器根据指定类型和名称查找bean
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
			}

			// 创建的Bean是否需要进行类型验证,一般不需要
			if (!typeCheckOnly) {
				// 向容器标记指定的Bean已经被创建
				markBeanAsCreated(beanName);
			}

			try {
				// 获取Root级别的BeanDefinition
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				// 保证当前bean所依赖的bean的初始化
				// 意思是先初始化当前bean所依赖的bean,即调用getBean方法执行
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						// 判断是否循环依赖
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						// 注册依赖bean
						registerDependentBean(dep, beanName);
						// 递归getBean方法
						getBean(dep);
					}
				}

				// 完成依赖的配置后,以下代码开始创建实例

				// 单例
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							// 主线
							// 创建Bean
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					//获取Bean实例
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
				// 原型
				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						// 创建原型Bean前的回调方法调用
						beforePrototypeCreation(beanName);
						// 创建原型Bean
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						// 创建原型Bean之后的回调方法调用
						afterPrototypeCreation(beanName);
					}
					//获取Bean实例
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}
				// 其他 例如Web应用程序中的request、session、application等生命周期
				else {
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						//获取Bean实例
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

		// 检查对于返回值的类型是否有类型限制,有则转为对应类型的
		if (requiredType != null && !requiredType.isInstance(bean)) {
			try {
				T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
				if (convertedBean == null) {
					throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
				}
				return convertedBean;
			}
			catch (TypeMismatchException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}

以上代码虽然很长,但是总结下来,主要完成:
代码18:执行beanName的转换,将别名转换为实际关联的beanName,或者去除FactoryBean的前缀&
代码19:获取是否有单例缓存,接下来会分两条分支,一条是存在缓存单例的bean,另一种则没有,没有的情况比较复杂,后面继续讲述。当有缓存单例bean的时候,调用代码3,getObjectForBeanInstance方法,获取Bean实例,该方法在上述代码中多处地方使用到。
代码20:判断是普通bean还是FactoryBean,然后执行对应处理后,返回实例。对于代码20过程有兴趣的,可以参考:getObjectForBeanInstance(instance,name,beanName,mbd)

如果对FactoryBean和BeanFactory不懂的,可以参考:Factory和BeanFactory的区别

代码19中,另外一个分支,假如没有缓存的单例bean的时候,首先检查父容器是否存在该bean,有则返回该实例。
没有,则开始执行创建bean操作,分别根据BeanDefinition的scope对应创建bean,此时就会调用到主线的createBean(beanName,mbd,args)方法。

接下来继续跟着主线,查看createBean执行了哪些内容:

AbstractAutowireCapableBeanFactory:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		if (logger.isDebugEnabled()) {
			logger.debug("Creating instance of bean '" + beanName + "'");
		}
		RootBeanDefinition mbdToUse = mbd;


		// 确保此时bean类已经解析,并克隆BeanDefinition ,因为动态解析的类不能被存储在共享合并的BeanDefinition
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);  //代码21
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			mbdToUse = new RootBeanDefinition(mbd);
			mbdToUse.setBeanClass(resolvedClass);
		}

		// 准备bean中方法的覆盖,并检查方法是否存在
		try {
			mbdToUse.prepareMethodOverrides();     //代码22
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
					beanName, "Validation of method overrides failed", ex);
		}

		try {
			// 给BeanPostProcessors一个机会来返回代理而不是目标bean的实例。
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);      //代码23
			if (bean != null) {
				return bean;
			}
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
					"BeanPostProcessor before instantiation of bean failed", ex);
		}

		try {
			// 主线
			// 执行真正创建Bean操作
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);   //代码24
			...
			return beanInstance;
		}
		catch (...) {
			...
		}
		
	}

上述代码,执行如下过程:
代码21:首先对BeanClass进行处理,即判断需要创建的Bean是否可以实例化,是否可以通过当前的类加载器加载
代码22:进行方法覆盖的准备,并校验方法是否存在
代码23:如果Bean配置了初始化前和初始化后的处理器,则试图返回一个Bean的代理对象
代码24:执行实际上创建bean的操作,下面讲解。

AbstractAutowireCapableBeanFactory:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		//实例化Bean
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			//创建实例  重点
			instanceWrapper = createBeanInstance(beanName, mbd, args);  //代码25
		}
		final Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		//允许post-processors修改合并后的BeanDefinition
		//加锁,保证原子性操作
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					//执行post-processors
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				//设置post-processors的执行标识
				mbd.postProcessed = true;
			}
		}

		//缓存单例以便处理循环引用,即使是像BeanFactoryAware这样的生命周期接口触发的
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isDebugEnabled()) {
				logger.debug("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		//初始化Bean实例
		Object exposedObject = bean;
		try {
			
			//注入过程  重点
			populateBean(beanName, mbd, instanceWrapper); //代码26
			// 将会执行Bean的初始化  
			// 将会触发postProcessBeforeInitialization和postProcessAfterInitialization
			exposedObject = initializeBean(beanName, exposedObject, mbd);  //代码27
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		
		if (earlySingletonExposure) {
			//获取指定名称的已注册的单态模式Bean对象
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				//根据名称获取的已注册的Bean和正在实例化的Bean是同一个
				if (exposedObject == bean) {
					//当前实例化的Bean初始化完成
					exposedObject = earlySingletonReference;
				}
				//当前Bean依赖其他Bean,并且当发生循环引用时不允许新创建实例对象
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					//获取当前Bean所依赖的其他Bean
					for (String dependentBean : dependentBeans) {
						//对依赖Bean进行类型检查
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
						...
					}
				}
			}
		}


		//注册完成依赖注入的Bean
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

代码25:createBeanInstance 创建Bean实例,普通Bean创建的主要方式。创建出来后,其里面的属性并没有值,因此需要下一步依赖注入。创建Bean实例具体详情请看:
createBeanInstance (beanName, mbd, args)
代码26: populateBean 依赖注入的处理。上述代码1仅完成对象的创建,但是未注入其属性值,通过populateBean 完成属性值得注入。具体详情请看:
populateBean(beanName, mbd, instanceWrapper)
代码27:initializeBean 初始化Bean,其实是调用了配置了init-method的方法。具体详情请看:
initializeBean(beanName, exposedObject, mbd)

自此,IoC初始化以及DI注入的全部过程已介绍完毕。很多细节其实并没有深入研究,因为毕竟spring是个团队型优秀项目,要扣全部细节全部吃透,需要花费大量时间,本文仅对大致的思路进行讲述,其余的还需留给读者一同深入研究。

3、IoC总结

回顾第2节提到的Helloworld程序Test.java

public class Test {

	public static void main(String[] args) {

		ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml"); //代码28
		Object bean = context.getBean(Bea.class);  //代码29

	}

}

经过第2节的一路分析,我们得知代码28构造器的执行,其实包含了IoC初始化和DI注入。而这整个过程,其实就是将传入的配置文件"application-context.xml"中配置的Bean构建成一个一个BeanDefinition,然后以beanName作为key,BeanDefinition作为值存放到ClassPathXmlApplicationContext父类AbstractRefreshableApplicationContext的成员变量DefaultListableBeanFactory中去。
然后再通过refresh方法中调用finishBeanFactoryInitialization方法,遍历beanDefinitionNames,触发了DI注入操作。DI注入操作将会根据传入的beanName,去获取BeanDefinition,并将其依赖的Bean优先创建,再根据对应BeanDefinition中定义的属性值,注入到新创建出来的实例中去。
代码29同样可以触发实例创建和依赖注入过程,与refresh方法调用的finishBeanFactoryInitialization方法,最终都是执行了AbstractBeanFactory的doGetBean。

你可能以为总结完就应该结束了吧,文章这么长,总应该是个头了吧,但不好意思,后面还有一章,嘻嘻。

4、Web项目对Spring IoC容器的初始化

我们做java的,几乎都是做Web项目的,因此Spring在Web项目中的初始化过程,才是我们最终关心的。下面将讲述JavaWeb项目初始化IoC容器的过程。
对于传统SSM(Spring+SpringMVC+Mybatis)项目,我们都应该对web.xml有印象,我们先看看一个传统SSM项目的Web.xml文件简单示例:

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<display-name>helloworld</display-name>

	<!--Spring IoC配置文件位置-->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:conf/applicationContext.xml</param-value>
	</context-param>
	<!--配置初始化IoC容器的监听器-->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	
	<!--SpringMVC的配置-->
	<servlet>
		<servlet-name>spring</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath*:conf/spring-mvc.xml</param-value>
		</init-param>

		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>spring</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
</web-app>

上述xml文件配置了ContextLoaderListener监听器,用来初始化Spring IoC容器,并且配置context-param为配置文件所在路径。下面我们看看ContextLoaderListener是如何完成IoC容器的初始化的。

ContextLoaderListener:

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

	public ContextLoaderListener() {
	}
	
	public ContextLoaderListener(WebApplicationContext context) {
		super(context);
	}

	/**
	 * 初始化入口
	 */
	@Override
	//监听器会默认调用该方法
	public void contextInitialized(ServletContextEvent event) {
		//初始化WebApplicationContext
		initWebApplicationContext(event.getServletContext());
	}

	@Override
	public void contextDestroyed(ServletContextEvent event) {
		closeWebApplicationContext(event.getServletContext());
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}
}

其实ContextLoaderListener实现了ServletContextListener接口,声明其本身为一个Web监听器,在web项目启动时候(filter和Serlvet初始化前),会调用contextInitialized方法。而ContextLoaderListener在初始化方法中,执行了initWebApplicationContext方法,来初始化WebApplicationContext。这个在上文有讲过,也是IoC容器中的一员。

ContextLoader:

	//初始化spring的根上下文
	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		//spring在完成webApplicationContext初始化后,会将其保存在servletContext中存储
		//其中key为org.springframework.web.context.WebApplicationContext.ROOT
		//判断该属性值是否存在,如果存在,说明webApplicationContext已初始化,则抛出异常
		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - " +
					"check whether you have multiple ContextLoader* definitions in your web.xml!");
		}
		...
		long startTime = System.currentTimeMillis();

		try {

			if (this.context == null) {
				//开始创建spring的上下文,默认创建XmlWebApplicationContext对象
				// 返回的时候,强转为ConfigurableWebApplicationContext对象
				this.context = createWebApplicationContext(servletContext); //代码30
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				//默认进入该分支,除非重写了createWebApplicationContext方法,修改对应返回值

				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {

					if (cwac.getParent() == null) {
						//设置父容器
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					//配置(设置id)和刷新
					//重点
					configureAndRefreshWebApplicationContext(cwac, servletContext); //代码31
				}
			}
			//将初始化完成的spring上下文,保存在servletContext中,key为org.springframework.web.context.WebApplicationContext.ROOT
			//这就是为什么说servletContext为spring IoC容器提供宿主环境
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);  //代码32

			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}
			...
			return this.context;
		}
		catch (...) {
			...
		}
		
	}

代码30,创建WebApplicationContext对象,ContextLoader.java默认给了实现,方法为protected,可以被子类覆盖,下面介绍默认实现:

ContextLoader:

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
		//确定使用哪个WebApplicationContext的实现类,默认实现类为XmlWebApplicationContext
		Class<?> contextClass = determineContextClass(sc);
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
					"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
		}
		return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
	}
	
	protected Class<?> determineContextClass(ServletContext servletContext) {
		//从servletContext中获取是否有设置contextClass
		String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
		if (contextClassName != null) {
			try {
				//假如servletContext配置了contextClass,则根据其配置,通过反射创建对应WebApplicationContext的实现类
				return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load custom context class [" + contextClassName + "]", ex);
			}
		}
		else {
			//当没有配置contextClass,则根据默认策略,将org.springframework.web.context.support.XmlWebApplicationContext
			// 作为WebApplicationContext的实现类,当然,默认值spring也是通过配置文件配置的
			contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
			try {
				//通过反射创建XmlWebApplicationContext对象
				return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load default context class [" + contextClassName + "]", ex);
			}
		}
	}

回到initWebApplicationContext方法的代码32(代码31下面重点介绍),将创建好的WebApplicationContext对象,保存在servletContext中,其中key为org.springframework.web.context.WebApplicationContext.ROOT与initWebApplicationContext方法前面获取对应。

代码31将对WebApplicationContext的id进行配置,并调用容器的refresh方法,对应第2节中AbstractApplicationContext的刷新,由于当前的容器具体实现为XmlWebApplicationContext,其refresh实现由AbstractApplicationContext提供:

AbstractApplicationContext:

	//配置和刷新容器
	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {

			//假如ServletContext中有配置contextId,则将其设置为容器的id,否则生成默认id
			String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
			if (idParam != null) {
				wac.setId(idParam);
			}
			else {
				//假如id为空,则生成默认id
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(sc.getContextPath()));
			}
		}
		//保存ServletContext
		wac.setServletContext(sc);

		//获取我们在web.xml里面配置的context.param的参数,即spring配置文件路径
		String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		if (configLocationParam != null) {
			//设置配置文件路径
			wac.setConfigLocation(configLocationParam);
		}

		//配置ServletContext提供的属性值
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
		}


		customizeContext(sc, wac);
		//执行刷新,当前容器为XmlWebApplicationContext对象
		//因此调用其父类AbstractApplicationContext的方法进行刷新
		wac.refresh();   //重点 
	}

上述代码,其实就是设置了容器id和对应的配置文件路径,最后执行了容器的refresh方法,其具体调用的为AbstractApplicationContext提供的refresh方法。该方法在第2节中已介绍,可以点击下面方法跳转查看refresh
我们看下XmlWebApplicationContext的继承关系图:
在这里插入图片描述
从第2节我们已经知道,执行完refresh方法后,会完成IoC容器的初始化和DI的注入,因此JavaWeb 关于IoC的初始化已介绍完毕。
在上述配置文件中,大家应该有印象配置了DispatcherServlet这个servlet吧,这里没有介绍,将放在下一篇文章讲,有兴趣的小伙伴可以看看。

下一篇:Spring 源码分析(3)–SpringMVC分析篇)











本人才疏学浅,请各位多多包涵,有任何疏漏错误之处,敬请提出。


版权声明:本文为aidaowuqiong原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。