Springboot源码系列之SpringBoot启动流程(

作者 : 玉龙小怪兽

1
复制代码成长的路总是伴随着艰辛,坚持,沉淀 , 分享 , 让自己和他人都有所收获

目录

  • 一、前言
  • 二、构建SpringApplication的过程
  • 三、SpringBoot核心流程之run()方法
  • 四、总结

一、前言

每一天都在使用Springboot , 都在 @Controller, @Service , @Configuration ,@Autowired等一系列注解的照顾下写出了一行一行代码。是的 , 我们是完成了功能的开发,我们是熟练的使用了Springboot提供给我们的注解,但却总感觉缺少了些什么。抛开这些常用的注解 , 大家想想你对Springboot还了解多少 ?

[所以我们需要去探究Springboot的设计思想 ,去看SpringBoot的代码风格 ,去基于Springboot提供给我们的接口做二次开发,从而提高对SpringBoot的整体把握,而不是局限于那些常用的注解]

二、构建SpringApplication的过程

Springboot的启动入口

1
2
3
4
5
6
java复制代码@SpringBootApplication
public class StartApp {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(StartApp.class, args);
}
}

通过main方法调用SpringApplication的静态方法run方法, 最后返回一个容器 , 完成Springboot的整个启动过程

1
2
3
java复制代码public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args){
return new SpringApplication(primarySources).run(args);
}

run方法它去构建了一个SpringApplication的实例,然后调用run方法去完成Springboot的启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
scss复制代码public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//获取资源加载器 (null)
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//SpringApplication实例记录主配置启动类,StartApp。class
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//选择应用程序的类型(servlet)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//从类路径下的META-INF/spring.factories下读取所有ApplicationContextInitializer的实现类并实例化存储到initializers属性里
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//从类路径下的META-INF/spring.factories下读取所有ApplicationListener的实现类并实例化存储到listeners属性里
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//存储主启动类到mainApplicationClass属性里
this.mainApplicationClass = deduceMainApplicationClass();
}

ApplicationContextInitializer(系统初始化器) 主要是用于准备容器时applyInitializers()方法执行里面的方法给容器中做一些初始化的处理

1
2
3
4
5
6
7
8
9
java复制代码public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);

}

加载META-INF/spring.factories下key值为org.springframework.context.ApplicationContextInitializer的实现类,Springboot默认的有如下:

1
2
3
4
5
6
7
properties复制代码# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

这里我们也可以按照Springboot它的规范 , 自定义我们自己的初始化类,做我们自己想做的事情。

1
2
properties复制代码org.springframework.context.ApplicationContextInitializer=\
com.captain.demo.RegisterBeanApplicationContextInitializer

ApplicationListener,Sping的监听器,可以对你感兴趣的事件进行监听和处理。

1
2
3
4
5
6
7
8
9
java复制代码public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);

}

加载META-INF/spring.factories默认的key = org.springframework.context.ApplicationListener的实现类,同样可以自定义属于我们自己的监听器,Springboot默认的有如下:

1
2
3
4
5
6
7
8
9
10
11
properties复制代码org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

关于Spring的事件机制 ,后面再用一篇文章详细的说明。Spring的事件机制需要三个角色: 1.事件的发布器 2.事件的载体 3.事件的监听者

这里在介绍一下SpringFactoriesLoader这个类它的作用:

​ 1.从classpath下的多个jar包特定的位置读取文件并初始化类(一般为 spring.factories下的)

​ 2.文件的内容以key为全限定名(抽象类/接口),value是实现类的全类名用,分割

1
2
3
4
5
6
7
8
java复制代码private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}

三、SpringBoot核心流程之run()方法

run方法的核心做的事情如下

1.构建系统需要的环境

2.创建并配置容器

3.加载,解析class为BeanDefinitions,注册到容器中

4.实例化Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
scss复制代码public ConfigurableApplicationContext run(String... args) {
//构建任务观察器
StopWatch stopWatch = new StopWatch();
//开始记录时间
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//设置程序在headless模式下工作
configureHeadlessProperty();
//默认情况只有一个SpringApplicationRunListener工作
SpringApplicationRunListeners listeners = getRunListeners(args);
//发布 ApplicationStartingEvent 事件,监听者监听到该事件后, 执行相应的逻辑代码
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备运行环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//打印 Banner
Banner printedBanner = printBanner(environment);
//创建AnnotationConfigServletWebServerApplicationContext容器
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//准备上下文,详细说明看下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新容器,详细说明看下文
refreshContext(context);
//刷新容器后的处理(空处理)
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//发布ApplicationStartedEvent(应用程序启动事件)
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}

try {
//发布ApplicationReadyEvent(应用程序就绪事件)
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
//返回上下文
return context;
}

prepareEnvironment() :准备环境的过程中, 主要做了以下几件事情

​ 1.创建系统的运行环境,并初始化一些属性源

​ 2.给环境设置转化器,重新排列PropertySource

​ 3.选择激活的配置文件 dev-properties,prod-properties

