spring4.1.8 初始化源码学习三部曲之三:AbstractApplicationContext.refresh 方法
欢迎访问我的 GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
我们先回顾 ClassPathXmlApplicationContext 类的初始化过程如下代码:
三部曲的前两篇学习了 super(parent)和 setConfigLocations(configLocations):
refresh 方法简介
本章来学习 refresh 方法,具体的源码在 AbstractApplicationContext 类中,该方法的简介请看下面源码中的注释:
接下来逐个分析吧:
prepareRefresh 方法
prepareRefresh 方法的源码如下:
上述代码中,注意以下两处:
initPropertySources 是个空方法,是留给子类实现的,以 AnnotationConfigWebApplicationContext 类为例,就 overwrite 了 initPropertySources 方法:
跟踪上面的 initPropertySources 方法,最终找到了 WebApplicationContextUtils.initServletPropertySources:
上面的代码所做的事情,就是给 context 增加环境变量数据(数据来自 servlet 相关的配置信息),这样 spring 环境就能从 context 中随时 key 取得对应的变量了;
getEnvironment().validateRequiredProperties()的作用是用来校验 context 中是否存在**"某些"**变量,何谓"某些"?来看 validateRequiredProperties 方法,追踪到多层调用,最终在 AbstractPropertyResolver 类的 validateRequiredProperties 方法中实现:
上述代码显示,如果集合 requiredProperties 中的 name 在 context 中找不到对应的变量,就会抛出异常;
那么问题来了,requiredProperties 集合是何时设置的呢?spring-framework 中并没有调用,但是官方的单元测试源码给我们了启发,如下图:
如上图红框,如果业务需要确保某些变量在 spring 环境中必须存在,就可以调用 setRequiredProperties 方法将变量的 name 传递进去,这样 validateRequiredProperties 方法就会做检查了,我们可以基于现有的各种 ApplicationContext 实现自己定制一个 Context 类,确保在 validateRequiredProperties 方法调用之前调用 setRequiredProperties 方法将变量的 name 传递进去(例如重写 initPropertySources),就能让 spring 帮我们完成检查了;
obtainFreshBeanFactory()
接下来看**ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();**得到临时变量 beanFactory,先看看 ConfigurableListableBeanFactory 和 BeanFactory 的关系:
再看看 obtainFreshBeanFactory 方法:
上述代码中有的 refreshBeanFactory 需要细看;
refreshBeanFactory 方法
refreshBeanFactory 方法,在 AbstractApplicationContext 类中是抽象方法,具体实现在子类中,以其子类 AbstractRefreshableApplicationContext 为例,我们来看看 refreshBeanFactory 方法的实现:
createBeanFactory 方法实际上返回的是一个 DefaultListableBeanFactory 实例:
接下来的 customizeBeanFactory 方法是留给子类 OverWrite 的,该方法的说明和源码如下,说明中推荐通过 OverWrite 的方式对现有 beanFactory 做特别的设置:
loadBeanDefinitions 在 AbstractRefreshableApplicationContext 类中是个抽象方法,留给子类实现,作用是把所有 bean 的定义后保存在 context 中,以 AbstractXmlApplicationContext 为例,看看 loadBeanDefinitions 方法做了什么:
以上代码可见,加载 bean 的定义是通过 XmlBeanDefinitionReader 来完成的,重点关注 loadBeanDefinitions 方法:
上述代码中的 getConfigResources()和 getConfigLocations(),究竟哪个会返回值有效数据呢?这就要去看 ClassPathXmlApplicationContext 的构造方法了:
因此,到底是 configLocations 还是 configResources ,和我们使用哪个构造方法来实例化 applicationContext 对象有关;
如果我们实例化 applicationContext 对象的方式是 new ClassPathXmlApplicationContext("applicationContext.xml"),那么 setConfigLocations 方法就会被调用,因此 loadBeanDefinitions 方法内部,实际执行的代码如下:
现在可以来看 AbstractBeanDefinitionReader 类的 loadBeanDefinitions(String... locations)方法了:
展开上面 for 循环中调用的方法:
以上方法中,首先要记得 resourceLoader 是 ClassPathXmlApplicationContext(beanDefinitionReader.setResourceLoader(this)这行代码),所有 resourceLoader.getResource(location)这行代码最终会调用 PathMatchingResourcePatternResolver 类的 getResources(String locationPattern)方法得到 bean 有关的 Resource 对象;得到 Resource 对象后,接着会调用 loadBeanDefinitions(Resource... resources)方法来加载 bean 的定义了,最终是调用 XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource)方法:
上述代码可见,重要的是通过 Resource 对象得到 InputStream,再调用 doLoadBeanDefinitions 方法:
上面是加载 bean 定义的关键代码:先制作 Document 对象,再调用 registerBeanDefinitions 方法,最终会将每个 bean 的定义放入 DefaultListableBeanFactory 的 beanDefinitionMap 中,详细的堆栈如下图:
完成了 bean 定义的注册,可以回到 AbstractRefreshableApplicationContext.refreshBeanFactory 方法了,看看 loadBeanDefinitions(beanFactory)之后的代码:
至此,refreshBeanFactory 方法分析完毕,该方法所做的事情:把 xml 文件中的 bean 定义被解析后,存放在 DefaultListableBeanFactory 的 beanDefinitionMap 中;
现在回到主线的 AbstractApplicationContext.refresh()方法内,obtainFreshBeanFactory()我们已经分析完毕,所有 bean 定义都被存放在 beanFactory 这个临时变量对应的实例中;
prepareBeanFactory
接下来是 prepareBeanFactory(beanFactory),看一下此方法的源码:
上述代码中有以下几点需要注意:
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())),此方法要配合 AbstractBeanFactory.registerCustomEditors 方法一起看更好理解:addPropertyEditorRegistrar 方法向 propertyEditorRegistrars 属性中放入了一个 registrar,之后调用 registerCustomEditors 方法的时候,会用到 propertyEditorRegistrars 中的 registrar,调用这些 registrar 的 registerCustomEditors 方法,完成自定义的转换器的设置;
beanFactory.addBeanPostProcessor 方法用来注入后置处理器,在 bean 实例被创建后,初始化方法被执行的前后,后置处理器的 postProcessBeforeInitialization、postProcessAfterInitialization 这两个方法会分别被调用;
beanFactory.ignoreDependencyInterface 设置了依赖注入时要忽略的接口,例如 bean 有个属性类型是 ResourceLoaderAware,那么该属性不会被注入 ResourceLoaderAware 类型的实例;
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory)是特殊设置,如果一个 bean 有个属性的类型是 BeanFactory,那么该属性会被设置为 beanFactory 这个实例;
总的来说 prepareBeanFactory 方法就是为 beanFactory 做一些设置工作,传入一些后面会用到的参数和工具类,再在 spring 容器中创建一些 bean;
postProcessBeanFactory
postProcessBeanFactory 方法是留给子类扩展的,可以在 bean 实例初始化之前注册后置处理器(类似 prepareBeanFactory 方法中的 beanFactory.addBeanPostProcessor),以子类 AbstractRefreshableWebApplicationContext 为例,其 postProcessBeanFactory 方法如下:
可见除了 WebApplicationContextUtils 类的工作之外,其余的都是和 prepareBeanFactory 方法中类似的处理;
invokeBeanFactoryPostProcessors
invokeBeanFactoryPostProcessors 方法用来执行 BeanFactory 实例的后置处理器 BeanFactoryPostProcessor 的 postProcessBeanFactory 方法,这个后置处理器除了原生的,我们也可以自己扩展,用来对 Bean 的定义做一些修改,由于此时 bean 还没有实例化,所以不要在自己扩展的 BeanFactoryPostProcessor 中调用那些会触发 bean 实例化的方法(例如 BeanFactory 的 getBeanNamesForType 方法),源码的文档中有相关说明,如下图红框所示,不要触发 bean 的实例化,如果要处理 bean 实例请在 BeanPostProcessor 中进行:
registerBeanPostProcessors
registerBeanPostProcessors 方法的代码略多,就不在此贴出来了,简单的说,就是找出所有的 bean 的后置处理器(注意,是 bean 的后置处理器,不是 beanFactory 的后置处理器,bean 后置处理器处理的是 bean 实例,beanfactory 后置处理器处理的是 bean 的定义),然后将这些 bean 的后置处理器分为三类:
实现了顺序接口 Ordered.class 的,先放入 orderedPostProcessors 集合,排序后顺序加入 beanFactory 的 bean 后处理集合中;
既没有实现 Ordered.class,也没有实现 PriorityOrdered.class 的后置处理器,也加入到 beanFactory 的 bean 后处理集合中;
最后是实现了优先级接口 PriorityOrdered.class 的,排序后顺序加入 beanFactory 的 bean 后处理集合中;
registerBeanPostProcessors 方法执行完毕后,beanFactory 中已经保存了有序的 bean 后置处理器,在 bean 实例化之后,会依次使用这些后置处理器对 bean 实例来做对应的处理;
initMessageSource
initMessageSource 方法用来准备国际化资源相关的,将实现了 MessageSource 接口的 bean 存放在 ApplicationContext 的成员变量中,先看是否有配置,如果有就实例化,否则就创建一个 DelegatingMessageSource 实例的 bean;
initApplicationEventMulticaster
spring 中有事件、事件广播器、事件监听器等组成事件体系,在 initApplicationEventMulticaster 方法中对事件广播器做初始化,如果找不到此 bean 的配置,就创建一个 SimpleApplicationEventMulticaster 实例作为事件广播器的 bean,并且保存- 为 applicationContext 的成员变量 applicationEventMulticaster;
onRefresh
onRefresh 是个空方法,留给子类自己实现的,在实例化 bean 之前做一些 ApplicationContext 相关的操作,以子类 AbstractRefreshableWebApplicationContext 为例,看看它的 onRefresh 方法:
可见是做了主题相关的初始化,并保存在 ApplicationContext 的成员变量中;
registerListeners
方法名为 registerListeners,看名字像是将监听器注册在事件广播器中,但实际情况并非如此,只有一些特殊的监听器被注册了,那些在 bean 配置文件中实现了 ApplicationListener 接口的类还没有实例化,所以此处只是将其 name 保存在广播器中,将这些监听器注册在广播器的操作是在 bean 的后置处理器中完成的,那时候 bean 已经实例化完成了,我们看代码:
finishBeanFactoryInitialization
finishBeanFactoryInitialization 方法做了两件事:
beanFactory 对象的初始化;
我们在 bean 配置文件中配置的那些单例的 bean,都是在 finishBeanFactoryInitialization 方法中实例化的;
看代码:
上述代码中,beanFactory.preInstantiateSingletons()需要展开仔细看:
上述代码中,要重点关注 getBean(beanName),这里面会实例化 bean,由于内容太多不适合在本章细说,这里先将实例化 bean 的调用路径整理出来:
以上调用路径可以看出,bean 对象的创建是 BeanUtils.instantiateClass 方法通过反射来创建的;
再来看看 bean 的成员变量是什么时候被注入值的,如下图,AbstractAutowireCapableBeanFactory.doCreateBean 方法中,先调用 createBeanInstance 创建 bean 的对象(绿框所示),再调用 populateBean 方法给成员变量注入内容(红框所示):
将注入值的调用堆栈整理如下,可见是也是通过反射完成注入的:
看过了成员变量注入的逻辑后,还有个重要的逻辑也请关注,就是 bean 的初始化(bean 的配置文件中的 init-method 属性),AbstractAutowireCapableBeanFactory.doCreateBean 方法中,在调用 populateBean 方法给成员变量注入值之后,马上调用 initializeBean 方法进行初始化操作,调用堆栈整理如下:
可见依旧是通过反射来执行初始化方法;
finishRefresh
最后一个方法是 finishRefresh,这是在 bean 的实例化、初始化等完成后的一些操作,例如生命周期变更的回调,发送 applicationContext 刷新完成的广播等,展开看看:
至此,整个初始化流程咱们已经过了一遍了,但是篇幅有限,很多细节都没有展开,另外很多子类也有自己独特的扩展,这些都需要花时间去细看,希望本文能帮您整理思路,从总体上了解初始化的各个关键步骤,以免过早陷入细节;
欢迎关注 InfoQ:程序员欣宸
版权声明: 本文为 InfoQ 作者【程序员欣宸】的原创文章。
原文链接:【http://xie.infoq.cn/article/42c18f41232cebf1ea87ca104】。文章转载请联系作者。
评论