zoukankan      html  css  js  c++  java
  • 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事件

    启动完成

    作者:海绵般汲取
    出处:https://www.cnblogs.com/gne-hwz/
    版权:本文版权归作者和博客园共有
    转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任
  • 相关阅读:
    Emacs教程
    华为上机测试 2015
    奇偶排序
    C语言中的EOF和回车不一样
    jquery 使用方法
    1116
    1115
    1109
    Oracle14~23
    get与post的区别
  • 原文地址:https://www.cnblogs.com/gne-hwz/p/14451320.html
Copyright © 2011-2022 走看看