spring4.1.8 初始化源码学习三部曲之二:setConfigLocations 方法
欢迎访问我的 GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本章是学习 spring4.1.8 初始化源码的第二篇,前一章《spring4.1.8初始化源码学习三部曲之一:AbstractApplicationContext构造方法》对 AbstractApplicationContext 的初始化做了分析,本章我们聚焦 ClassPathXmlApplicationContext.setConfigLocations 方法;
整体概括
本章会涉及到多个类的细节,所以先从整体上概括 AbstractRefreshableConfigApplicationContext.setConfigLocations 方法的主要功能,后面的细节都是基于这些总结展开的:
setConfigLocations 主要工作有两个:创建环境对象 ConfigurableEnvironment 、处理 ClassPathXmlApplicationContext 传入的字符串中的占位符;
环境对象 ConfigurableEnvironment 中包含了当前 JVM 的 profile 配置信息、环境变量、 Java 进程变量;
处理占位符的关键是 ConfigurableEnvironment、PropertyResolver、PropertyPlaceholderHelper 之间的配合:
用思维导图来辅助:
对占位符的处理实战,请参考文章《windows下修改、编译、构建spring-framework4.1.8.RELEASE源码》,这里面用 demo 展示了占位符的替换;
展开详情
接下来去阅读 setConfigLocations 方法内部的细节代码:
跟踪该方法,找到是在类 AbstractRefreshableConfigApplicationContext 中实现的:
从上述代码可以发现,本章我们要重点学习的是 resolvePath(locations[i]),结合上一章 demo 中的入参,此处应该是方法 resolvePath("classpath:applicationContext.xml");
跟踪到 AbstractRefreshableConfigApplicationContext 类,这个方法的目的是替换掉 path 字符串中的占位符 ${XXX}这样的内容:
先看 getEnvironment()方法:
关于 ConfigurableEnvironment 接口,我们先来看看他的声明方法以及继承关系:
从上图可见,ConfigurableEnvironment 接口有两个重要部分组成:Profile 和 Property;Profile 是对测试、生产等不同环境下的 bean 配置,这里我们没有特别设置,所以用到的 profile 是 AbstractEnvironment 的 defaultProfiles;接下来关于 Property 资源是如何产生的;
顺着调用一路看过去,发现最终会调用 AbstractEnvironment 类的构造方法:
上面的 customizePropertySources 是在 StandardEnvironment 类中实现的,如下:
上述代码中,可以将 propertySources 对象理解成一个容器(其对象内部核心成员 propertySourceList 是个 CopyOnWriteArrayList 实例);
首先向 propertySources 添加一组属性,来自 Java 进程变量(getSystemProperties()内是 System.getProperties()方法);
接着向 propertySources 再添加一组属性,来自系统环境变量(getSystemEnvironment()内是 System.getenv()方法);
getSystemProperties 和 getSystemEnvironment 方法中有个相同的细节需要注意,在获取进程变量或者系统环境变量的时候,都有可能因为安全限制抛出异常,这时候就返回一个 ReadOnlySystemAttributesMap 的实现类,外部调用 get 方法的时候,再去尝试获取进程变量或者系统环境变量对应的值,取不到则返回 null,代码如下:
StandardEnvironment 对象创建成功后,接着看它的 resolveRequiredPlaceholders 方法:
上面的 propertyResolver 从何而来?以下代码可见,这个 final 型的成员变量在声明时就创建了,前面准备好的 propertySources 集合通过构造方法传给了它,所有它已经获得了所有系统环境变量和进程环境变量:
跟踪 PropertySourcesPropertyResolver.resolveRequiredPlaceholders,发现真正处理占位符的逻辑是在 PropertyPlaceholderHelper.doResolvePlaceholders 方法:
getPropertyAsRawString 的具体实现在 PropertySourcesPropertyResolver 类中:
继续跟踪 helper.replacePlaceholders(),到了 PropertyPlaceholderHelper.parseStringValue 方法,这里面逐一找出每个占位符去做替换:
parseStringValue 方法中,找到了占位符后,会调用入参 placeholderResolver 的 resolvePlaceholder(placeholder)方法,也就是步骤 9 中匿名类的 getPropertyAsRawString 方法(实际上就是 PropertySourcesPropertyResolver.getPropertyAsRawString 方法),最终会在 PropertySourcesPropertyResolver.getProperty 方法中找出所有的属性来匹配占位符:
至此,Spring 环境的初始化准备工作已经完成,下一章一起去看 refresh()方法,那里聚集了 Spring 初始化的核心操作:《spring4.1.8 初始化源码学习三部曲之三:AbstractApplicationContext.refresh 方法》
欢迎关注 InfoQ:程序员欣宸
版权声明: 本文为 InfoQ 作者【程序员欣宸】的原创文章。
原文链接:【http://xie.infoq.cn/article/96c21ac15b3e9e1277e8d57d5】。文章转载请联系作者。
评论