SpringCloud启动源码分析

前言

​   在前面的博客中我们已经详细分析过Spring的启动源码,包括beanDefinition的加载、bean的初始化以及各种后处理器的回调,这个过程还是比较复杂的。

​   作为Spring全家桶中的一员,SpringCloud也是对Spring做的封装,底层逻辑其实一致,我们先来看看SpringCloud的入口方法:

​    我们直接去看看SpringApplicationrun 方法,根据调用形式可以看到这里调用的是SpringApplication的静态run方法:

​   这部分的代码逻辑是:先创建一个SpringApplication对象,然后调用这个对象的的run 方法。所以核心逻辑有两个部分:SpringApplication的构造方法和它的非静态run方法。

1. SpringApplication的构造方法

​   我们从源码可以看到,SpringApplication类没有继承任何类,也没有实现任何接口(我们在讲解Spring时,ClassPathXmlApplicationContext类图就非常复杂)。

​   我们接着来看看SpringApplication的构造方法,该构造方法的核心是调用getSpringFactoriesInstances方法,完成三个类型bean的实例化,注意这一步我们并未创建beanFactory:

​   这里完成了三个类型bean的实例化:BootstrapRegistryInitializerApplicationContextInitializerApplicationListener

​    我们接着看看getSpringFactoriesInstances方法,该方法核心逻辑有两个:

​   1.通过SpringFactoriesLoaderloadFactoryNames获得指定类型的类的全路径名;

​   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方法:

​   最后还是调用到了AbstractApplicationContextrefresh 方法,是不是很熟悉,没错就是ClassPathXmlApplicationContext调用的refresh 方法,但是Spring Cloud在Spring基础上做了封装修改,refresh方法也有小改动,但是大体并没有变:

​   到了这一步,相信看了Spring源码文章的朋友们就非常熟悉了,这就是Spring启动的核心代码。

​   需要注意的有两点:

​   1.obtainFreshBeanFactory 方法:在Spring中该方法会调用AbstractRefreshableApplicationContextrefreshBeanFactory 方法,会完成beanDefinition加载。而在SpringCloud中,该方法会调用GenericApplicationContext中的refreshBeanFactory方法,该方法只设置了beanFactory的序列化id,并不会加载beanDefinition。

​   这里还有一个小细节需要注意一下,我们可以看到AbstractRefreshableApplicationContextrefreshBeanFactory方法会去创建一个beanFactory,那么SpringCloud是在哪创建beanFacotry的呢?

​   2.invokeBeanFactoryPostProcessors 方法:在SpringCloud中该方法负责加载beanDefinition。其实SpringCloud对Spring做了进一步的封装体现在,SpringCloud自己定义了许多BeanFactoryPostProcessor类型的bean,SpringCloud自己负责注册这些bean,并让这些bean负责不同的功能。

​   我们也可以简单去看看invokeBeanFactoryPostProcessors方法:

​   我们可以进到invokeBeanFactoryPostProcessors方法内看看回调的逻辑:

​   我们再来看看此时容器中已经实例化的BeanFacoryPostProcessor类型的bean:

​   这三个类型的后处理器中,CachingMetadataReaderFactoryPostProcessorConfigurationWarningsPostProcessor都实现了BeanDefinitionRegistryPostProcessor接口,我们来看看它们的postProcessBeanDefinitionRegistry方法。

​   我们聚焦于核心流程,来看看CachingMetadataReaderFactoryPostProcessorpostProcessBeanDefinitionRegistry方法:

​   回调完后,我们继续回到invokeBeanFactoryPostProcessors方法中:

​   我们查看beanFactory中的beanDefinitionMap 属性可以发现org.springframework.context.annotation.internalConfigurationAnnotationProcessor 对应的bean为ConfigurationClassPostProcessor

​   接着继续回调ConfigurationClassPostProcessorpostProcessBeanDefinitionRegistry方法:

​   我们可以继续进到processConfigBeanDefinitions方法看看(这个方法巨长,我只截了关键代码):

​   所以在SpringCloud中,加载beanDefinition的主要工作是在ConfigurationClassPostProcessor中完成的。

小结

本文主要内容是SpringCloud的启动流程源码分析,主要是聚焦于与Spring启动流程的差异性和特殊方面,有关Spring启动流程源码在之前的博客中已经有过讲解,因此就没有在本文中赘述。

本文并未涉及SpringCloud服务发现和服务注册的源码,该部分会在后面的博客中更新(最近上班当牛马,狠狠干活,更新可能比较慢)。

相关推荐
猿java3 小时前
在Spring中,事务是如何隔离的?
java·后端·spring
岁岁岁平安3 小时前
spring学习(spring的IoC思想、spring容器、spring配置文件、依赖注入(DI)、BeanProxy机制(AOP))
java·学习·spring·di·aop·beanfactory·ioc容器
橘子海全栈攻城狮3 小时前
[源码+调试+讲解]微信小程序的成都美食分享系统springboot
开发语言·spring boot·后端·微信小程序·小程序·美食
小奏技术4 小时前
从来都是在controller层进行参数校验,便对吗?
后端
uhakadotcom4 小时前
杀疯了,90后博士第一次创业,刚成立1年半,融资12亿,估值30亿
后端·算法·架构
人生の三重奏4 小时前
django项目3——连接sqlite数据库
后端·python·django
hshpy5 小时前
why spring boot not load NacosConfigBootstrapConfiguration class
java·spring boot·后端
happycao1235 小时前
Spring Cloud Sleuth 分布式链路追踪入门
spring cloud·分布式链路·slueth