Java生态圈里的Spring框架众所周知,研究它源码的大神也多如牛毛,小弟不才,也对它的源码很感兴趣,
不光是要一窥框架设计之精妙,也要在平时的开发过程中能借鉴其中的设计思想。今天这篇文章我们就来
聊聊它加载bean的过程中有什么值得学习的地方。
首先我们可以写个简单的demo,来一步步调试和窥探:
1 | public static void main( String[] args ) |
spring-config.xml配置文件中我们就定义了一个简单的bean元素,那么我们先自己简单地想下Spring内部要解决哪些
问题它才能获取到这个实例,我们要带着问题去阅读源码才会有所收获。我首先想到的就是如下这些:
- 它是如何定位xml配置文件并读取的
- 如何解析xml配置文件中的bean元素和属性
- 解析完了以后这些配置放到哪里呢
- getBean的时候又是去哪获取的呢
定位xml配置文件
我们在把一个文件名放入ClassPathXmlApplicationContext构造函数的时候,它内部就开始了定位逻辑,
因为它支持多个配置文件路径,所以我们传入一个路径的时候它会new一个String数组,调用其他构造函数,
1 | public ClassPathXmlApplicationContext(String configLocation) throws BeansException { |
最后就调用了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19我们可以在ClassPathXmlApplicationContext类中自己实现,这就是一个很好的设计之处。
AbstractRefreshableApplicationContext的setConfigLocations方法如下:
```Java
/**
* Set the config locations for this application context.
* <p>If not set, the implementation may use a default as appropriate.
*/
public void setConfigLocations(@Nullable String... locations) {
if (locations != 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;
}
}
它的注释说的很明白,如果我们不调用setConfigLocations,那它就会根据情况用一个默认路径了,后果不看设想。。
显然我们要重点关注的是1
2
3
4
5
6
7
8
9
10
11
12
```Java
/**
* Resolve the given path, replacing placeholders with corresponding
* environment property values if necessary. Applied to config locations.
* @param path the original file path
* @return the resolved file path
* @see org.springframework.core.env.Environment#resolveRequiredPlaceholders(String)
*/
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
我们又要看它的注释了,很清楚,根据给定的path路径字符串,如果必要的话会用环境变量替换它里面占位符,然后赋给配置路径,
比如我们把spring-config.xml
换成${java.version}spring-config.xml传入,那么它会把${java.version}用我们环境变量替换掉
具体要看org.springframework.core.env.Environment#resolveRequiredPlaceholders(String)
里面的代码了。
我们下回再解!