契机:之前写了很多宏观分析Spring的文章,很多细节都不是很清楚,所以想开一个源码阅读的专栏,希望能够坚持下去。
本来不打算写关于IOC的文章的,后来看了下之前写的文章,都相对浅薄没有深入源码分析,于是做本文。
IOC部分打算分为两篇文章来分别讲解,本文是该系列之一『容器启动阶段』
入口点
1 | public class EntranceApp { |
1 | public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) |
上面这个例子我们看到是在main方法中直接触发的Spring,而我们日常Web开发中是在哪儿&由谁来执行上面这部分代码的呢?
我们进入Java Web项目的web.xml中会发现这样一段代码1
2
3<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
而上述xml配置是基于Spring的Web项目必备的,我们看看这个ContextLoaderListener干了啥?1
2
3
4
5
6
7
8
9
10
11
12
13
14public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
/**
* Close the root web application context.
*/
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
该类实现了ServletContextListener接口,并且覆写了contextInitialized方法和contextDestroyed方法。前者会在容器启动阶段被调用,且在任何servlet or filter执行之前;后者会在容器销毁阶段被调用,且在全部servlet and filter被销毁后执行。
在容器启动阶段,会执行ContextLoaderListener#contextInitialized方法,而该方法会调用下面这段逻辑1
2// wac is instance of ConfigurableWebApplicationContext
wac.refresh();
我们发现最终也执行到了大名鼎鼎的refresh方法中
1 |
|
本文主要分析的部分是invokeBeanFactoryPostProcessors(beanFactory)
和它之前的部分
1)准备重启(refresh)
2)获得BeanFactory(这里获得的BeanFactory已经持有注册好的BeanDefination了)
3)准备BeanFactory
4)对获得到的BeanFactory进行后处理
5)调用BeanFactoryPostProcessors
本文的重点在2,3,5
obtainFreshBeanFactory()
refreshBeanFactory()
1 |
|
首先,判断当前是否已经有BeanFactory了,如果有那么要把beans全部销毁并关闭BeanFactory。
接着,创建BeanFactory
最后,解析BeanDefination并注册到容器中
我们重点看后两个步骤
createBeanFactory()
1 | protected DefaultListableBeanFactory createBeanFactory() { |
代码最后追到上面那里,首先调用父类的构造函数(空实现),接着将上面三个XXXAware类放到Set中,在该Set中的类的Xxx属性会由容器注入,且在依赖检查的时候会被忽略。依赖检查已经被Spring抛弃了。所以不必过于纠结这里
延伸知识:
In Spring,you can use dependency checking feature to make sure the required properties have been set or injected.
none – No dependency checking.
simple – If any properties of primitive type (int, long,double…) and collection types (map, list..) have not been set, UnsatisfiedDependencyException will be thrown.
objects – If any properties of object type have not been set, UnsatisfiedDependencyException will be thrown.
all – If any properties of any type have not been set, an UnsatisfiedDependencyException
will be thrown.
Tips:
在Spring中名为xxxAware的bean,一般都会调用setXxx()以实现目标的注入。
loadBeanDefinitions(beanFactory)
1 |
|
这里首先为BeanFactory新建了一个BeanDefinationReader(此时Reader持有BeanFactory);
接着填充Reader的一些属性;
最后调用loadBeanDefinitions(beanDefinitionReader)
,构建document、解析并注册BeanDefination1
2
3
4
5
6
7
8//XmlBeanDefinationReader.class
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
//...
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
//...
}
doLoadDocument(inputSource, resource)
根据输入流封装的inputSource以及resource去构建document,这里具体的代码就不去分析了,重点看这个。
Ps:以后有document解析的需求可以参考这里的代码
registerBeanDefinitions(doc, resource)
1 | public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { |
我们层层递进追到了上面这个方法中,我们在分析自动化aop的时候发现就是在这里的delegate.parseCustomElement(ele)
对<aop:aspectj-autoproxy/>
进行解析的。我们先来看默认的情况1
2
3
4
5
6
7
8
9
10
11
12
13
14
15private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
我们直接进入到processBeanDefinition(ele, delegate)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
这里主要分为了两个步骤,一个是BeanDefination的真正解析;一个是BeanDefination的注册。具体的解析步骤这里就不进行分析了。而BeanDefination的注册其实就是将beanName和beanDefination的映射关系保存在map中,this.beanDefinitionMap.put(beanName, beanDefinition);
Ps:显然beanDefinitionMap是被BeanFactory持有的
getBeanFactory()
这个步骤就是简单地将上一步骤中实例化以及初始化过得beanFactory返回。return this.beanFactory;
prepareBeanFactory
1 | protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { |
这里我们看到添加了一个BeanPostProcessor-ApplicationContextAwareProcessor;并且同样忽略了很多以Aware结尾的接口。关于ApplicationContextAwareProcessor我们会在Bean的实例化阶段再去分析。
invokeBeanFactoryPostProcessors(beanFactory)
1 | public static void invokeBeanFactoryPostProcessors( |
对每个普通的BeanFactoryProcessor都调用了postProcessBeanFactory方法。
至此容器的启动阶段结束。
小结:
1)如果当前存在BeanFactory那么销毁所有Beans并关闭BeanFactory
2)实例化BeanFactory,与此同时指定了一些不会被依赖检查的类型的bean
3)将xml解析为Document(xml->inputsource&resource)
4)利用BeanDefinitionParserDelegate对Document进行解析,解析的结果是BeanDefination(对特殊注解比如aspectJ aop的解析也在这里)
5)对解析出来的BeanDefination进行注册(其实就是放到了一个ConcurrentHashMap中,该map被BeanFactory持有)
6)回调BeanFactoryPostProcessor类型的bean的postProcessBeanFactory方法
以上です