# SpringBoot 启动流程

本篇文章研究的SpringBoot版本为2.3.3-RELEASE,主要记录一下Web类型SpringBoot项目的启动流程。

一般的springboot的启动类长这个样子。

@SpringBootApplication
public class SampleApplication {

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

可以看到这个main方法里面只有简单的一行code,这一行就是理解springboot启动过程的入口了。其中run方法的参数有两个,一个是Class类型,用于定位basePackage,一个是String[]类型,单纯的对运行参数进行转发。点进去看一看它做了什么事情,大致上可以认为分为两个部分,首先是new了一个SpringApplication对象,然后调用了它的run方法。

    /**
     * Static helper that can be used to run a {@link SpringApplication} from the
     * specified source using default settings.
     * @param primarySource the primary source to load
     * @param args the application arguments (usually passed from a Java main method)
     * @return the running {@link ApplicationContext}
     */
    public static ConfigurableApplicationContext run(Class primarySource, String... args) {
        return run(new Class[] { primarySource }, args);
    }

    /**
     * Static helper that can be used to run a {@link SpringApplication} from the
     * specified sources using default settings and user supplied arguments.
     * @param primarySources the primary sources to load
     * @param args the application arguments (usually passed from a Java main method)
     * @return the running {@link ApplicationContext}
     */
    public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
    }

new SpringApplication 阶段做了哪些事情

结合code我们看一下在new SpringApplication时,SpringBoot框架做了哪些事情

    public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
//设置primarySources
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//确定application类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
//读取配置在spring.factories文件中的ApplicationContextInitializer,需要注意一下,在这个方法里面,springboot会开始读取spring.factories里面的配置并且根据ClassLoader的不同,缓存到内存中。这个之后再展开讲。
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//读取配置在spring.factories文件中的ApplicationListener。
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//确定入口类
        this.mainApplicationClass = deduceMainApplicationClass();
    }

首先就是配置了primarySources这个属性

这个属性主要用于之后确定扫描的basePackage。具体的原理我们可以之后再讲。

其次,就是确定一下我们这个项目的类型

判断方法比较简单粗暴,就是看一下你项目中有没有对应类型的标志性的类。举个例子,如果你有WEBFLUX_INDICATOR_CLASS这个类的话,就认为你的项目类型是REACTIVE。

    private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";

    private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";

    private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";

    private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";

    private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";

    static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
        for (String className : SERVLET_INDICATOR_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        return WebApplicationType.SERVLET;
    }

然后就是通过SPI机制获取spring.factories的配置

在构造器中,我们可以很明显的看到有两次对于getSpringFactoriesInstances方法的调用,这个方法兜兜转转,最后会调用到SpringFactoriesLoader的loadSpringFactories的方法。在这个方法中,对于相同的classsloader,只会尝试load一次spring.factories文件中的配置。因此,一般情况下,这个阶段已经完成了对spring.factories的扫描和加载。

    private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
            Enumeration urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

从load好的spring.factorie中获取配置好的ApplicationContextInitializer和ApplicationListener

这个就没啥好说了。需要注意的是,这些ApplicationListener是在调用run方法之前就已经加载进来的,所以这些ApplicationListener不仅仅能够接收ApplicationStartedEvent以及之后的ApplicationEvent,之前的比如ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent等等事件类型也都可以接收到。

确定主类

这个的逻辑也比较简单,首先获取当前的方法调用栈(这点还是有疑问为啥不通过Thread.currentThread().getStackTrace()这种看起来更加正式的方法来获取stackTrace)。然后从栈顶一个一个找下去,直到找到一个名字为main的方法,这个方法所对应的类就可以认为是主类了。

   private Class deduceMainApplicationClass() {
       try {
           StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
           for (StackTraceElement stackTraceElement : stackTrace) {
               if ("main".equals(stackTraceElement.getMethodName())) {
                   return Class.forName(stackTraceElement.getClassName());
               }
           }
       }
       catch (ClassNotFoundException ex) {
           // Swallow and continue
       }
       return null;
   }

为了验证我也写个了sample

public class Test {
    public static void main(String[] args) {
        SampleApplication.main(args);
    }
}

@SpringBootApplication
public class SampleApplication {

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

发现调用Test.main方法之后,定位出来的mainclass确实是SampleApplication。

run()方法做了哪些事情

还是先看这些稍微加了点注释的源码

