执行run()方法

上面分析了SpringApplication的构建过程,一切都做好了铺垫,现在到了启动的过程了。

作者根据源码将启动过程分为了「8步」,下面将会一一介绍。

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

SpringApplicationRunListener这个监听器和ApplicationListener不同,它是用来监听应用程序启动过程的,接口的各个方法含义如下:

public?interface?SpringApplicationRunListener?{

????//?在run()方法开始执行时,该方法就立即被调用,可用于在初始化最早期时做一些工作
????void?starting();
????//?当environment构建完成,ApplicationContext创建之前,该方法被调用
????void?environmentPrepared(ConfigurableEnvironment?environment);
????//?当ApplicationContext构建完成时,该方法被调用
????void?contextPrepared(ConfigurableApplicationContext?context);
????//?在ApplicationContext完成加载,但没有被刷新前,该方法被调用
????void?contextLoaded(ConfigurableApplicationContext?context);
????//?在ApplicationContext刷新并启动后,CommandLineRunners和ApplicationRunner未被调用前,该方法被调用
????void?started(ConfigurableApplicationContext?context);
????//?在run()方法执行完成前该方法被调用
????void?running(ConfigurableApplicationContext?context);
????//?当应用运行出错时该方法被调用
????void?failed(ConfigurableApplicationContext?context,?Throwable?exception);
}

如何获取运行监听器?

SpringApplication#run()方法中,源码如下:

//从spring.factories中获取监听器
SpringApplicationRunListeners?listeners?=?getRunListeners(args);

跟进getRunListeners()方法,其实还是调用了loadFactoryNames()方法从spring.factories文件中获取值,如下:

org.springframework.boot.SpringApplicationRunListener=/
org.springframework.boot.context.event.EventPublishingRunListener

最终注入的是EventPublishingRunListener这个实现类,创建实例过程肯定是通过反射了,因此我们看看它的构造方法,如下图:

image

这个运行监听器内部有一个事件广播器(SimpleApplicationEventMulticaster),主要用来广播特定的事件(SpringApplicationEvent)来触发特定的监听器ApplicationListener

EventPublishingRunListener中的每个方法用来触发SpringApplicationEvent中的不同子类。

如何启动运行监听器?

SpringApplication#run()方法中,源码如下:

//执行starting()方法
listeners.starting(bootstrapContext,?this.mainApplicationClass);

执行SpringApplicationRunListenersstarting()方法,跟进去其实很简单,遍历执行上面获取的运行监听器,这里只有一个EventPublishingRunListener。因此执行的是它的starting()方法,源码如下图:

image

上述源码中逻辑很简单,其实只是执行了multicastEvent()方法,广播了ApplicationStartingEvent事件。至于multicastEvent()内部方法感兴趣的可以看看,其实就是遍历ApplicationListener的实现类,找到监听ApplicationStartingEvent这个事件的监听器,执行onApplicationEvent()方法。

总结

这一步其实就是广播了ApplicationStartingEvent事件来触发监听这个事件的ApplicationListener

因此如果自定义了ApplicationListener并且监听了ApplicationStartingEvent(应用程序开始启动)事件,则这个监听器将会被触发。

2. 环境构建

这一步主要用于加载系统配置以及用户的自定义配置(application.properties),源码如下,在run()方法中:

ConfigurableEnvironment?environment?=?prepareEnvironment(listeners,?bootstrapContext,?applicationArguments);

prepareEnvironment方法内部广播了ApplicationEnvironmentPreparedEvent事件,源码如下图:

image

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

3. 创建IOC容器

源码在run()方法中,如下:

context?=?createApplicationContext();

跟进代码,真正执行的是ApplicationContextFactory方法,如下图:

image

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

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

4. IOC容器的前置处理

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

prepareContext(context,?environment,?listeners,?applicationArguments,printedBanner);

prepareContext()源码解析如下图,内容还是挺多的:

image

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

调用初始化器

SpringApplication构建过程中设置的初始化器,从spring.factories取值的。执行的流程很简单,遍历执行,源码如下图:

image

将自定义的ApplicationContextInitializer放在META-INF/spring.factories中,在此时也是会被调用。

加载启动类,注入容器

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

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

image

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

load(context,?sources.toArray(new?Object[0]));

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

image

将主启动类加载到beanDefinitionMap,后续该启动类将作为开启自动配置化的入口,后续章节详细介绍。

两次广播事件

这一步涉及到了两次事件广播,分别是ApplicationContextInitializedEventApplicationPreparedEvent,对应的源码如下:

listeners.contextPrepared(context);
load(context,?sources.toArray(new?Object[0]));

5. 刷新容器

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

