SpringBoot启动过程解析(简化)

springBoot web方式启动过程
在这个启动过程中会有各种SpringBoot对外提供的扩展接口来对不同启动阶段进行自定义操作。
了解启动过程也是为了让我们更好理解SpringBoot提供的扩展接口使用
 
jar包启动或者外置war包启动都是调用SpringApplication.run()方法进行项目启动
tomcat会查询context上下文中实现ServletContainerInitializer接口的类,然后调用类的onStartup(Set<Class<?>> c, ServletContext ctx)方法
Spring的SpringServletContainerInitializer实现了这个ServletContainerInitializer接口,
会获取WebApplicationInitializer接口的实现类,调用onStartup()方法

SpringBoot的类SpringBootServletInitializer实现了Spring的WebApplicationInitializer扩展接口,
会在onStartup()方法中创建SpringApplication类,并调用SpringApplication.run()来完成启动项目
与我们在开发时调用Application.main()方法启动时一样的原理

 

开始分析

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        this.setRegisterErrorPageFilter(false); // 错误页面有容器来处理,而不是SpringBoot
        return builder.sources(Application.class);
    }

}

 

1、SpringApplication构造方法
初始化 ApplicationListener 和 ApplicationContextInitializer,还有设置应用类型:默认为WebApplicationType.SERVLET 即servlet容器
 
接着执行SpringApplication.run()方法
在这个方法里面总的概述就是对上下文和环境进行初始化配置,application上下文的BeanFactory管理bean定义和bean的初始化过程。。
public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
            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);
            }
            listeners.started(context);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

 

2 、触发ApplicationStartingEvent事件

SpringBoot对Spring的ApplicationListener进行了一个包装

在启动过程中使用SpringApplicationRunListener接口的实现类EventPublishingRunListener来触发各种事件

public interface SpringApplicationRunListener {
    starting ==> ApplicationStartingEvent
    environmentPrepared ==> ApplicationEnvironmentPreparedEvent
    contextPrepared ==> 无
    contextLoaded ==》 ApplicationPreparedEvent
    started ==》 ApplicationStartedEvent
    running ==》 ApplicationReadyEvent
    failed ==》 ApplicationFailedEvent
}

我们可以通过自定义SpringApplicationRunListener来在不同阶段操作,也可以通过配置ApplicationListener来完成不同阶段的操作。

这个开始启动事件触发操作:主要是进行日志系统初始化,字符集设置等操作

最开始触发的监听器:LoggingApplicationListener和BackgroundPreinitializer

LoggingApplicationListener日志系统实例生成,执行初始化前方法loggingSystem.beforeInitialize()
BackgroundPreinitializer 后台线程预初始化任务,用于耗时任务的初始化
1.默认类型转换器,format格式解析初始化
2.初始化校验器
3. 默认处理表单数据的消息转换器初始化的AllEncompassingFormHttpMessageConverter
4.初始化tomcat的MBeanFactory
5. 字符集初始化

 

3、获取命令行参数和准备上下文环境

prepareEnvironment(listeners, applicationArguments)

 初始化systemProperties,systemEnvironment属性源。获取系统属性设置到环境中
 
这个方法里触发ApplicationEnvironmentPreparedEvent环境准备完成事件监听器
主要是ConfigFileApplicationListener监听这个事件
会调用EnvironmentPostProcessor 环境后置处理器接口
还有这个类本身后置处理器会将配置文件加载到environment环境的属性源列表中

环境准备事件触发操作:配置文件属性源加载,日志系统配置环境属性设置

EnvironmentPostProcessor 这个接口扩展处理上下文环境操作

 DelegatingApplicationListener,这个委托代理监听器只能代理ApplicationEnvironmentPreparedEvent环境准备完成事件

 

4、初始化上下文信息

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

为context上下文设置reader和scanner,用于获取bean定义
调用ApplicationContextInitializer接口,对上下文进行初始化
将ApplicationArguments和SpringApplication作为bean注册到BeanFactory中
load(context, sources.toArray(new Object[0])); 这里是将应用启动类,即Application.java类,将其生成BeanDefinition即bean定义,绑定到beanFactory中
触发ApplicationPreparedEvent事件

ApplicationContextInitializer接口, 对上下文进行初始化操作

触发ApplicationPreparedEvent事件

网上资料:ApplicationPreparedEvent事件:在Bean定义加载之后、bean初始化之前,刷新上下文之前触发