    public ConfigurableApplicationContext run(String... args) {
//起了个timer,用于计时,不用管
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection exceptionReporters = new ArrayList<>();
//这个就是配置个系统环境变量 java.awt.headless
        configureHeadlessProperty();
//获取SpringApplicationRunListeners,这个和之前提到的ApplicationListener是两个不同的接口。两者间的联系在于,
//在springboot的默认实现中,有一个特殊的类EventPublishingRunListener会继承SpringApplicationRunListeners,
//他会在springboot的特殊阶段发布对应的ApplicationEvent,并且被ApplicationListener接收。
        SpringApplicationRunListeners listeners = getRunListeners(args);
//进入starting阶段,发布ApplicationStartingEvent事件。
        listeners.starting();
        try {
//包装启动参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备environment,确定propertiesSource。发布ApplicationEnvironmentPreparedEvent。
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//配置环境变量spring.beaninfo.ignore
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
//创建应用上下文
            context = createApplicationContext();
//获取SpringBootExceptionReporter
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
//发布ApplicationContextInitializedEvent事件和ApplicationPreparedEvent事件
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
/划重点,进行bean的初始化工作
            refreshContext(context);
//暂时是个空function。
            afterRefresh(context, applicationArguments);
//关闭计算器,也就是前面创建的那个timer
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
//发布ApplicationStartedEvent事件
            listeners.started(context);
//运行ApplicationRunner和CommandLineRunner
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
//发布ApplicationReadyEvent事件
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

可以看到这个方法比较复杂,基本上每一行语句都对应着一个函数。为了更加方便地捋清楚整个flow,我们跟着SpringApplicationRunListener,看一下它的每个方法都在什么时候被trigger,两个方法之间都做了什么事情。

首先看一下SpringApplicationRunListener(s)有哪些方法

SpringApplicationRunListener并没有直接被SpringApplication调用,而是作为一个整体SpringApplicationRunListeners被统一调用,它有starting、environmentPrepared、contextPrepared、contextLoaded、started、running、failed等七个方法。由于这一篇我们只讲SpringBoot的启动流程,因此只讨论前六个方法,看他们的调用时机。

public interface SpringApplicationRunListener {

    /**
     * Called immediately when the run method has first started. Can be used for very
     * early initialization.
     */
    default void starting() {
    }

    /**
     * Called once the environment has been prepared, but before the
     * {@link ApplicationContext} has been created.
     * @param environment the environment
     */
    default void environmentPrepared(ConfigurableEnvironment environment) {
    }

    /**
     * Called once the {@link ApplicationContext} has been created and prepared, but
     * before sources have been loaded.
     * @param context the application context
     */
    default void contextPrepared(ConfigurableApplicationContext context) {
    }

    /**
     * Called once the application context has been loaded but before it has been
     * refreshed.
     * @param context the application context
     */
    default void contextLoaded(ConfigurableApplicationContext context) {
    }

    /**
     * The context has been refreshed and the application has started but
     * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
     * ApplicationRunners} have not been called.
     * @param context the application context.
     * @since 2.0.0
     */
    default void started(ConfigurableApplicationContext context) {
    }

    /**
     * Called immediately before the run method finishes, when the application context has
     * been refreshed and all {@link CommandLineRunner CommandLineRunners} and
     * {@link ApplicationRunner ApplicationRunners} have been called.
     * @param context the application context.
     * @since 2.0.0
     */
    default void running(ConfigurableApplicationContext context) {
    }

    /**
     * Called when a failure occurs when running the application.
     * @param context the application context or {@code null} if a failure occurred before
     * the context was created
     * @param exception the failure
     * @since 2.0.0
     */
    default void failed(ConfigurableApplicationContext context, Throwable exception) {
    }

}

在starting方法之前

springboot在调用run方法后,基本上第一时间就trigger了listener的starting方式,在这之前,基本上没做啥事。

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);

一个是创建了一个timer(StopWatch)用来之后计算启动耗时,下面这段日志就是它功能的体现。

2021-09-22 16:29:38.241  INFO 8044 --- [           main] com.sample.app.SampleApplication       : Started SampleApplication in 32.023 seconds (JVM running for 121.055)

一个是调用configureHeadlessProperty方法来设置java.awt.headless的系统环境变量,这个默认值是true。

  private boolean headless = true;

  private void configureHeadlessProperty() {
      System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
              System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
  }

再有就是从之前解析好的spring.factories中读取所有的SpringApplicationRunListeners。

    private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class[] types = new Class[] { SpringApplication.class, String[].class };
        return new SpringApplicationRunListeners(logger,
                getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }

environmentPrepared之前

对应的代码在run方法里对应两行,其中第一行是把系统参数包装一下,使得用起来更加方便。第二行则是进行一些环境的准备工作。

            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

我们重点看prepareEnvironment这个方法中做了哪些工作。

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // Create and configure the environment
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach(environment);
        listeners.environmentPrepared(environment);
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                    deduceEnvironmentClass());
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

可以清楚的看到,在调用listeners.environmentPrepared(environment);方法之前,只有三行code,分别做了三件事情。
getOrCreateEnvironment根据应用类型来创建ConfigurableEnvironment