protected?void?refresh(ApplicationContext?applicationContext)?{
????Assert.isInstanceOf(AbstractApplicationContext.class,?applicationContext);
????//调用创建的容器applicationContext中的refresh()方法
????((AbstractApplicationContext)applicationContext).refresh();
}
public?void?refresh()?throws?BeansException,?IllegalStateException?{
????synchronized?(this.startupShutdownMonitor)?{
????????/**
?????????*?刷新上下文环境
?????????*/
????????prepareRefresh();

????????/**
?????????*?初始化BeanFactory,解析XML,相当于之前的XmlBeanFactory的操作,
?????????*/
????????ConfigurableListableBeanFactory?beanFactory?=?obtainFreshBeanFactory();

????????/**
?????????*?为上下文准备BeanFactory,即对BeanFactory的各种功能进行填充,如常用的注解@Autowired?@Qualifier等
?????????*?添加ApplicationContextAwareProcessor处理器
?????????*?在依赖注入忽略实现*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等
?????????*?注册依赖,如一个bean的属性中含有ApplicationEventPublisher(beanFactory),则会将beanFactory的实例注入进去
?????????*/
????????prepareBeanFactory(beanFactory);

????????try?{
????????????/**
?????????????*?提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess
?????????????*/
????????????postProcessBeanFactory(beanFactory);

????????????/**
?????????????*?激活各种BeanFactory处理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
?????????????*?执行对应的postProcessBeanDefinitionRegistry方法?和??postProcessBeanFactory方法
?????????????*/
????????????invokeBeanFactoryPostProcessors(beanFactory);

????????????/**
?????????????*?注册拦截Bean创建的Bean处理器,即注册BeanPostProcessor,不是BeanFactoryPostProcessor,注意两者的区别
?????????????*?注意,这里仅仅是注册,并不会执行对应的方法,将在bean的实例化时执行对应的方法
?????????????*/
????????????registerBeanPostProcessors(beanFactory);

????????????/**
?????????????*?初始化上下文中的资源文件,如国际化文件的处理等
?????????????*/
????????????initMessageSource();

????????????/**
?????????????*?初始化上下文事件广播器,并放入applicatioEventMulticaster,如ApplicationEventPublisher
?????????????*/
????????????initApplicationEventMulticaster();

????????????/**
?????????????*?给子类扩展初始化其他Bean
?????????????*/
????????????onRefresh();

????????????/**
?????????????*?在所有bean中查找listener?bean,然后注册到广播器中
?????????????*/
????????????registerListeners();

????????????/**
?????????????*?设置转换器
?????????????*?注册一个默认的属性值解析器
?????????????*?冻结所有的bean定义,说明注册的bean定义将不能被修改或进一步的处理
?????????????*?初始化剩余的非惰性的bean,即初始化非延迟加载的bean
?????????????*/
????????????finishBeanFactoryInitialization(beanFactory);

????????????/**
?????????????*?通过spring的事件发布机制发布ContextRefreshedEvent事件,以保证对应的监听器做进一步的处理
?????????????*?即对那种在spring启动后需要处理的一些类,这些类实现了ApplicationListener<ContextRefreshedEvent>,
?????????????*?这里就是要触发这些类的执行(执行onApplicationEvent方法)
?????????????*?另外,spring的内置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
?????????????*?完成初始化,通知生命周期处理器lifeCycleProcessor刷新过程,同时发出ContextRefreshEvent通知其他人
?????????????*/
????????????finishRefresh();
????????}

????????finally?{

????????????resetCommonCaches();
????????}
????}
}

6. IOC容器的后置处理

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

afterRefresh(context,?applicationArguments);

最后

为什么我不完全主张自学?
平台上的大牛基本上都有很多年的工作经验了,你有没有想过之前行业的门槛是什么样的,现在行业门槛是什么样的?以前企业对于程序员能力要求没有这么高,甚至十多年前你只要会写个“Hello World”,你都可以入门这个行业,所以以前要入门是完全可以入门的。
②现在也有一些优秀的年轻大牛,他们或许也是自学成才,但是他们一定是具备优秀的学习能力,优秀的自我管理能力(时间管理,静心坚持等方面)以及善于发现问题并总结问题。
如果说你认为你的目标十分明确,能做到第②点所说的几个点,以目前的市场来看,你才真正的适合去自学。

除此之外,对于绝大部分人来说,报班一定是最好的一种快速成长的方式。但是有个问题,现在市场上的培训机构质量参差不齐,如果你没有找准一个好的培训班,完全是浪费精力,时间以及金钱,这个需要自己去甄别选择。

我个人建议线上比线下的性价比更高,线下培训价格基本上没2W是下不来的,线上教育现在比较成熟了,此次疫情期间,学生基本上都感受过线上的学习模式。相比线下而言,线上的优势以我的了解主要是以下几个方面:
①价格:线上的价格基本上是线下的一半;
②老师:相对而言线上教育的师资力量比线下更强大也更加丰富,资源更好协调;
③时间:学习时间相对而言更自由,不用裸辞学习,适合边学边工作,降低生活压力;
④课程:从课程内容来说,确实要比线下讲的更加深入。

应该学哪些技术才能达到企业的要求?(下图总结)

Java全套资料免费领取方式:戳这里

头秃了,二十三张图带你从源码了解SpringBoot启动流程,Java面试问题

头秃了,二十三张图带你从源码了解SpringBoot启动流程,Java面试问题