博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Boot启动过程及回调接口汇总
阅读量:4344 次
发布时间:2019-06-07

本文共 7750 字,大约阅读时间需要 25 分钟。

Spring Boot启动过程及回调接口汇总 
链接: 


来自:

注:本文基于, 撰写。

启动顺序

Spring boot的启动代码一般是这样的:

1
2
3
4
5
6
@SpringBootApplication
public
class
SampleApplication {
  
public
static
void
main(String[] args) 
throws
Exception {
    
SpringApplication.run(SampleApplication.
class
, args);
  
}
}

初始化SpringApplication

1、SpringApplication#run(Object source, String... args)

2、 -> SpringApplication(sources)

3、SpringApplication#initialize(Object[] sources) 

4、 添加source(复数),SpringApplication使用source来构建Bean。一般来说在run的时候都会把标记的类(本例中是SampleApplication)放到sources参数里,然后由这个类出发找到Bean的定义。

5、 初始化列表(见附录)

6、 初始化列表(见附录)

7、 -> SpringApplication#run(args),进入运行阶段

推送ApplicationStartedEvent

SpringApplication#run(args)

1、 初始化 (的集合)。它内部只包含。

2、 推送给所有的(见附录)。 下面是关心此事件的listener:

  • (见附录)

准备Environment

SpringApplication#run(args)->->prepareEnvironment(...)准备。

1、 创建(见附录)。

2、 配置,将命令行和默认参数整吧整吧,添加到。

3、 推送给所有的(见附录)。下面是关心此事件的listener:

4、

5、

6、

7、(见附录)

8、

9、

10、

11、

可以参考了解构建好之后,其内部到底有些啥东东。

创建及准备ApplicationContext

SpringApplication#run(args)

1、->SpringApplication#createApplicationContext()创建。可以看到实际上创建的是或。

2、在构造的时候,间接注册了一个的Bean:。经由->。

->SpringApplication#prepareContext(...)准备

->context.setEnvironment(environment),把之前准备好的塞给

->postProcessApplicationContext(context),给设置了一些其他东西

->applyInitializers(context),调用之前准备好的

->listeners.contextPrepared(context)->,但实际上啥都没做。

->load,负责将source(复数)里所定义的Bean加载到里,在本例中就是SampleApplication,这些source是在初始化SpringApplication阶段获得的。

->listeners.contextLoaded(context)->。

10、将自己拥有的加入到

11、发送。目前已知关心这个事件的有、、

要注意的是在这个阶段,里只有SampleApplication,SampleApplication是Bean的加载工作的起点。

刷新ApplicationContext

根据前面所讲,这里的实际上是 ->或者

SpringApplication#run(args) ->->SpringApplication#refreshContext(context) ->->SpringApplication#refresh(context) ->->AbstractApplicationContext#refresh

1、->AbstractApplicationContext#prepareRefresh(),做了一些初始化工作,比如设置了当前Context的状态,初始化propertySource(其实啥都没干),检查required的property是否都已在Environment中(其实并没有required的property可供检查)等。

->obtainFreshBeanFactory(),获得,实际上这里获得的是

->prepareBeanFactory(beanFactory)准备

4、给beanFactory设置了ClassLoader

5、给beanFactory设置了

6、给beanFactory设置了

7、给beanFactory添加了(的实现类),需要注意的是它是第一个被添加到的

8、给beanFactory设置忽略解析以下类的依赖:、、、、。原因是注入这些回调接口本身没有什么意义。

9、给beanFactory添加了以下类的依赖解析:、、、

10、给beanFactory添加用来处理的回调,在和AspectJ集成的时候会用到

11、把getEnvironment()作为Bean添加到beanFactory中,Bean Name: environment

12、把getEnvironment().getSystemProperties()作为Bean添加到beanFactory中,Bean Name: systemProperties

13、把getEnvironment().getSystemEnvironment()作为Bean添加到beanFactory中,Bean Name: systemEnvironment

->postProcessBeanFactory(beanFactory),后置处理,实际啥都没做

->invokeBeanFactoryPostProcessors(beanFactory),利用,对beanFactory做后置处理。调用此方法时有四个:

的内部类,是在 创建及准备ApplicationContext 2.3 时添加的:

的内部类,是在 创建及准备ApplicationContext 2.3 时添加的:

的内部类,是在 创建及准备ApplicationContext 2.6 时添加的:->->

,负责读取是在 创建及准备ApplicationContext 1.1 时添加的

->registerBeanPostProcessors(beanFactory),注册

->initMessageSource(),初始化,不过其实此时的是个Noop对象。

->initApplicationEventMulticaster(),初始化。

->onRefresh(),这个方法啥都没做

