spring源码学习笔记-初始化(一)-概览
spring源码学习笔记-初始化(一)-概览
转自http://www.sandzhang.com/blog/2011/03/30/spring-study-notes-initialization-1/
版本:spring-framework-3.0.5.RELEASE
很多人看开源框架源代码的时候都不知道从哪里入手,我这里推荐一个最简单的办法,写一个最简单的应用的例子,然后根据这个应用一点一点查看在源码中的运行步骤,这样就能对框架有一个基本的了解,有了这个基本的认识,再去针对不同模块扩展开来仔细研究。
本系列主要是学习spring的源码,首先是最简单的使用例子:
- ApplicationContext ctx = new ClassPathXmlApplicationContext(
- "spring-config.xml");
- FooService foo = (FooService) ctx.getBean("FooService");
我们看第一行初始化了一个ClassPathXmlApplicationContext对象,
注:也可以用FileSystemXmlApplicationContext来加载,两者的区别只是查找配置文件的起始路径不同,一个以classpath为当前路径,一个是直接用文件系统的当前路径,内部没有太大区别。
注2:web工程大家都知道我们配置了ContextLoaderListener,这里暂时不做介绍,后面会有专门的分析,起始主题流程都差不多,详情请参见:从源码看Spring在web工程中的初始化。
请看下面的构造方法,参数是spring配置文件在classpath中的全路径名:
- public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
- this(new String[] {configLocation}, true, null);
- }
可以看到里面调用了另外一个构造方法(如下),其中传递了两个默认参数refresh=true立即刷新,parent=null继承为空,并且把配置文件封装为了一个String数组,这里主要是因为spring是支持多个配置文件的
- public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
- throws BeansException {
- super(parent);
- setConfigLocations(configLocations);
- if (refresh) {
- refresh();
- }
- }
我们看这个构造方法的代码:
- 首先调用了父类的构造函数
- 调用setConfigLocations设置配置文件位置信息(代码如下)
- 判断如果refresh为true则调用refresh()方法,refresh方法在AbstractApplicationContext中
setConfigLocations代码:
很简单,主要是检验是否传入参数为空然后赋值给configLocations属性,不过其中有两个小特点可以注意一下:
- 并没有直接把传入参数locations直接赋值给属性,而是new了一个String数组,然后循环赋值,这里主要是出于安全考虑避免外面传入的字符串数组变化影响spring内部
- 可以看到赋值前有调用了一下resolvePath方法,这个方法实现了一些系统变量的替换,例如路径里可以使用${}加载系统变量值
- public void setConfigLocations(String[] locations) {
- if (locations != null) {
- Assert.noNullElements(locations,
- "Config locations must not be null");
- this.configLocations = new String[locations.length];
- for (int i = 0; i < locations.length; i++) {
- this.configLocations[i] = resolvePath(locations[i]).trim();
- }
- } else {
- this.configLocations = null;
- }
- }
下面看refresh刷新方法,这个刷新方法主要是从配置文件加载bean配置的过程,代码如下:
- public void refresh() throws BeansException, IllegalStateException {
- // 整个刷新过程是同步的
- synchronized (this.startupShutdownMonitor) {
- // 刷新前的准备工作
- prepareRefresh();
- // 关闭释放旧的beanFactory创建新的beanFactory,读取配置文件等
- ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
- // 对beanFactory进行一些基本的初始化
- prepareBeanFactory(beanFactory);
- try {
- // 下面两行主要用户扩展,处理所有已注册的BeanFactoryPostProcessor,实现在已经加载配置但未初始化bean时对配置进行修改
- postProcessBeanFactory(beanFactory);
- invokeBeanFactoryPostProcessors(beanFactory);
- // 处理所有已注册的BeanPostProcessor,主要用于扩展,实现bean初始化前后的一些定制操作
- registerBeanPostProcessors(beanFactory);
- // 初始化消息源bean
- initMessageSource();
- // 初始化事件监听器集,也有人叫事件监听器的注册表,所有的事件监听器都在这个bean里进行管理
- initApplicationEventMulticaster();
- // 主要用于扩展,实现一些特殊bean的初始化,时间点是类似消息源事件监听器集等特殊bean初始化后,普通的bean初始化前
- onRefresh();
- // 注册监听器
- registerListeners();
- // 初始化其余的非延迟加载的单例bean
- finishBeanFactoryInitialization(beanFactory);
- // 刷新完成调用LifecycleProcessor的onRefresh方法,并且发布ContextRefreshedEvent事件
- finishRefresh();
- } catch (BeansException ex) {
- // 销毁已经创建的单例bean
- destroyBeans();
- // 重新设置active标记
- cancelRefresh(ex);
- throw ex;
- }
- }
- }
上面是一个spring初始化的基本流程框架,后面几篇将陆续对其中每一个方法进行详细的分析