    private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        }
        switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
        }
    }

configureEnvironment 来配置propertiesSource和profiles
这里面主要完成了ropertiesSource和profiles之的配置。

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
       if (this.addConversionService) {
           ConversionService conversionService = ApplicationConversionService.getSharedInstance();
           environment.setConversionService((ConfigurableConversionService) conversionService);
       }
       configurePropertySources(environment, args);
       configureProfiles(environment, args);
   }

其中,configurePropertySources是配置environment中的propertySources属性。propertySources是用于记录系统的配置参数可以从哪里读取,读取的结果是什么。各个propertySource在其中的先后顺序体现了他们彼此的优先级。默认的propertySources包括systemProperties和systemEnvironment两个。在configurePropertySources步骤中根据代码或者参数的不同,可能会额外加上defaultProperties和commandLineArgs两个propertySource。

    protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
        MutablePropertySources sources = environment.getPropertySources();
        if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
            sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
        }
        if (this.addCommandLineProperties && args.length > 0) {
            String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
            if (sources.contains(name)) {
                PropertySource source = sources.get(name);
                CompositePropertySource composite = new CompositePropertySource(name);
                composite.addPropertySource(
                        new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
                composite.addPropertySource(source);
                sources.replace(name, composite);
            }
            else {
                sources.addFirst(new SimpleCommandLinePropertySource(args));
            }
        }
    }

configureProfiles则是试图从现有的propertySources中尝试获取spring.profiles.active参数。需要注意的是,此时的propertySources并不包含与你的配置文件(比如application.properties)相对应的propertySource,所以无论你的配置文件怎么配置,对这个阶段都不造成影响。

    protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
        Set profiles = new LinkedHashSet<>(this.additionalProfiles);
        profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
        environment.setActiveProfiles(StringUtils.toStringArray(profiles));
    }

ConfigurationPropertySources.attach(environment)将自己置于propertySources的第一个的位置
前面也讲了,各个PropertySource在propertySources中的先后顺序决定了优先级,现在把ConfigurationPropertySources放到第一位,也就意味着它的优先级是最高的。细心的人应该也会注意到ConfigurationPropertySources.attach(environment)这一行前后调用了两次,这个也是为了确保ConfigurationPropertySources优先加载的顺序。

    public static void attach(Environment environment) {
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
        MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
        PropertySource attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
        if (attached != null && attached.getSource() != sources) {
            sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
            attached = null;
        }
        if (attached == null) {
            sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
                    new SpringConfigurationPropertySources(sources)));
        }
    }

在environmentPrepared方法

EventPublishingRunListener发布ApplicationEnvironmentPreparedEvent事件

    public void starting() {
        this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
    }

订阅了这个事件的ApplicationListener也找到了几个,我们先记录下来。

  • ConfigFileApplicationListener
  • AnsiOutputApplicationListener
  • LoggingApplicationListener
  • ClasspathLoggingApplicationListener
  • BackgroundPreinitializer
  • DelegatingApplicationListener
  • FileEncodingApplicationListener

其中比较有意思的是ConfigFileApplicationListener,因此我们展开讲一下,剩下的等有空了再说。
在接收到ApplicationEnvironmentPreparedEvent之后,做了这件事

    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
// 加载所有EnvironmentPostProcessor并排序
        List postProcessors = loadPostProcessors();
        postProcessors.add(this);
        AnnotationAwareOrderComparator.sort(postProcessors);
//依次调用EnvironmentPostProcessor的postProcessEnvironment方法
        for (EnvironmentPostProcessor postProcessor : postProcessors) {
            postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
        }
    }

在这块逻辑中,会调用它自身的postProcessEnvironment方法,从而实现配置文件的加载

   @Override
   public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
       addPropertySources(environment, application.getResourceLoader());
   }

   /**
    * Add config file property sources to the specified environment.
    * @param environment the environment to add source to
    * @param resourceLoader the resource loader
    * @see #addPostProcessors(ConfigurableApplicationContext)
    */
   protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
       RandomValuePropertySource.addToEnvironment(environment);
       new Loader(environment, resourceLoader).load();
   }

其中,loader的load方法我们也记录一下

        void load() {
            FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
                    (defaultProperties) -> {
                        this.profiles = new LinkedList<>();
                        this.processedProfiles = new LinkedList<>();
                        this.activatedProfiles = false;
                        this.loaded = new LinkedHashMap<>();
                        initializeProfiles();
                        while (!this.profiles.isEmpty()) {
                            Profile profile = this.profiles.poll();
                            if (isDefaultProfile(profile)) {
                                addProfileToEnvironment(profile.getName());
                            }
                            load(profile, this::getPositiveProfileFilter,
                                    addToLoaded(MutablePropertySources::addLast, false));
                            this.processedProfiles.add(profile);
                        }
                        load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
                        addLoadedPropertySources();
                        applyActiveProfiles(defaultProperties);
                    });
        }

