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配置文件:
现在有印象了吧,但之前我们只知道,写配置文件配置好我们需要的Bean,然后把配置文件交给Spring容器,它就可以帮我们自动构建我们需要的Bean对象。但是这个所谓的“自动”过程是怎样的呢?下面开始IoC源码的讲解,请人手一份spring-5.0.4.RELEASE版本的源码,跟着我的思路一起走。
2.1、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对象的一个过程。我们看回到时序图,回首定位、加载、注册初始化三部曲:
时序图大图查看下载地址: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分析篇)
本人才疏学浅,请各位多多包涵,有任何疏漏错误之处,敬请提出。