type
status
date
slug
summary
tags
category
icon
password
😀
这里写文章的前言: 一个简单的开头,简述这篇文章讨论的问题、目标、人物、背景是什么?并简述你给出的答案。
可以说说你的故事:阻碍、努力、结果成果,意外与转折。
 

📝 SpringBoot 之 run 方法启动

SpringBoot 无配置式启动,大大的减轻了以前需要配置很多xml中的bean/aop/事物等. 只需要使用@SpringBootApplication 和 SpringApplication.run() 方法就可以轻松的跑起来了. 想要看怎么这么神奇,就需要看其run() 方法的执行操作.
run() 方法又包含了之前分析的 refresh 方法,但是这里的refresh 方法是要比之前的 单独分析 Spring 的 refresh方法的内容要多得多.

run方法整体

我们先看下 run 方法整体的流程. 其实也不难看出来, 也是new对象,调用方法. 但是人家可不仅仅是这二种就轻松搞定了的. 而且里面采用了大量的设计模式等知识.
从run 方法整体来看, 先是启动 StopWatch,然后获取出SpringApplicationRunListener对应的Listener,最后封装到SpringApplicationRunListeners这个Object类中, 接着调用这个类的 starting 方法,也就是启动方法.
再往下就是创建 DefaultApplicationArguments,准备环境,然后往System.setProperty给key为"spring.beaninfo.ignore" 设置ignore.toString() 方法进去. 打印Banner(也就是我们看到的SpringBoot,用很多杠杆给拼接起来的)
创建一个AnnotationConfigApplicationContext的Context(我个人觉得context是对beanFactory的一种扩展吧).
接着在 获取 SpringBootExceptionReporter 对应的信息(从META-INF/spring.factiories中获取),返回的是一个集合对象.
再就是为context进行准备.
接着就是调用refresh 方法,这个方法会加载beanPostProcssor,初始化bean等操作.
afterRefresh 就是创建完容器之后执行一些额外的操作
stopWatch.stop 就是打印启动计时等信息
再就是执行 listeners.started() 方法, org.springframework.boot.context.event.EventPublishingRunListener#started,也就是new一个ApplicationStartedEvent给传播出去,然后监听器拿到这个事件后,执行相应的逻辑处理关系.
callRunners 获取出 ApplicationRunner和CommandLineRunner,从beanFactory中获取出来,然后调用其run方法
接着就是执行 listeners.running()方法:
org.springframework.boot.context.event.EventPublishingRunListener#running,其内部就是new一个ApplicationReadyEvent事件给传播出去,然后相应的监听器拿到这个事件,就会去执行其内部的逻辑代码.
return 一个 context回去,也就是run方法已经走完了.
 

run 中 configureHeadlessProperty() 方法

最后是给 java.awt.headless 设置了默认值 headless = true, 所以也就是给 SYSTEM_PROPERTY_JAVA_AWT_HEADLESS 在System中设置上了true. 所以这个方法也没有特别重要的地方讲解
 

run 中 getRunListeners() 方法

方法依次往下执行的. 可以看出这个方法, 根据 SpringApplicationRunListener.class 从 META-INF/spring.factories 中获取并且实例化获取出来的结果. 然后放入到 SpringApplicationRunListeners 这个对象中. 从 META-INF/spring.factories中获取出来的数据,是有一个cache缓存的,也就是使用的Map.
 

实战一把

参考 org.springframework.boot.context.event.EventPublishingRunListener
先写一个类 :
 
然后在META-INF/spring.factories中配置下:
控制台的log输出顺序: 可以看到打印的参数顺序, 也就是先反射调用实例化, 然后接着调用 starting 方法,接着就是started 方法, 最后就是 running方法. 可以看到执行的一次顺序.
调用到了 private final String[] args; 有参数构造方法 15:47:56.498 [main] INFO com.iyang.bootsourceread.listener.GavinYangEventPublishingRunListener - GavinYangEventPublishingRunListener 调用 starting 方法成功
2020-07-02 15:47:57.069 INFO 4063 --- [ main] .b.l.GavinYangEventPublishingRunListener : GavinYangEventPublishingRunListener 调用到了 started方法 2020-07-02 15:47:57.070 INFO 4063 --- [ main] .b.l.GavinYangEventPublishingRunListener : GavinYangEventPublishingRunListener 调用 running 方法成功
具体的执行,可自行debug进去看,是怎么具体走到哪步的. 与EventPublishingRunListener不同的是, 我们这里是没有new什么事件丢给监听器的. 就是简单的打印了几句log.