自己调试:这个bean定义加载只是对Application.java启动类和内置的bean定义进行加载,我们项目中自定义的bean定义还是没有进行加载。

ApplicationPreparedEvent事件触发操作:
将ConfigFileApplicationListener # PropertySourceOrderingPostProcessor添加到ApplicationContext的BeanFactoryPostProcessors列表中,这个是BeanFactory的后置处理器
将Bean名称为:“springBootLoggingSystem”日志系统注册到beanfactory中
注意:在这里所有自定义的Bean定义都还没有开始加载,这个进行自定义获取bean是无效的
 
 
5、刷新上下文
refreshContext(context);
 这个是spring启动过程中最多逻辑的阶段
 
 5.1、 prepareRefresh()
设置系统启动标志为启动,以及属性校验等操作
 5.2、prepareBeanFactory(beanFactory)
配置beanfactory
设置bean类加载器,设置SpEL表达式解析器,注册配置属性编辑器,
初始化BeanPostProcessor(如ApplicationContextAwareProcessor),
设置忽略自动装配的接口,
注册默认环境bean(environment,systemProperties,systemEnvironment)

设置忽略自动装配的接口是Spring默认指定的Aware及其子接口

当类A中有属性是类B时,在初始化A的bean时,
如果类B是实现了Aware或其子接口时。不会为A自动注入类B
而是要通过B实现了Aware其子接口的接口方法来为A设置属性
一般来说:实现了Aware其子接口的实现类都是作为工具类,或者配置类

BeanPostProcessor接口:对bean初始化前后做一些操作逻辑,比如设置bean的属性,或对特殊的bean的方法进行调用等

5.3 postProcessBeanFactory(beanFactory)

context的子类在配置好BeanFactory后的扩展方法,

这里是AnnotationConfigServletWebServerApplicationContext类
添加了一个后置处理器,一个忽略自动注入接口
beanFactory.addBeanPostProcessor(WebApplicationContextServletContextAwareProcessor) beanFactory.ignoreDependencyInterface(ServletContextAware.
class)

 

5.4 invokeBeanFactoryPostProcessors(beanFactory)

 回调BeanFactoryPostProcessor处理器
在这里会进行bean定义的加载
会将ApplicationContext中设置的beanFactoryPostProcessors列表
转换成BeanDefinitionRegistryPostProcessor(registryProcessors 注册处理器) 和BeanFactoryPostProcessor(regularPostProcessors 规则处理器)

 内置beanFactory后置处理器列表

SharedMetadataReaderFactoryContextInitializer # CachingMetadataReaderFactoryPostProcessor
ConfigurationWarningsApplicationContextInitializer # ConfigurationWarningsPostProcessor
ConfigFileApplicationListener # PropertySourceOrderingPostProcessor

会先调用内置的注册处理器的postProcessBeanDefinitionRegistry(registry);方法
进行自定义bean的注册,或者对beanFactory中的bean定义的属性值进行修改

比如

SharedMetadataReaderFactoryContextInitializer # CachingMetadataReaderFactoryPostProcessor
会将SharedMetadataReaderFactoryContextInitializer.SharedMetadataReaderFactoryBean类创建bean定义注册到BeanFactory中,
还有修改ConfigurationAnnotationProcessor类的metadataReaderFactory属性值为SharedMetadataReaderFactoryBean
 
重点)执行完后置处理器的注册方法后,会获取到Beanfactory中绑定的BeanDefinitionRegistryPostProcessor
也就是ConfigurationClassPostProcessor,调用它的postProcessBeanDefinitionRegistry(registry)方法
这个类会将Spring注解标记的Bean定义解析绑定到BeanFactory中

也就是说只要有@Configuration、@Component、@ComponentScan、@Import、@ImportResource和@Bean中的其中一个注解,就会将bean注册到beanfactory中
@Import注解处理,如果导入的是ImportSelector接口实现类,会调用接口方法String[] selectImports()
通过返回字符串数组。里面是配置类名称。动态选择要导入的配置类全名

除了解析上面注解定义的bean外

还会处理@PropertySource注解,将指定的配置属性源设置到environment环境中

this.reader.loadBeanDefinitions(configClasses);这里会进行额外的bean定义加载:(被Import导入的类加载,方法定义的bean加载,资源文件中定义的bean)

 

后置处理器处理过程中会被调用三次

