Spring 源码阅读笔记(一)
背景
作为Java开发程序员,不管是在工作中、和同事聊天中、面试中,都必不开的会聊一个框架:Spring,在面试中几乎都会问那么多的问题,例如Bean的生命周期,三级缓存,循环依赖这些问题。
我们都背过很多面试题,面试题背完就忘,真正记住还是得去看一遍源码,在写这篇文章之前,我保证我没有看过Spring的源码。这一系列算是领着各位看官从零开始。
我当前源码版本为 5.3.16 当前为Github上的最新的版本
下面就正式开始了。
Spring 生成Context流程概述
阅读源码前,需要找到源码的入口。
先做一个简单的spring的启动程序。
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
System.out.println(context);
}
}
复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean class="org.springframework.study.bean.FirstBean"></bean>
<bean class="org.springframework.study.bean.SecondBean"></bean>
</beans>
复制代码
先看一下Spring启动时的全部的方法调用的流程。
看到上方的调用图,可以看出Refresh方法是关键,那么里面这么多方法,具体是做什么的呢,我也不知道。
在正式读代码之前,我们需要对我们此次设计的类进行一个认识。整理出了这样的一张图。
我们先不管这些类当中有什么方法和属性,当我们读到的时候,可以再来追加。那么我们进入第一个Spring启动时的第一个方法。
Super构造器
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent); // 初次进入时parent是null
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
复制代码
看到这个方法,直接进入super的构造一探究竟
public AbstractXmlApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
public AbstractRefreshableConfigApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
public AbstractRefreshableApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
setParent(parent);
}
复制代码
当追到AbstractApplicationContext时,发生了变化,调用了自己的无参构造。
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
// 返回 ResourcePatternResolver 以用于解析位置模式进入资源实例。 默认是一个
// {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver},
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
this.resourceLoader = resourceLoader;
}
复制代码
通过这里查看呢,这个方法,弄了一个资源加载器,那么为什么ApplicationContext可以传到ResourceLoader中,通过上面的类图可以看出,AbstractContext时继承了一个DefaultResourceLoader。至于这个ResourceLoader是做什么的,我粗略的看了一下,根据他的getResources方法中,看出是获取资源文件的加载器。回到上面的代码接着往下看。
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
setParent(parent);
}
@Override
public void setParent(@Nullable ApplicationContext parent) {
this.parent = parent;
if (parent != null) {
// 获取了环境信息
Environment parentEnvironment = parent.getEnvironment();
if (parentEnvironment instanceof ConfigurableEnvironment) {
// 如果是ConfigurableEnvironment 触发合并操作
getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
}
}
}
复制代码
由于项目刚刚启动,这里传的null,到这里发现,看了个寂寞?,可以跳过,我们大致读一下,如果说优质的情况下他会做一些什么操作。获取一下环境信息,如果是配置环境类的话,会触发一个合并操作。这里简单记一下脑子里大概有个印象。
接下来回到ClassPathXmlApplication的构造方法中。
setConfigLocations
public void setConfigLocations(@Nullable 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;
}
}
复制代码
这一段代码比较简单,就是做了个configLocations的赋值,可能后面的操作中会使用到。
告一段落
由于Refresh方法里面涉及的点比较多,这篇文章先写到这。
到目前为止,类图的样子是这样的