• 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的关联。

  • 相关阅读:
    Java异常处理和设计
    一次qps测试实践
    Alternate Task UVA
    Just Another Problem UVA
    Lattice Point or Not UVA
    Play with Floor and Ceil UVA
    Exploring Pyramids UVALive
    Cheerleaders UVA
    Triangle Counting UVA
    Square Numbers UVA
  • 原文地址:https://www.cnblogs.com/wanghaoyang/p/10913571.html
Copyright © 2020-2023  润新知