contextPrepared之前

这部分在SpringApplication对应的代码主要是这么三行

//初始化
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);

第一行是根据应用类型,创建ApplicationContext的实例

protected ConfigurableApplicationContext createApplicationContext() {
        Class contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

第二行就是获取SpringBootExceptionReporter的实例。
第三行所对应的方法比较复杂,和我们这个阶段对应的主要是这么几行。

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        postProcessApplicationContext(context);
        applyInitializers(context);
                ....
    }

其中applyInitializers(context)这行就是对我们在new SpringApplication()时找到的ApplicationContextInitializer进行初始化工作。如果我们要增加自己的BeanFactoryPostProcessor,就可以创建一个ApplicationContextInitializer来做。BeanFactoryPostProcessor这个类很多同学可能并不清楚,这也是Spring的拓展点之一,我们可以使用它来读取并覆盖应用程序上下文中配置的bean属性。

contextLoaded之前

这个部分只对应了run 方法中的一行,和上面一段代码块属于同一个私有方法。对应到这个阶段,主要就是注册了一些特殊的sington的bean,然后尝试去注册primarySource的definition,为之后的componentScan时找basePackage做好准备。

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
...
        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);
        }
        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // Load the sources
        Set sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
}
 
 

其中的getAllResource长这个样子

    public Set getAllSources() {
        Set allSources = new LinkedHashSet<>();
        if (!CollectionUtils.isEmpty(this.primarySources)) {
            allSources.addAll(this.primarySources);
        }
        if (!CollectionUtils.isEmpty(this.sources)) {
            allSources.addAll(this.sources);
        }
        return Collections.unmodifiableSet(allSources);
    }
 
 

load(context, sources.toArray(new Object[0]))这一行就不展开讲了,就是取sources的第一个元素,尝试去注册其对应的definition,之后找basePackage的时候会用到

started之前

这个在run方法中只对应了一行代码refreshContext(context);。它会间接的对ApplicationContext的refresh方法进行调用。由于我们现在debug的是一个web项目,因此走进去对应的ApplicationContext的实现类是ServletWebServerApplicationContext。它的refresh方法可以简单的视为对父类refresh方法的调用,它的onRefresh方法则在父类(AbstractApplicationContext)的方法之上创建了WebServer。

    @Override
    public final void refresh() throws BeansException, IllegalStateException {
        try {
            super.refresh();
        }
        catch (RuntimeException ex) {
            WebServer webServer = this.webServer;
            if (webServer != null) {
                webServer.stop();
            }
            throw ex;
        }
    }

    @Override
    protected void onRefresh() {
        super.onRefresh();
        try {
            createWebServer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start web server", ex);
        }
    }

我们看一下在父类的refresh方法中做了哪些事情,其中有几个点需要注意一下,一个是扫描并注册beanDefinition的行postProcessBeanFactory,一个是创建WebServer的行onRefresh,一个是根据definition初始化bean的行finishBeanFactoryInitialization,一个是启动WebServer的行finishRefresh

    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
//这个步骤会清除缓存,检查是否缺少环境参数,保存前期加载的ApplicationListener实例(从applicationListeners属性到earlyApplicationListeners)
            // Prepare this context for refreshing.
            prepareRefresh();
            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//这个步骤通过调用ignoreDependencyInterface,registerResolvableDependency,addBeanPostProcessor等方法完成beanFactory的一些配置。其中addBeanPostProcessor方法会被调用至少两次,传参分别为ApplicationContextAwareProcessor和ApplicationListenerDetector
            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);
//这个步骤会加载bean的definition
                // 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();

//Web类型的App将在这个步骤创建WebServer,但是WebServer此时并不会立刻启动
                // Initialize other special beans in specific context subclasses.
                onRefresh();
//注册监听
                // Check for listener beans and register them.
                registerListeners();
//这个时候会根据之前获取的的bean的definition,进行bean的初始化
                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);
//Web类型的App将在这个步骤启动之前创建好的WebServer
                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                ...
            }
        }
    }

running之前

这个部分就比较简单了,主要是invoke了ApplicationRunner和CommandLineRunner

            callRunners(context, applicationArguments);

function的内容长这个样子

    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List 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);
            }
        }
 
 

最后

在这之后,我们的web类型的application就启动成功了!

你可能感兴趣的:(# SpringBoot 启动流程)