有兴趣的小伙伴可以看b站相关视频:https://www.bilibili.com/video/BV1bY41167rg/?spm_id_from=333.999.0.0
前言
本文主要从BeanFactory接口定义的行为和注释作为切入点,以ClassPathXmlApplicationContext入口梳理Spring加载配置和创建对象的总体方法流程,构建Spring的主干流程。先有主干再学习枝干,像一棵树一样不断生长。
一、BeanFactory接口
大家都知道BeanFactory接口是Spring的核心,先来看该接口提供了什么方法。(温馨提示:看源码先看接口)
从上图可以看出,主要是通过名称获取对象,并且接口名称后缀是Factory,可以大胆猜想就是通过getBean方法来创建对象。接着看接口的注释,看能不能挖掘一些东西出来。
根据接口的第一段注释和第三段注释可知,持有一些BeanDefinition的信息,每个Bean的定义信息拥有唯一名字;从配置文件(如XML)中读取配置进行解析,再结合之前对spring的了解,画出一个简单的轮廓。主要是两个步骤,读取配置文件,解析配置文件为BeanDefinition,如下图。
其他功能都是在该轮廓的基础上扩充,目的都是为创建对象服务。如果掌握了这个流程,那么可以自行看源码,然后将功能添加到上图中。
二、Spring主干
class Demo{
public static void main(String[] args){
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
DemoService service = (DemoService)context.getBean("demoService");
// service CRUD 业务逻辑
}
}
以上是使用Spring的基操,创建ClassPathXmlApplicationContext,指定xml配置路径。
先来看一下ApplicationContext的命名,很有意思,看名字,基本上就可以猜出功能,将ClasspathXmlApplicationContext分割为Classpath、Xml、ApplicationContext,Classpath说明是在classpath文件目录下,xml表示配置的类型;再看一下父类FileSystemXmlApplicationContext,说明是文件系统下的xml配置,后面大家可以去看看其他的,是不是也有这个规律。
接下来,进入我们的重头戏refresh方法【refresh方法在AbstractApplicationContext类】。温馨提示:看源码主要看主干,先了解主干,再到细节
首先,我们先看下注释,这里提到加载配置信息,这个方法实现了接口,我们看下接口上对这个方法的解释,一般接口上的方法都有注释,用来说明它的功能。refresh方法是用来加载配置的,我们来看下它如何加载的。
我们先总体浏览一下refresh这个方法
public final void refresh() throws ApplicationContextException, BeansException {
if (this.contextOptions != null && !this.contextOptions.isReloadable())
throw new ApplicationContextException("Forbidden to reload config");
refreshBeanFactory();
// invoke configurers that can override values in the bean definitions
invokeContextConfigurers();
// load options bean for this context
loadOptions();
// initialize message source for this context
initMessageSource();
// initialize other special beans in specific context subclasses
onRefresh();
// check for listener beans and register them
refreshListeners();
// instantiate singletons this late to allow them to access the message source
preInstantiateSingletons();
// last step: publish respective event
publishEvent(new ContextRefreshedEvent(this));
}
判断contextOptions是否为null,是否可以加载,先记住有这么一个东西,往后看你就会知道它是做什么的?
获取当前时间,这个不重要,我们先删掉
接下来是refreshBeanFactory,通过名字猜测刷新bean工厂,那我们点进去看一下注释,子类需要实现实现这个方法,完成配置的加载,而且这个方法必须在其他初始化动作之前完成。
下面打印日志的,我们直接跳过
再往后走是invokeContextConfigurers,先看下注释,(停顿一下),回调修改Bean定义信息的配置,具体是什么,我们后面再详细讲解,先有个印象就可以
下一个是loadOptions,这个看不出是做什么用的,点进去看一下,这个方法做了什么。发现创建了一个类,这个类在哪里用呢,我们搜一下,发现这个类在refresh方法一进来的时候的判断条件,通过注释也可以很清楚地了解到,就是在运行的过程中,能不能重新加载配置信息,也就是能不能重复调用refresh方法。
继续往下走是initMessageSource,通过注释看不出是做什么用的,不过没关系,要记住我们现在是了解refresh的总体过程,这个看不懂的先跳过,后面回过头来一个方法一个方法讲,
再往下就是onRefresh方法,还是老规矩,先看注释,子类实现的初始化一些特殊的bean,点进去看下,发现是空方法,说明在这里没有实现,相当于是一个钩子函数,给自己去做一些特殊的操作。高版本的WebServer就是在这里加载,有兴趣的小伙伴可以先去看一看。
往后再走一步到refreshListeners,通过方法名字 + 注释可以了解到,就是检查和加载监听器。
再往后走到preInstantiateSingletons,实例化单例,这个方法是重点,后面会详细讲。
接下来到最后一步,发送ContextRefreshedEvent事件。
总结:
第一步:刷新BeanFactory,加载配置,当前是XML
第二步:提供修改Bean定义信息的回调函数
第三步:允不允许重复加载配置,也就是重复调用refresh方法
第四步:初始化消息源,
第五步:给子类去实现,初始化特殊的bean
第六步:检查和加载监听器
第七步:初始化单例
第八步:发送上下文完成刷新的事件