1. 第一次是执行ConfigurationClassPostProcessor后置处理器,获取项目中的bean定义
执行过了后置处理器不会再次执行
2. 第二次是在所有bean定义中获取有加@Order注解的BeanDefinitionRegistryPostProcessor实现类,执行后置处理器。
3. 第三次是再次获取没有执行过的处理器进行再次执行
比如mybatis的MapperScannerConfigurer就是在这里执行实现mybatis注解的bean定义加载
4. 最后是执行registryProcessors注册器和regularPostProcessors规则处理器的postProcessBeanFactory(beanFactory)方法
实现不同接口分类按从上到下执行
PriorityOrdered接口
PropertySourcesPlaceholderConfigurer

Ordered接口

没有实现上面接口的
org.springframework.boot.context.properties.ConfigurationBeanFactoryMetadata
preserveErrorControllerTargetClassPostProcessor
dataSourceInitializerSchedulerDependencyPostProcessor

 

5.5、registerBeanPostProcessors(beanFactory);

从beanFactory获取BeanPostProcessor分别按照PriorityOrdered,Ordered,普通的类型顺序注册BeanPostProcessor

 beanFactory.addBeanPostProcessor(postProcessor)

 

5.6、initMessageSource():一般是我们用来初始化我们国际化文件的

5.7、initApplicationEventMulticaster(); 初始化事件广播器

5.8、onRefresh():初始化其他的子容器类中的bean,同时创建spring的内置tomcat,

 ServletWebServerApplicationContext.onRefresh()  在这里创建createWebServer
        这里会调用tomcat的内置启动流程tomcat.start()
        StandardServer->StandardService->StandardEngine(servlet引擎)->StandardHost(可以理解为ip+端口号)->StandardContext(这里就是我们的项目)

5.9、registerListeners():

添加用户设置applicationListeners,然后从beanFactory获取ApplicationListener,
然后发布需要earlyApplicationEvents事件

是从beanFactory中获取所有实现ApplicationListener接口的类,
getBeanNamesForType(ApplicationListener.class, true, false)
第一个type参数表示类型,第二个参数是否包含非单例的bean
第三个参数是否允许提前初始化, true表示允许。会导致bean提前初始化,
false,表示不允许提前初始化,如果实在spring启动过程中,这个参数需要设置为false

 

6.0、finishBeanFactoryInitialization(beanFactory);

缓存所有bean定义元数据 bean definition metadata
beanFactory.preInstantiateSingletons();
实例化所有剩余的(非延迟初始化)单例。
在这个过程中会调用beanPostProcessors,bean实例化前后的处理器
调用AbstractAutowireCapableBeanFactory的 initializeBean(beanName, exposedObject, mbd);方法

在这个方法里会处理实现Aware接口的Bean类
invokeAwareMethods(beanName, bean);方法会处理实现了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware接口实现了类的接口方法

接着调用applyBeanPostProcessorsBeforeInitialization 处理BeanPostProcessor bean调用初始化之前的处理
接着调用bean的初始化方法invokeInitMethods(beanName, wrappedBean, mbd);
接着调用bean的初始化方法之后的后置处理器

 

ApplicationContextAwareProcessor:

处理实现EnvironmentAware,EmbeddedValueResolverAware,ResourceLoaderAware,ApplicationEventPublisherAware,MessageSourceAware,ApplicationContextAware这些接口的实现bean类在
在初始化回调之前调用对应接口方法做对应的操作。
比如initializingBean的afterPropertiesSet()方法之前或@Bean注解设置的init-method之前调用

WebApplicationContextServletContextAwareProcessor

处理实现了ServletContextAware接口的bean,可以通过setServletContext(ServletContext)方法获取servletContext

 

6.1、finishRefresh();

完成上下文刷新,
清除上下文级别的resources缓存(MetadataReader封装的资源,比如扫描的配置类)
初始化lifecycleProcessor,用来管理spring生命周期的onRefresh作用是容器启动成功,onClose是只应用要关闭的时候

然后触发ContextRefreshedEvent事件

refreshContext(context);执行完成
到这里spring项目容器已经启动成功

 

7、afterRefresh(context, applicationArguments);

这里没有操作

8、触发ApplicationStartedEvent事件

9、callRunners(context, applicationArguments)

从beanfactory中获取ApplicationRunner和CommandLineRunner接口实现类
调用他们的run方法

10、触发ApplicationReadyEvent事件

启动完成

 

posted @ 2021-02-26 16:00  海绵般汲取  阅读(1013)  评论(0编辑  收藏  举报