->registerListeners(),把自己的注册到里,并且将之前因为没有而无法发出的发送出去。

->finishBeanFactoryInitialization。注意,在这一步的时候才会实例化所有non-lazy-init bean,这里说的实例化不只是new而已,注入、都会执行。

->finishRefresh()

27、在发送了

调用 ApplicationRunner 和 CommandLineRunner

SpringApplication#run(args) ->afterRefresh(context, applicationArguments) ->callRunners(context, args) -> 先后调用了当前中的和。关于它们的相关文档可以看。

需要注意的是,此时的已经刷新完毕了,该有的Bean都已经有了。

推送ApplicationReadyEvent or ApplicationFailedEvent

SpringApplication#run(args)->listeners.finished(context, null) 间接地调用了EventPublishingRunListener#getFinishedEvent,发送了或回调接口

ApplicationContextInitializer

 

加载方式:读取classpath*:META-INF/spring.factories中key等于org.springframework.context.ApplicationContextInitializer的property列出的类

排序方式:

已知清单1:spring-boot-1.4.1.RELEASE.jar!/META-INF/spring.factories

1、(优先级:0)

2、(优先级:Ordered.LOWEST_PRECEDENCE – 10)

3、(优先级:无=Ordered.LOWEST_PRECEDENCE)

4、(优先级:无=Ordered.LOWEST_PRECEDENCE)

已知清单2:spring-boot-autoconfigure-1.4.1.RELEASE.jar!/META-INF/spring.factories

1、(优先级:无=Ordered.LOWEST_PRECEDENCE)

2、(优先级:无=Ordered.LOWEST_PRECEDENCE)

ApplicationListener

 

加载方式:读取classpath*:META-INF/spring.factories中key等于org.springframework.context.ApplicationListener的property列出的类

排序方式:

已知清单1:spring-boot-1.4.1.RELEASE.jar!/META-INF/spring.factories中定义的

1、(优先级:无=Ordered.LOWEST_PRECEDENCE)

2、(优先级:Ordered.LOWEST_PRECEDENCE – 10)

3、(优先级:Ordered.LOWEST_PRECEDENCE)

4、(优先级:ConfigFileApplicationListener.DEFAULT_ORDER + 1)

5、(优先级:Ordered.HIGHEST_PRECEDENCE + 10)

6、(优先级:0)

7、(优先级:无=Ordered.LOWEST_PRECEDENCE)

8、(优先级:LoggingApplicationListener的优先级 + 1)

9、(优先级:Ordered.HIGHEST_PRECEDENCE + 20)

已知清单2:spring-boot-autoconfigure-1.4.1.RELEASE.jar!/META-INF/spring.factories中定义的

10、

SpringApplicationRunListener

加载方式:读取classpath*:META-INF/spring.factories中key等于org.springframework.boot.SpringApplicationRunListener的property列出的类

排序方式:

已知清单:spring-boot-1.4.1.RELEASE.jar!/META-INF/spring.factories定义的

1、org.springframework.boot.context.event.EventPublishingRunListener(优先级:0)

EnvironmentPostProcessor

可以用来自定义()。

加载方式:读取classpath*:META-INF/spring.factories中key等于org.springframework.boot.env.EnvironmentPostProcessor的property列出的类

排序方式:

已知清单:spring-boot-1.4.1.RELEASE.jar!/META-INF/spring.factories定义的

1、(优先级:ConfigFileApplicationListener.DEFAULT_ORDER – 1)

2、(优先级:Ordered.HIGHEST_PRECEDENCE + 5)

BeanPostProcessor

 

用来对Bean实例进行修改的勾子,根据Javadoc ApplicationContext会自动侦测到BeanPostProcessor Bean,然后将它们应用到后续创建的所有Bean上。

BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor

负责调用和。在之前被调用。

*Aware

*Aware是一类可以用来获得Spring对象的interface,这些interface都继承了,已知的有:

@Configuration 和 Auto-configuration

替代xml来定义的一种手段。也是定义的一种手段。

这两者的相同之处有:

1、都是使用注解的类,这些类里都可以定义、、。

都可以使用来根据情况选择是否加载

而不同之处有:

1、加载方式不同:

  • 普通则是通过扫描package path加载的

  • 的是通过读取classpath*:META-INF/spring.factories中key等于org.springframework.boot.autoconfigure.EnableAutoConfiguration的property列出的加载的

2、加载顺序不同:普通的加载在之前,但是有例外情况,看下面。
3、内部加载顺序可控上的不同:

  • 普通则无法控制加载顺序

  • 可以使用、、

以下情况下会在普通前加载:

1、如果出现在最初的扫描路径里(@ComponentScan),就会被提前加载到,然后被当作普通的处理,这样和就没用了。参看例子代码里的InsideAutoConfiguration和InsideAutoConfiguration2。

如果提供,那么它会被提前加载。参见例子代码里的BeanPostProcessorAutoConfiguration。

如果使用了,那么会被提前加载。参见例子代码里的ImportBeanDefinitionRegistrarAutoConfiguration。

如果使用了,那么会被提前加载。参见例子代码里的UselessDeferredImportSelectorAutoConfiguration。

参考和附录了解Spring boot内部处理机制。

AnnotatedBeanDefinitionReader

这个类用来读取和,并将注册到里。

ConfigurationClassPostProcessor

是一个,负责处理。

需要注意一个烟雾弹:看->。而order的值则是在从注解中提取的。 这段代码似乎告诉我们它会对进行排序,然后按次序加载。 实际上不是的,是一个递归加载的过程。在本例中,是先从SampleApplication开始加载的,而事实上在这个时候,也就只有SampleApplication它自己可以提供排序。 而之后则直接使用了,它里面并没有排序的逻辑。

关于排序的方式简单来说是这样的:的排序根据且只根据排序,如果没有则优先级最低。

ConfigurationClassParser

前面讲了使用,实际上加载的工作是在这里做的。

下面讲以下加载的顺序:

1、以SampleApplication为起点开始扫描

2、扫描得到所有的和,从中读取并导入:

3、如果注解了

4、如果使用的是,则递归导入

5、如果使用的是,则递归导入

6、如果使用的是,则仅收集不导入

7、如果注解了,则递归导入

8、迭代之前收集的,递归导入

那在哪里呢? 实际上是在第3步里,存在注解,它使用了, 是一个,所以也就是说,是在普通之后再加载的。

顺带一提,如果里再使用,那么效果和使用效果是一样的,不会再被延后处理。参见例子代码里的UselessDeferredImportSelectorAutoConfiguration。

EnableAutoConfigurationImportSelector

负责导入。

它利用对进行排序。逻辑算法是:

1、先根据类名排序

2、再根据排序,如果没有则优先级最低

3、再根据,排序

内置类说明

LoggingApplicationListener

用来配置日志系统的,比如logback、log4j。Spring boot对于日志有,如果你想,那么也请参考本文中对于的被调用时机的说明以获得更深入的了解。

StandardEnvironment

有一个,它里面有多个,负责提供property(即property的提供源),目前已知的实现有:、、等。当查找property值的时候,是从里依次查找的,而且一旦查找到就不再查找,也就是说如果要覆盖property的值,那么就得提供顺序在前的。

ConfigFileApplicationListener

用来将application.properties加载到中。

内部使用了(见附录)自定义

ApplicationContextAwareProcessor

实现了接口,根据javadoc这个类用来调用以下接口的回调方法:

1、

2、

3、

3、

4、

5、

AnnotationConfigApplicationContext

根据,这个类用来将和作为输入来注册BeanDefinition。

特别需要注意的是,在中讲到其支持@Bean的覆盖:

In case of multiple @Configuration classes, @Bean methods defined in later classes will override those defined in earlier classes. This can be leveraged to deliberately override certain bean definitions via an extra @Configuration class.

它使用来读取和。

AnnotatedBeanDefinitionReader

在其构造函数内部间接()的给注册了几个与相关注解的处理器。

1、

2、

3、

4、

5、

6、

转载于:https://www.cnblogs.com/wangdaijun/p/7988084.html

你可能感兴趣的文章
Azure云服务托管恶意软件
查看>>
My安卓知识6--关于把项目从androidstudio工程转成eclipse工程并导成jar包
查看>>
旧的起点(开园说明)
查看>>
生产订单“生产线别”带入生产入库单
查看>>
crontab导致磁盘空间满问题的解决
查看>>
自定义滚动条
查看>>
APP开发手记01(app与web的困惑)
查看>>
笛卡尔遗传规划Cartesian Genetic Programming (CGP)简单理解(1)
查看>>
初识前端作业1
查看>>
ffmpeg格式转换命令
查看>>
万方数据知识平台 TFHpple +Xpath解析
查看>>
Hive实现oracle的Minus函数
查看>>
秒杀多线程第四篇 一个经典的多线程同步问题
查看>>
RocketMQ配置
查看>>
蚂蚁金服井贤栋:用技术联手金融机构,形成服务小微的生态合力
查看>>
端口号大全
查看>>
机器学习基石笔记2——在何时可以使用机器学习(2)
查看>>
POJ 3740 Easy Finding (DLX模板)
查看>>
MySQL 处理重复数据
查看>>
关于typedef的用法总结(转)
查看>>