美文网首页
springboot 启动分析六

springboot 启动分析六

作者: 0爱上1 | 来源:发表于2018-10-21 12:26 被阅读8次

    承接启动分析五,本文继续探索springboot的启动之旅

    该文主要分析上下文刷新完成之后做了哪些工作

    public ConfigurableApplicationContext run(String... args) {
    
        // 声明并实例化一个跑表,用于计算springboot应用程序启动时间
        StopWatch stopWatch = new StopWatch();
    
        // 启动跑表
        stopWatch.start();
    
        // 声明一个应用程序上下文,注意这里是一个接口声明
        ConfigurableApplicationContext context = null;
    
        // 声明一个集合,用于记录springboot异常报告
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    
        // 配置不在意的属性 java.awt.headless
        configureHeadlessProperty();
    
        // 获取用于监听spring应用程序run方法的监听器实例
        SpringApplicationRunListeners listeners = getRunListeners(args);
    
        // 循环启动用于run方法的监听器
        listeners.starting();
        try {
            // 封装应用参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
    
            // 根据SpringApplication实例化时候推断出来的应用类型 webApplicationType,
            // 去获取不同的环境,然后获取配置要适用的PropertySource以及激活哪个Profile
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
    
            // 根据环境配置需要忽略的bean的信息
            configureIgnoreBeanInfo(environment);
    
            // 根据环境配置打印banner,即根据bannerMode 枚举值,决定是否打印banner和banner打印的位置
            Banner printedBanner = printBanner(environment);
    
            // key 4 创建应用程序上下文,
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[]{ConfigurableApplicationContext.class}, context);
            // key 5 准备应用上下文
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            // key 6 刷新上下文      
            refreshContext(context);
            // key 7 处理刷新上下文之后的事情
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            // key 8 上下文启动完成发布事件
            listeners.started(context);
            // key 9
            callRunners(context, applicationArguments);
        } catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }
    
        try {
            // key 10
            listeners.running(context);
        } catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }
    

    key7 刷新之后 afterRefresh(context, applicationArguments)

    这里其实方法是空的,没有作任何的事

    protected void afterRefresh(ConfigurableApplicationContext context,
            ApplicationArguments args) {
    }
    

    key 8 上下文刷新完成,应用程序已经启动,事件发布

    但此时 CommandLineRunners和ApplicationRunners还没有被回调

    public void started(ConfigurableApplicationContext context) {
        context.publishEvent(
                new ApplicationStartedEvent(this.application, this.args, context));
    }
    

    跟踪代码看到最后发布事件之前只有两个Listener感兴趣ApplicationStartedEvent事件

    感兴趣ApplicationStartedEvent事件的监听器

    BackgroundPreinitializer

    在耗时任务的后台线程中触发早期初始化

    public void onApplicationEvent(SpringApplicationEvent event) {
        if (event instanceof ApplicationStartingEvent
                && preinitializationStarted.compareAndSet(false, true)) {
            performPreinitialization();
        }
        if ((event instanceof ApplicationReadyEvent
                || event instanceof ApplicationFailedEvent)
                && preinitializationStarted.get()) {
            try {
                preinitializationComplete.await();
            }
            catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
    }
    

    可以看到这里两个if都没有进入

    DelegatingApplicationListener

    委托给其他监听器处理事件

    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
                    ((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
            if (delegates.isEmpty()) {
                return;
            }
            this.multicaster = new SimpleApplicationEventMulticaster();
            for (ApplicationListener<ApplicationEvent> listener : delegates) {
                this.multicaster.addApplicationListener(listener);
            }
        }
        if (this.multicaster != null) {
            this.multicaster.multicastEvent(event);
        }
    }
    

    这里也没有作什么事情,继续往下看

    key 9 callRunners

    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<>();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        for (Object runner : new LinkedHashSet<>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }
    
    1. 从上下文中获取ApplicationRunner类型的bean,以及CommandLineRunner类型的bean

    2. 排序

    3. 依次调用其run方法,并传入相应的参数

       private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
       try {
           (runner).run(args);
       }
       catch (Exception ex) {
           throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
       }
       }
      
       private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
       try {
           (runner).run(args.getSourceArgs());
       }
       catch (Exception ex) {
           throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
       }
       }
      

    至此,整个callRunners方法就分析完毕

    key 10 listeners.running(context)

    发布上下文ApplicationReadyEvent事件

    public void running(ConfigurableApplicationContext context) {
        context.publishEvent(
                new ApplicationReadyEvent(this.application, this.args, context));
    }
    

    这里可以看到有三个监听器感兴趣该事件

    ApplicationReadyEvent

    前两个事件分析过了,重点看一下第三个事件

    ApplicationListenerMethodAdapter

    public void onApplicationEvent(ApplicationEvent event) {
        this.processEvent(event);
    }
    
    public void processEvent(ApplicationEvent event) {
        Object[] args = this.resolveArguments(event);
        if (this.shouldHandle(event, args)) {
            Object result = this.doInvoke(args);
            if (result != null) {
                this.handleResult(result);
            } else {
                this.logger.trace("No result object given - no result to handle");
            }
        }
    
    }
    

    总结

    通过前面几篇的springboot 启动分析,至此已经完成了整个启动过程

    相关文章

      网友评论

          本文标题:springboot 启动分析六

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