run 中 starting() 方法

org.springframework.boot.SpringApplicationRunListeners#starting
该方法就是直接走 SpringApplicationRunListeners中的starting方法. getRunListeners方法最后返回的是SpringApplicationRunListeners,该类中是包装了 我们SpringApplicationRunListener.class对应的Listener的.
然后这里就会迭代每个 listener 并且会调用到 starting 放. 这个结果在上面的方法小实战中,是可以很明显的体会到的.

run 中 prepareEnvironment() 方法

可以看到这该方法最后处理的都是和 Environment有关的.
 

run 中 createApplicationContext() 方法

创建 ApplicationCntext, 该方法可以看到 根据 this.webApplicationType来创建 ApplicationContext的class的,然后使用BeanUtils.instantiateClass() 方法来实例化这个bean. 这里还是很好理解的,就是创建一个 ApplicationContext. 最后返回这个 context给赋值到 context 这个参数.
 

run 中 getSpringFactoriesInstances(SpringBootExceptionReporter.class...) 方法

之前阅读到的 getRunListeners方法,其内部也是走了 getSpringFactoriesInstances 这个方法,然后这里也是根据SpringBootExceptionReporter.class来获取出相应的集合数据.
所以这里直接说,返回的集合中,如果没有进行扩展的话,那么这里就只有 FailureAnalyzers这一个.
org.springframework.boot.diagnostics.FailureAnalyzers
如果是有异常的话,就会放入到 SpringBootExceptionReporter 这个里,由其子类给打印出来. Reporter这个意思还是很好理解的.
 

run 中 prepareContext() 方法

可以看到这个方法基本都是一直在操作 context,先是给context设置environment.
然后判断internalConfigurationBeanNameGenerator要不要注册到 beanFactory中去. 如果this.resourceLoader不是null的话,就会分别设置ResourceLoader/ClassLoader(看具体的值),给ConversionService set 到beanFactory中去.
调用 ApplicationContextInitializer 的 initialize 方法.
listeners.contextPrepared(context) : 广播一个 ApplicationContextInitializedEvent 出去
注册一个springApplicationArguments到 beanFactory中去. printedBanner 不是null的话,也注册到beanFactory中去. 如果beanFactory是DefaultListableBeanFactory的话,就this.allowBeanDefinitionOverriding赋值为false
如果是有lazyInitialization,就会添加一个LazyInitializationBeanFactoryPostProcessor到context中去.
getAllSources() 这里获取出来的就是只有我们的启动类,对获取出来的集合进行非空判断.
最后广播一个 ApplicationPreparedEvent 出去. 广播出去之前会有对listener都添加到 context中,如果是ApplicationContextAware的话,就会再调用一个 setApplicationContext 方法.
 

run.refreshContext() 方法

最后是走到了 org.springframework.context.support.AbstractApplicationContext#refresh 这里,也就是走到refresh方法.

run -> listeners.started(context) 方法

这里可以看到是广播了一个 ApplicationStartedEvent 事件出去.
于是后面又广播了一个 AvailabilityChangeEvent 事件出去.

run.callRunners() 方法

该方法可以看到是, 分别获取出 ApplicationRunner 和 CommandLineRunner, 然后调用callRunner方法,该方法其内部也是去调用的run方法.
 

run -> listeners.running(context) 方法

可以看到 running是分别广播了二个事件出去. 然后相应的监听器分别获取到这个event,并且进行相应的逻辑处理.
走完该方法,如果是没有出现任何异常的话,就会返回一个context,所以该run方法也就是走到这里就算走完了.
 

🤗 总结归纳

📎 参考文章

 
💡
有关文章的问题,欢迎您在底部评论区留言,一起交流~