Spring 源码阅读笔记(一)

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启动时的全部的方法调用的流程。

2022-03-17-17-11-02-image.png
看到上方的调用图,可以看出Refresh方法是关键,那么里面这么多方法,具体是做什么的呢,我也不知道。

在正式读代码之前,我们需要对我们此次设计的类进行一个认识。整理出了这样的一张图。

2022-03-18-13-11-38-image.png

我们先不管这些类当中有什么方法和属性,当我们读到的时候,可以再来追加。那么我们进入第一个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方法里面涉及的点比较多,这篇文章先写到这。

到目前为止,类图的样子是这样的

image.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享