前言
在前面的博客中我们已经详细分析过Spring的启动源码,包括beanDefinition的加载、bean的初始化以及各种后处理器的回调,这个过程还是比较复杂的。
作为Spring全家桶中的一员,SpringCloud也是对Spring做的封装,底层逻辑其实一致,我们先来看看SpringCloud的入口方法:
我们直接去看看SpringApplication
的run 方法,根据调用形式可以看到这里调用的是SpringApplication
的静态run方法:
这部分的代码逻辑是:先创建一个SpringApplication
对象,然后调用这个对象的的run 方法。所以核心逻辑有两个部分:SpringApplication
的构造方法和它的非静态run方法。
1. SpringApplication的构造方法
我们从源码可以看到,SpringApplication
类没有继承任何类,也没有实现任何接口(我们在讲解Spring时,ClassPathXmlApplicationContext
类图就非常复杂)。
我们接着来看看SpringApplication
的构造方法,该构造方法的核心是调用getSpringFactoriesInstances方法,完成三个类型bean的实例化,注意这一步我们并未创建beanFactory:
这里完成了三个类型bean的实例化:BootstrapRegistryInitializer
、ApplicationContextInitializer
和ApplicationListener
。
我们接着看看getSpringFactoriesInstances方法,该方法核心逻辑有两个:
1.通过SpringFactoriesLoader
的loadFactoryNames获得指定类型的类的全路径名;
2.通过createSpringFactoriesInstances创建实例;
getSpringFactoriesInstances 首先会通过getClassLoader方法去获得类加载器:
类加载的一个作用是根据字节码文件创建Class
对象,并保存在堆内存中。因此,创建一个对象是绕不开类加载器的,只不过大部分情况下是jvm帮我们完成了这些工作。
在获得类加载器后,会调用SpringFactoriesLoader
的静态方法loadFactoryNames ,该方法的作用是找到我们指定类的全路径名,有了类的全路径名才能加载Class
对象并创建该类的对象,我们接着来看看loadFactoryNames方法:
loadFactoryNames 的底层逻辑很好理解:通过静态方法loadSpringFactories找到所有第三方依赖包中的文件spring.factories的类的全路径名,然后进一步筛选出指定类型的全路径名并返回。
loadSpringFactories方法的底层逻辑是这样的:
ABAP
1.根据类加载器查缓存(SpringFactoriesLoader的cache属性),结果不为空就直接返回;
2.结果为空,就去找有"META-INF/spring.factories"文件的第三方依赖包的路径;
3.遍历2中得到的路径,根据路径加载spring.factories文件中的全路径名;
ABAP
4.遍历全路径名和实现类将其保存在result;
5.对result的value去重,并将value更改为不可修改的列表;
6.将入参classLoader作为key,result作为value,存入SpringFactoriesLoader的cache属性中。
当loadFactoryNames 方法执行完成后,我们再回到getSpringFactoriesInstances方法:
接下来就是通过createSpringFactoriesInstances 方法创建实例对象了,我们可以进到createSpringFactoriesInstances方法看看,其核心思想是通过反射创建对象:
以上就是getSpringFactoriesInstances方法的主要逻辑,总的来说就干了一件事,实例化指定类型的对象。让我们再回到SpringApplication的构造方法:
2.SpringApplication的非静态run方法
run 方法会对SpringApplication
继续做一些配置。
其中重要的是会创建一个上下文容器(类型为AnnotationConfigServletWebServerApplicationContext
),就是我们Spring中讲过的上下文容器(类型为ClassPathXmlApplicationContext
),二者类型有区别,但是也负责实例化bean:
之前讲解Spring容器的时候详细讲过refresh 方法干了什么,虽然上下文容器的类型不同,但是处理的逻辑是大致相同,我们这里就简单介绍一下,不过多深入,首先我们来看看refreshContext方法:
最后还是调用到了AbstractApplicationContext
的refresh 方法,是不是很熟悉,没错就是ClassPathXmlApplicationContext
调用的refresh 方法,但是Spring Cloud在Spring基础上做了封装修改,refresh方法也有小改动,但是大体并没有变:
到了这一步,相信看了Spring源码文章的朋友们就非常熟悉了,这就是Spring启动的核心代码。
需要注意的有两点:
1.obtainFreshBeanFactory 方法:在Spring中该方法会调用AbstractRefreshableApplicationContext
的refreshBeanFactory 方法,会完成beanDefinition加载。而在SpringCloud中,该方法会调用GenericApplicationContext
中的refreshBeanFactory方法,该方法只设置了beanFactory的序列化id,并不会加载beanDefinition。
这里还有一个小细节需要注意一下,我们可以看到AbstractRefreshableApplicationContext
的refreshBeanFactory方法会去创建一个beanFactory,那么SpringCloud是在哪创建beanFacotry的呢?
2.invokeBeanFactoryPostProcessors 方法:在SpringCloud中该方法负责加载beanDefinition。其实SpringCloud对Spring做了进一步的封装体现在,SpringCloud自己定义了许多BeanFactoryPostProcessor
类型的bean,SpringCloud自己负责注册这些bean,并让这些bean负责不同的功能。
我们也可以简单去看看invokeBeanFactoryPostProcessors方法:
我们可以进到invokeBeanFactoryPostProcessors方法内看看回调的逻辑:
我们再来看看此时容器中已经实例化的BeanFacoryPostProcessor
类型的bean:
这三个类型的后处理器中,CachingMetadataReaderFactoryPostProcessor
和ConfigurationWarningsPostProcessor
都实现了BeanDefinitionRegistryPostProcessor
接口,我们来看看它们的postProcessBeanDefinitionRegistry方法。
我们聚焦于核心流程,来看看CachingMetadataReaderFactoryPostProcessor
的postProcessBeanDefinitionRegistry方法:
回调完后,我们继续回到invokeBeanFactoryPostProcessors方法中:
我们查看beanFactory中的beanDefinitionMap 属性可以发现org.springframework.context.annotation.internalConfigurationAnnotationProcessor 对应的bean为ConfigurationClassPostProcessor
:
接着继续回调ConfigurationClassPostProcessor
的postProcessBeanDefinitionRegistry方法:
我们可以继续进到processConfigBeanDefinitions方法看看(这个方法巨长,我只截了关键代码):
所以在SpringCloud中,加载beanDefinition的主要工作是在ConfigurationClassPostProcessor
中完成的。
小结
本文主要内容是SpringCloud的启动流程源码分析,主要是聚焦于与Spring启动流程的差异性和特殊方面,有关Spring启动流程源码在之前的博客中已经有过讲解,因此就没有在本文中赘述。
本文并未涉及SpringCloud服务发现和服务注册的源码,该部分会在后面的博客中更新(最近上班当牛马,狠狠干活,更新可能比较慢)。