• SpringBoot的启动流程分析(2)


    我们来分析SpringApplication启动流程中的run()方法,代码如下

        public ConfigurableApplicationContext run(String... args) {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            FailureAnalyzers analyzers = null;
            configureHeadlessProperty();
            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.starting();
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                        args);
                ConfigurableEnvironment environment = prepareEnvironment(listeners,
                        applicationArguments);
                Banner printedBanner = printBanner(environment);
                context = createApplicationContext();
                analyzers = new FailureAnalyzers(context);
                prepareContext(context, environment, listeners, applicationArguments,
                        printedBanner);
                refreshContext(context);
                afterRefresh(context, applicationArguments);
                listeners.finished(context, null);
                stopWatch.stop();
                if (this.logStartupInfo) {
                    new StartupInfoLogger(this.mainApplicationClass)
                            .logStarted(getApplicationLog(), stopWatch);
                }
                return context;
            }
            catch (Throwable ex) {
                handleRunFailure(context, listeners, analyzers, ex);
                throw new IllegalStateException(ex);
            }
        }

    看起来方法比较长,不过我们主要看启动流程,哪些监控,失败分析的我们以后再说,所以这个方法中关键的几步如下

    1 context = createApplicationContext();
    2 prepareContext(context,environment,listeners,applicationArguments,printedBanner);
    3 refreshContext(context);
    4 afterRefresh(context, applicationArguments);

    没错,看到关键的方法就是这4个,我们首先分析 createApplicationContext()方法,代码如下

      public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
                + "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
        public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
                + "annotation.AnnotationConfigApplicationContext";
    
    
        protected ConfigurableApplicationContext createApplicationContext() {
            Class<?> contextClass = this.applicationContextClass;
            if (contextClass == null) {
                try {
                    contextClass = Class.forName(this.webEnvironment
                            ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
                }
                catch (ClassNotFoundException ex) {
                    throw new IllegalStateException(
                            "Unable create a default ApplicationContext, "
                                    + "please specify an ApplicationContextClass",
                            ex);
                }
            }
            return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
        }

    很明显,在web环境下它加载并创建了 ConfigurableApplicationContext,简单点说就是创建了ApplicationContext,即创建了Bean的容器(BeanFactory)

    然后看 prepareContext()方法,可以看出,这个方法是将配置,参数,监听器,以及banner信息注入applicationContext中

        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
            context.getBeanFactory().registerSingleton("springApplicationArguments",
                    applicationArguments);
            if (printedBanner != null) {
                context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
            }
    
            // Load the sources
            Set<Object> sources = getSources();
            Assert.notEmpty(sources, "Sources must not be empty");
            load(context, sources.toArray(new Object[sources.size()]));
            listeners.contextLoaded(context);
        }

    方法3是启动的关键函数,在这一步中springboot已经启动,因为比较复杂,我们稍后说。所以我么先看 afterRefresh(context, applicationArguments),其细节如下

        protected void afterRefresh(ConfigurableApplicationContext context,
                ApplicationArguments args) {
            callRunners(context, args);
        }
    
        private void callRunners(ApplicationContext context, ApplicationArguments args) {
            List<Object> runners = new ArrayList<Object>();
            runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
            runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
            AnnotationAwareOrderComparator.sort(runners);
            for (Object runner : new LinkedHashSet<Object>(runners)) {
                if (runner instanceof ApplicationRunner) {
                    callRunner((ApplicationRunner) runner, args);
                }
                if (runner instanceof CommandLineRunner) {
                    callRunner((CommandLineRunner) runner, args);
                }
            }
        }    
    
      private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
            try {
                (runner).run(args.getSourceArgs());
            }
            catch (Exception ex) {
                throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
            }
        }

    看到这里大家想起来什么了没有?没错,我们自己现实的 ApplicationRunner 或 CommandLineRunner 这些类就是在这里被调用执行的。

    最后我们来看最重要的 refreshContext(context),其相关的代码如下

        private void refreshContext(ConfigurableApplicationContext context) {
            refresh(context);
            if (this.registerShutdownHook) {
                try {
                    context.registerShutdownHook();
                }
                catch (AccessControlException ex) {
                    // Not allowed in some environments.
                }
            }
        }
    
        protected void refresh(ApplicationContext applicationContext) {
            Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
            ((AbstractApplicationContext) applicationContext).refresh();
        }
    
    
        public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
                // Prepare this context for refreshing.
                prepareRefresh();
    
                // Tell the subclass to refresh the internal bean factory.
                ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
                // Prepare the bean factory for use in this context.
                prepareBeanFactory(beanFactory);
    
                try {
                    // Allows post-processing of the bean factory in context subclasses.
                    postProcessBeanFactory(beanFactory);
    
                    // Invoke factory processors registered as beans in the context.
                    invokeBeanFactoryPostProcessors(beanFactory);
    
                    // Register bean processors that intercept bean creation.
                    registerBeanPostProcessors(beanFactory);
    
                    // Initialize message source for this context.
                    initMessageSource();
    
                    // Initialize event multicaster for this context.
                    initApplicationEventMulticaster();
    
                    // Initialize other special beans in specific context subclasses.
                    onRefresh();
    
                    // Check for listener beans and register them.
                    registerListeners();
    
                    // Instantiate all remaining (non-lazy-init) singletons.
                    finishBeanFactoryInitialization(beanFactory);
    
                    // Last step: publish corresponding event.
                    finishRefresh();
                }
    
                catch (BeansException ex) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Exception encountered during context initialization - " +
                                "cancelling refresh attempt: " + ex);
                    }
    
                    // Destroy already created singletons to avoid dangling resources.
                    destroyBeans();
    
                    // Reset 'active' flag.
                    cancelRefresh(ex);
    
                    // Propagate exception to caller.
                    throw ex;
                }
    
                finally {
                    // Reset common introspection caches in Spring's core, since we
                    // might not ever need metadata for singleton beans anymore...
                    resetCommonCaches();
                }
            }
        }

    最后落在了AbstractApplicationContext.refresh方法中。

    这个方法的每一步上都有明确的注释。

    prepareRefresh()准备刷新,设置应用的开启时间,设置active标志,并执行一些属性的初始化工作。

    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();获取BeanFactory。

    prepareBeanFactory(beanFactory)执行一些BeanFactory的初始化工作。

    postProcessBeanFactory(beanFactory)设置BeanFactory的后置处理器。(可以参考Spring中Bean的生命周期)

    invokeBeanFactoryPostProcessors(beanFactory)调用BeanFactory的后置处理器。

    registerBeanPostProcessors(beanFactory)在容器内的所有Bean实例化之前注册Bean的前(后)置处理器。(可以参考BeanPostProcessor)

    initMessageSource()设置messgeSource

    initApplicationEventMulticaster()初始化Context的广播器

    onRefresh()根据子类的实现方式不同做不同的事。EmbeddedWebApplicationContext会创建内置的Tomcat并启动。

    registerListeners()创建监听器

    finishBeanFactoryInitialization(beanFactory)实例化BeanFactory中非懒加载的单例的Bean。这个方法比较关键。

    finishRefresh()初始化或启动声明周期相关的任务,发布Context已经刷新的消息。

    以上是springboot启动的粗略流程,让我们大致明白启动的流程是什么样的。各个步骤解释的不是特别详细,也有可能还有错误。

    以后可能会逐步的进行详细的分析。

    引出的问题

    BeanFactory是什么。

    Tomcat中和BeanFactory中生命周期的问题。

    Spring中的事件监听机制。

    Tomcat和Servlet和Springboot的关联。

  • 相关阅读:
    通达OA 新旧两种数据库连接方式
    c++ 如何获取系统时间
    性能测试开源小工具——http_load介绍
    http_load安装与测试参数分析
    不错的C++框架: Thrift(2)-传输和网络相关
    管理处理器的亲和性(affinity)
    300元内,此耳机是首选。不亏千人好评,对的起你的耳朵。
    [品质生活] 舒适 Schick HYDRO 5剃须刀
    巴氏刷牙法_百度百科
    Amazon.com : The Odyssey of the Manual Toothbrusher
  • 原文地址:https://www.cnblogs.com/wanghaoyang/p/10913571.html
Copyright © 2020-2023  润新知