​ 4.将创建的环境与SpringApplication关联

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
scss复制代码private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//创建运行环境(StandardEnvironment),分别初始化MapPropertySource / ConfigurationPropertySourcesPropertySource/PropertiesPropertySource/SystemEnvironmentPropertySource
ConfigurableEnvironment environment = getOrCreateEnvironment();
//设置转化器,配置属性源,配置 激活的配置文件
configureEnvironment(environment, applicationArguments.getSourceArgs());
//将ConfigurationPropertySourcesPropertySource属性源放在最前面
ConfigurationPropertySources.attach(environment);
//发布ApplicationEnvironmentPreparedEvent(环境准备事件)
listeners.environmentPrepared(environment);
//将环境与SpringApplication进行绑定
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//将ConfigurationPropertySourcesPropertySource属性源放在最前面
ConfigurationPropertySources.attach(environment);
return environment;
}

prepareContext() :准备上下文主要做了以下几件事情:

​ 1.给容器设置环境,给factoty设置转化器

​ 2.执行所有initializers属性的初始化方法,对容器进行初始化处理

​ 3.发布ApplicationContextInitializedEvent(容器初始化事件)

​ 4.注册主启动类(StartApp)到BeanDefinitionMap中

​ 5.发布 ApplicationPreparedEvent(应用程序准备事件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
scss复制代码private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//给容器设置环境
context.setEnvironment(environment);
//给factory工厂设置转换服务
postProcessApplicationContext(context);
//执行之前加载进来的initializers属性的初始化方法,对容器进行初始化处理
applyInitializers(context);
//发布ApplicationContextInitializedEvent(容器初始化事件)
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//下面几步操作给beanFactory设置属性
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//注册主启动类(StartApp)到BeanDefinitionMap中
load(context, sources.toArray(new Object[0]));
//发布 ApplicationPreparedEvent(应用程序准备事件)
listeners.contextLoaded(context);
}

refreshContext(context) :刷新容器的这个方法是最为核心的,要做了以下几件事情

​ 1.准备刷新容器,给容器设置同步标识,具体方法

​ 2.给容器中注入一些容器需要的系统bean,beanfactoryPostProcessor等

​ 3.执行BeanFactoryPostProcessor的postProcessBeanDefinitionRegistry方法,将扫描到的注解中的所有的

​ BeanDefinition注册到容器中

​ 4.注册Bean创建前后的BeanPostProcessor

​ 5.国际化处理

6.初始化事件的发布者

7.给事件的发布者注册监听器

​ 8.实例化非懒加载的所有的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
scss复制代码@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//1.设置容器的激活状态,验证必要的属性(可以设置环境中必要的属性),
prepareRefresh();

// Tell the subclass to refresh the internal bean factory.
//Beanfactoty设置序列化id
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
//给beanFactoty添加BeanPostProcessor,设置beanFactoty的属性
prepareBeanFactory(beanFactory);

try {
//添加WebApplicationContextServletContextAwareProcessor,
//设置 WebApplicationScope 为 request /session
postProcessBeanFactory(beanFactory);
//执行BeanFactoryPostProcessor的postProcessBeanDefinitionRegistry方法
//对BeanDefinition进行增删改
//加载所有的自动配置类并注册到beanDefinitionMap中
invokeBeanFactoryPostProcessors(beanFactory);
//beanFactory添加BeanPostProcessors,对Bean初始化前后进行操作
registerBeanPostProcessors(beanFactory);
//国际化处理
initMessageSource();
//初始化 ApplicationEventMulticaster(应用程序事件多播器)
initApplicationEventMulticaster();
//构建web服务器 (tomcat服务器)
onRefresh();
//给事件发布器注册监听者
registerListeners();
//实例化所有的非懒加载的单例bean
finishBeanFactoryInitialization(beanFactory);
//清除资源缓存,给上下文初始化生命周期处理器
//发布容器刷新完成的事件
finishRefresh();
}

catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

// Destroy already created singletons to avoid dangling resources.
destroyBeans();

// Reset 'active' flag.
cancelRefresh(ex);

// Propagate exception to caller.
throw ex;
}

finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

四、总结

1
erlang复制代码坚持,沉淀 , 分享 , 让自己和他人都有所收获.
  • 有身边的朋友说,我读了源码,好像就是在背这个源码的执行流程,过几天就忘了,活生生的变成了八股文,流程只是让我们知道他到底干了点撒事情,更加需要我们去理解的是:他的类与类之间是如何面向对象设计的,框架中如何运用设计模式的,我想这样的收获肯定是大于你去记住那些流程来得更加的有意义。
  • 通过上面对Spingboot的整体脉络的理解,我想大家已经可以摸索出一点心得了。 Springboot在启动时,到底都干了哪些事情,如何利用ApplicationContextInitializer,ApplicationListener去做自己的扩展,自动化配置是在哪里被加载成为BeanDefinition的等。
  • 当然本篇文章只是把Springboot启动的整体脉络数理了,并没有做更深入的探究,下一篇文章将会把ApplicationContextInitializer和ApplicationListener进行实战,基于他们做一些功能的二次开发,会去构建一个属于自己的framework
  • 文章基于个人的理解以及参考优秀的书籍,文章,如有错误,敬请指出!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%