今天面试被问到了Spring, 被面试官吊捶,痛定思痛,决定重新开始学习源码!
首先Spring容器的顶层容器接口是什么?BeanFactory和ApplicationContext, 其中ApplicationContext加了一些上下文的支持,更为高级一点。
以ClassPathXmlApplicationContext为例,容器初始化的入口方法在哪里呢?refresh()方法。
refresh()进来后有几处核心方法,我们来一一看下。
第一处 obtainFreshBeanFactory(): 这个方法又调用了refreshBeanFactory(),主要做了两件事情:创建容器,加载BeanDefinition,也就是我们常说的容器初始化三步走的第一步。
1 | DefaultListableBeanFactory beanFactory = createBeanFactory(); |
继续看下这个loadBeanDefinition():

可以看到里面的代码分为两部分,第一部分就是创建并设置一个读取器,用来读取资源文件,第二部分就是加载BeanDefinition,好,继续深入。

这里的代码也是很言简意赅,主要就是看我们的资源文件是以什么形式存在的,从而决定加载的是Resource还是String. 调试了一手发现我们这个走的是第一种类型,好,继续看:
代码依旧简单,我们来看最核心的这一行干了啥:
1 |
|
继续深入loadBeanDefinitions():
这个代码比较长,我就只截取最重要的部分进行说明了

这个代码主要做的工作就是负责从xml里面装载BeanDefinition.
继续看doLoadBeanDefinitions():

这里把xml转化成document, 然后执行registerBeanDefinitions()方法。
继续看registerBeanDefinitions():
代码依旧简单,主要分为两部分:首先创建一个Reader, 然后由reader进行注册。这两部分都很重要,我们一一来看:
创建Reader:
进行注册:

代码简单,继续深入:
doRegisterBeanDefinitions():
这里面最核心的一句:
parseBeanDefinitions(root, this.delegate);
继续深入:
parseDefaultElement(ele, delegate);
继续深入:

这里就厉害了,根据不同的节点名字进行不同的操作,那我们主要是加载bean, 那么节点名肯定就是BEAN_ELEMENT了,继续深入:

继续来看下是如何解析Element的:
解析的方法叫这个名字
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean)
可以看到它返回了一个BeanDefinitionHolder, BeanDefinitionHolder是对BeanDefinition的进一步封装,不信我们可以来看这个方法的返回值:
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
可以看到里面封装了beanName,alias以及beanDefinition,那么beanDefinition又是在哪里定义的?
这个方法里有这么一句代码:
1 | AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); |

