美文网首页
springboot启动源码分析

springboot启动源码分析

作者: loveFXX | 来源:发表于2020-01-13 20:27 被阅读0次

    springboot启动通过main方法

    示例代码

    启动类CmsNewApplication.class

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

    debug流程

    SpringApplication#run(Class<?>, String...)
    把CmsNewApplication.class传入到变量primarySource


    image.png

    SpringApplication#run(Class<?>[], String[])


    image.png
    首先通过new SpringApplication创建类,传入的参数是CmsNewApplication类。然后再调用run方法

    创建SpringApplication类

    创建SpringApplication类,主要是对resourceLoader、primarySources、webApplicationType、mainApplicationClass变量进行赋值。并添加初始化类和监听器

    变量赋值

    SpringApplication#SpringApplication(java.lang.Class<?>...)
    SpringApplication类的构造方法


    image.png

    SpringApplication#SpringApplication(ResourceLoader, Class<?>...)


    image.png
    ①、resourceLoader=null,
    ②、primarySources等于传入的CmsNewApplication类。
    ③、webApplicationType

    调用deduceFromClasspath()方法对webApplicationType赋值
    WebApplicationType#deduceFromClasspath


    image.png
    执行逻辑:首先判断是否含有reactive的DispatcherHandler类并且不含有servlet的DispatcherServlet并且不含有jersey的ServletContainer,如果成立则会返回REACTIVE常量。
    对javax.servlet.Servlet和ConfigurableWebApplicationContext进行遍历,如果不包含任意一个则返回NONE。否则都含有则返回WebApplicationType.SERVLET类型。并对webApplicationType赋值等于SERVLET
    ④、mainApplicationClass
    mainApplicationClass通过deduceMainApplicationClass()方法获取
    image.png
    主要通过得到调用链查找main方法得到所在类(就是得到debug调用链)
    添加初始化类和监听器
    image.png
    image.png

    与启动有关的类是DelegatingApplicationContextInitializer和RestartApplicationListener

    调用SpringApplication的run方法

    创建SpringApplication(spring应用类)完成,继续调用run方法

    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;
        }
    

    1、StopWatch 简单秒表类,通过start()和stop()方法进行计时
    2、创建ConfigurableApplicationContext类并赋值为null,并创建异常SpringBootExceptionReporter类的集合,并设置Headless配置
    3、获取SpringApplicationRunListeners类,这个是spring应用运行监听器类,在spring容器中获取。这里获取只有一个EventPublishingRunListener


    image.png

    这个类的结构是


    image.png
    当创建这个类时,会创建默认的initialMulticaster类SimpleApplicationEventMulticaster,并把所有监听器加入到事件广播器属性defaultRetriever之中
    4、监听器EventPublishingRunListener类调用starting()开始运行
    SpringApplicationRunListeners#starting类实际调用到实现类EventPublishingRunListener#starting方法
    image.png

    调用事件广播器发布ApplicationStartingEvent(应用开始)事件
    SimpleApplicationEventMulticaster#multicastEvent(ApplicationEvent)


    image.png
    SimpleApplicationEventMulticaster#multicastEvent(ApplicationEvent, ResolvableType)
    image.png
    这个方法获取事件类型,是否有多线程执行器Executor,如果包含则后台执行
    如果想要添加只需要@Bean("applicationEventMulticaster"),必须是这个beanName
    @Bean("applicationEventMulticaster")
        public SimpleApplicationEventMulticaster simpleApplicationEventMulticaster(BeanFactory beanFactory,ThreadPoolTaskExecutor poolTaskExecutor){
            SimpleApplicationEventMulticaster simpleApplicationEventMulticaster
                    = new SimpleApplicationEventMulticaster(beanFactory);
            simpleApplicationEventMulticaster.setTaskExecutor(poolTaskExecutor);
            return simpleApplicationEventMulticaster;
        }
        @Bean
        public ThreadPoolTaskExecutor poolTaskExecutor(){
            ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
            threadPoolTaskExecutor.setMaxPoolSize(15);
            threadPoolTaskExecutor.setCorePoolSize(10);
            threadPoolTaskExecutor.setQueueCapacity(30);
            threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
            threadPoolTaskExecutor.initialize();
            return threadPoolTaskExecutor;
        }
    

    SimpleApplicationEventMulticaster#invokeListener


    image.png

    SimpleApplicationEventMulticaster#doInvokeListener


    image.png
    RestartApplicationListener#onApplicationEvent
    image.png

    5、初始化applicationArguments应用参数及ConfigurableEnvironment配置环境,配置忽略的bean信息。输出Banner图《Spring》


    image.png
    6、创建应用上下文createApplicationContext()
    image.png
    根据webApplicationType变量值,创建AnnotationConfigServletWebServerApplicationContext类,并封装成ConfigurableApplicationContext类返回并赋值给context
    如果是REACTIVE则创建AnnotationConfigReactiveWebServerApplicationContext类
    默认创建AnnotationConfigApplicationContext类
    7、获取SpringBootExceptionReporter异常类型exceptionReporters的集合
    image.png

    8、准备spring上下文环境prepareContext()

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
            context.setEnvironment(environment);
            postProcessApplicationContext(context);
            applyInitializers(context);
            listeners.contextPrepared(context);
            if (this.logStartupInfo) {
                logStartupInfo(context.getParent() == null);
                logStartupProfileInfo(context);
            }
            // Add boot specific singleton beans
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
            if (printedBanner != null) {
                beanFactory.registerSingleton("springBootBanner", printedBanner);
            }
            if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory) beanFactory)
                        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
            // Load the sources
            Set<Object> sources = getAllSources();
            Assert.notEmpty(sources, "Sources must not be empty");
            load(context, sources.toArray(new Object[0]));
            listeners.contextLoaded(context);
        }
    

    ①、首先为上下文设置环境environment
    ②、添加ConversionService

    image.png
    bean工厂设置添加ApplicationConversionService
    ③、调用ApplicationContextInitializer子类初始化一些类到spring容器
    ④、调用监听器的准备上下文环境contextPrepared()方法
    EventPublishingRunListener#contextPrepared发布ApplicationContextInitializedEvent应用环境初始化事件
    image.png
    这里调用三个监听器对环境没有任何变化
    DelegatingApplicationListener、BackgroundPreinitializer、RestartApplicationListener
    ⑤、 调用活跃的配置信息logStartupProfileInfo(context)
    ⑥、beanFactory的获取和注册其他单例类
    ⑦、获取primarySources资源文件(main所在的类)并加载
    image.png
    注册到AnnotatedBeanDefinitionReader
    image.png
    ⑧、监听器加载环境SpringApplicationRunListeners#contextLoaded
    把监听器加入到spring上下文环境中,并发布ApplicationPreparedEvent事件
    image.png
    RestartApplicationListener、ConfigFileApplicationListener为bean工厂环境添加类
    9、刷新上下文环境refreshContext(context)
    image.png
    SpringApplication#refresh
    image.png
    ServletWebServerApplicationContext#refresh
    image.png
    最终调用到AbstractApplicationContext#refresh
    与通过注解加载spring环境一样调用加载过程,执行invokeBeanFactoryPostProcessors()执行bean工厂后置处理器,注册bean后置处理器,注册监听器,实例化bean等过程
    image.png
    ①、在实例化initApplicationEventMulticaster方法中
    查看bean工厂是否包含beanName是applicationEventMulticaster的ApplicationEventMulticaster类的bean,
    解释了在添加为事件广播器添加多线程为什么@Bean需要beanName
    image.png
    ②、onRefresh()方法createWebServer 创建web服务
    具体调用AbstractApplicationContext#onRefresh的实现类
    image.png
    ServletWebServerApplicationContext#onRefresh
    image.png
    ServletWebServerApplicationContext#createWebServer
    image.png
    当webServer和servletContext都是null时,将会从bean工厂获取类型是ServletWebServerFactory的web服务工厂类
    ServletWebServerApplicationContext#getWebServerFactory
    image.png
    factory.getWebServer()方法首先获取web服务,然后再调用getSelfInitializer方法
    ①、获取web服务
    TomcatServletWebServerFactory#getWebServer
    image.png
    TomcatServletWebServerFactory#getTomcatWebServer
    image.png
    image.png
    TomcatWebServer#initialize
    image.png
    ②、getSelfInitializer调用
    ServletWebServerApplicationContext#getSelfInitializer
    image.png
    ServletWebServerApplicationContext#prepareWebApplicationContext
    主要是xx.ROOT属性及设置servletContext变量值
    image.png
    image.png
    registerApplicationScope注册servletContext到beanFactory
    image.png
    ServletContextInitializer调用onStartup方法
    image.png
    对DispatcherServletRegistrationBean和FilterRegistrationBean初始值设置
    调用到this.tomcat.start()之后,会调用getSelfInitializer()方法。启动tomcat
    启动之后继续调用TomcatWebServer#initialize方法内start之后的代码
    TomcatWebServer#startDaemonAwaitThread
    tomcat的设置非守护线程,进入线程后台await状态
    image.png
    如果servletContext已经有值则直接调用getSelfInitializer().onStartup(servletContext);初始化
    10、刷新之后afterRefresh(),空实现。之后停止计时
    11、调用callRunners方法
    image.png
    runSqlScript方法,执行数据库相关命令参数

    监听器的使用

    1、ApplicationStartingEvent事件
    listeners.starting()发布将要开始运行事件ApplicationStartingEvent
    有以下默认监听器:
    RestartApplicationListener、LoggingApplicationListener、BackgroundPreinitializer、
    DelegatingApplicationListener、LiquibaseServiceLocatorApplicationListener
    2、ApplicationStartedEvent事件
    listeners.started(context)发布开始结束事件ApplicationStartedEvent
    有以下默认监听器:
    DelegatingApplicationListener、BackgroundPreinitializer、RestartApplicationListener
    3、ApplicationReadyEvent事件
    listeners.running(context)发布正在运行事件ApplicationReadyEvent
    有以下默认监听器:没有实际效果
    RestartApplicationListener、SpringApplicationAdminMXBeanRegistrar、BackgroundPreinitializer、DelegatingApplicationListener、ConditionEvaluationDeltaLoggingListener

    onApplicationEvent

    对发布的事件调用对应的监听器的onApplicationEvent方法
    如:RestartApplicationListener#onApplicationEvent


    image.png

    总结:

    springboot启动流程关键就是在什么时机进行刷新spring上下文环境即是调用refresh方法
    在刷新过程中创建怎么样的ServletWebServerFactory环境
    getWebServer方法创建tomcat环境,并启动tomcat
    在启动过程中监听器设计模式的应用及监听器调用时机
    创建SpringApplication类对变量的赋值
    添加执行线程后台运行监听器方法

    相关文章

      网友评论

          本文标题:springboot启动源码分析

          本文链接:https://www.haomeiwen.com/subject/belgactx.html