hand-Spring-boot 启动流程源码

直接看springboot的启动类

main方法就一句代码

SpringApplication.run

由此可知——main方法中办了两件事:创建了SpringApplication和调用了run方法

 创建SpringApplication

 下图是创建SpringApplication的源代码

 主要的作用就是下面注释的部分

this.webApplicationType = WebApplicationType.deduceFromClasspath();//设置应用类型①
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//设置初始化器②
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//设置监听器③

①设置应用类型

 主要意思是:如果时是web应用就设置应用类型为SERVLET即调用Tomcat,否则就是NONE,不调用Tomcat

REACTIVE: 暂时不用管

如何判断是否需要调用Tomcat呢?查看

deduceFromClasspath()

代码意思:如果调用了

WEBFLUX_INDICATOR_CLASS(点击查看是:org.springframework.web.reactive.DispatcherHandler)

 调用DispatcherHandler就会认定是Web应用需要调用Tomcat,于是就会设置为SERVLET

②设置初始化器

下图即是源码

 主要做了三件事:

Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));//使用Set去重,获取初始化的名称
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);//根据名称进行实例化
AnnotationAwareOrderComparator.sort(instances);// 根据优先级进行排序

③设置监听器

 打开自动配置spring-boot-autoconfigure源码其实可以发现,在spring.factories中配置了初始化器和监听器

spring.factories文件不止一个,同样监听器也不止以上这些。

至此:SpringApplication的创建基本完成,SpringApplication的构建是为了run()方法做铺垫

 执行Run方法

上面分析的SpringApplication过程,都是做的铺垫,现在到了启动的过程

1、获取、启动运行过程监听器

 如何获取运行监听器

下图中红色框中的代码

SpringApplicationRunListeners listeners = getRunListeners(args);

 查看getRunListeners的源码

 上图可以发现:还是调用了loadFactoryNames方法

 即还是从spring.factories中获取值

 问:spring.factories的作用是什么?

 答:“在spring-boot项目中pom文件里面添加的依赖中的bean是如何注册到spring-boot项目的spring容器中的呢?”,不难得出spring.factories文件是帮助spring-boot项目包以外的bean(即在pom文件中添加依赖中的bean)注册到spring-boot项目的spring容器的结论。由于@ComponentScan注解只能扫描spring-boot项目包内的bean并注册到spring容器中,因此需要@EnableAutoConfiguration注解来注册项目包外的bean。而spring.factories文件,则是用来记录项目包外需要注册的bean类名。——转载

     监听器的接口写好之后,是由哪个类来继承实现的呢?

接着就是 EventPublishingRunListener这个类了,见下图源码

 做了三件事:

this.application = application;
this.args = args;//1、 保存创建好的springApplication
this.initialMulticaster = new SimpleApplicationEventMulticaster();//2、初始化构建一个事件广播器
for (ApplicationListener<?> listener : application.getListeners()) {
   this.initialMulticaster.addApplicationListener(listener);
}//3、将SpringApplication创建好的监听器,添加到广播器中

启动运行监听器

调用监听器的starting方法

 调用starting方法,启动.....

2、环境构建

在run()方法中 查看源代码

 这一步主要用于加载系统配置以及用户的自定义配置(application.properties)

点击prepareEnvironment查看源码

ConfigurableEnvironment environment = getOrCreateEnvironment();// 获取到webApplication环境是servlet
configureEnvironment(environment, applicationArguments.getSourceArgs());// 加载系统属性配置
listeners.environmentPrepared(bootstrapContext, environment);//广播环境准备好事件,触发监听器
bindToSpringApplication(environment);//绑定环境到监听器

总结:环境构建这一步加载了系统环境配置、用户自定义配置并且广播了ApplicationEnvironmentPreparedEvent事件,触发监听器。

3、创建IOC容器

在run()方法中,找到创建IOC容器的代码

 跟进代码,找到最终执行的代码

 根据webApplicationType决定创建的类型,很显然,这里的是servlet,因此创建的是AnnotationConfigServletWebServerApplicationContext

这一步仅仅是创建了IOC容器,未有其他操作。

 4. IOC容器的前置处理

这一步真是精华了,在刷新容器之前做准备,其中有一个非常关键的操作:将启动类注入容器,为后续的自动化配置奠定基础。源码如下:(run方法中)

 点击查看源码

context.setEnvironment(environment);// 设置容器环境,包括自定义配置、系统环境配置
postProcessApplicationContext(context);// 执行后置处理
applyInitializers(context);//执行初始化器ApplicationContextInitializer
listeners.contextPrepared(context);//广播容器准备完事件,触发监听器
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();//注册启动参数bean,将容器参数封装成bean,注入容器
beanFactory.registerSingleton("springBootBanner", printedBanner);//设置banner
Set<Object> sources = getAllSources();//获取启动类指定的参数
load(context, sources.toArray(new Object[0]));//加载启动类,将启动类注入容器createBeanDefinitionLoader
listeners.contextLoaded(context);//发布容器已加载事件

从上图可以看出步骤很多,下面将会详细介绍几个重点的内容。

调用初始化器

部分内置的初始化器

 

加载启动类,注入容器

这一步是将主启动类加载到IOC容器中,作为后续自动配置的入口。

SpringApplication构建过程中将主启动类放置在primarySources这个集合中,此时的getAllSources()即是从其中取值,如下图:

 这里取出的就是主启动类,当然你的项目中可能不止一个,接下来就是将其加载到IOC容器中了,源码如下:

跟着代码进去,其实主要逻辑都在BeanDefinitionLoader.load()方法,如下图:

this.annotatedReader.register(source);//以注解的方式,将启动类bean信息存入beanDefinitionMap

5. 刷新容器

 刷新容器完全是Spring的功能了,比如初始化资源,初始化上下文广播器等,这个就不再详细介绍,有兴趣可以看看Spring的源码。

 6. IOC容器的后置处理

一个扩展方法,源码如下:

 默认为空,如果有自定义需求可以重写,比如打印一些启动结束日志等。

 7. 发出结束执行的事件

同样是EventPublishingRunListener这个监听器,广播ApplicationStartedEvent事件。

 但是这里广播事件和前几次不同,并不是广播给SpringApplication中的监听器(在构建过程中从spring.factories文件获取的监听器)。因此在IOC容器中注入的监听器(使用@Component等方式注入的)也能够生效。前面几个事件只有在spring.factories文件中设置的监听器才会生效。

 这里并没有用事件广播器SimpleApplicationEventMulticaster广播事件,而是使用ConfigurableApplicationContext直接在IOC容器中发布事件。

8. 执行Runners

Spring Boot 提供了两种Runner让我们定制一些额外的操作,分别是CommandLineRunnerApplicationRunner,关于这两个的区别,后面文章详细介绍。

调用的源码如下:

 深入源码:

 逻辑很简单,从IOC容器中获取,遍历调用。

总结

Spring Boot 启动流程相对简单些,作者将其细分了以上八个步骤,希望能够帮助读者理解,流程图如下:

 Spring Boot启动流程就介绍到这里了,需要重点理解run()方法执行的八个步骤以及事件、初始化器、监听器等组件的执行时间点。

猜你喜欢

转载自blog.csdn.net/awodwde/article/details/118889818