直接看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
让我们定制一些额外的操作,分别是CommandLineRunner
和ApplicationRunner
,关于这两个的区别,后面文章详细介绍。
调用的源码如下:
深入源码:
逻辑很简单,从IOC容器
中获取,遍历调用。
总结
Spring Boot
启动流程相对简单些,作者将其细分了以上八个步骤,希望能够帮助读者理解,流程图如下:
Spring Boot
启动流程就介绍到这里了,需要重点理解run()
方法执行的八个步骤以及事件、初始化器、监听器等组件的执行时间点。