今天面试被问到了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); |