Spring Boot源码阅读系列(2)-启动过程(1)

131 阅读13分钟

源码

github.com/spring-proj…

版本为:3.3.x

引言

首先,需要先前往Spring Boot的github地址clone对应的源码;然后配置好自己的Gradle就好了。 Spring Boot的Demo代码主要存放在spring-boot-tests/spring-boot-smoke-tests下,我们从spring-boot-smoke-tests-tomcat为例开始调试阅读。

内容较多,可以只读自己感兴趣的章节

主函数

@SpringBootApplication
public class SampleTomcatApplication {
    private static final Log logger = LogFactory.getLog(SampleTomcatApplication.class);

    @Bean
    protected ServletContextListener listener() {
       return new ServletContextListener() {
          @Override
          public void contextInitialized(ServletContextEvent sce) {
             logger.info("ServletContext initialized");
          }
          @Override
          public void contextDestroyed(ServletContextEvent sce) {
             logger.info("ServletContext destroyed");
          }
       };
    }

    public static void main(String[] args) {
       // 在SpringBoot程序中所有的开始都是.run()
       SpringApplication.run(SampleTomcatApplication.class, args);
    }
}

SpringBoot程序中,一切的开始都来自于xxxxxx#main中的SpringApplication.run(Class<?>, String[]);函数

new SpringApplicaiton(Class<?>[])

image.png

该方法首先创建一个SpringApplicaiton对象

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 由SpringBootApplication.run()调用时, resourceLoader为null, primarySources为主类
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 获得web应用类型
    this.properties.setWebApplicationType(WebApplicationType.deduceFromClasspath());
    // 从META-INF/spring.factories中加载BootstrapRegistryInitializer实现类
    this.bootstrapRegistryInitializers = new ArrayList<>(
          getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    // 加载META-INF/spring.factories中的ApplicationContextInitializer实现类
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 加载META-INF/spring.factories中的ApplicationListener实现类
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 寻找main方法所在的类
    this.mainApplicationClass = deduceMainApplicationClass();
}
  1. resourceLoader: 由SpringBootApplication.run()调用时, resourceLoader为null
  2. primarySources: 顾名思义,就是主类
  3. properties.setWebApplicationType(): 用来记录当前的应用类型,不同的应用类型会执行不同的操作
  4. bootstrapRegistryInitializers: 加载所有BootstrapContext的初始化器
  5. setInitializers():加载所有的ApplicationContext初始化器
  6. setListeners():加载所有的ApplicaitonListener
  7. mainApplicationClass:用于记录主类(通过main()方法寻找)
// WebApplicationType.deduceFromClasspath()
public enum WebApplicationType {
    /**
     * 非web应用,不启动内嵌web服务器
     */
    NONE,
    /**
     * servlet web应用,启动内嵌servlet web服务器
     */
    SERVLET,
    /**
     * reactive web应用,启动内嵌reactive web服务器
     */
    REACTIVE;

    private static final String[] SERVLET_INDICATOR_CLASSES = { "jakarta.servlet.Servlet",
           "org.springframework.web.context.ConfigurableWebApplicationContext" };

    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";
    /**
     * 基于classpath检测应用类型 {@link WebApplicationType}
     */
    static WebApplicationType deduceFromClasspath() {
        // 当存在webflux相关类,不存在webmvc和jersey相关类时,返回reactive
        if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
              && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
              && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
           return WebApplicationType.REACTIVE;
        }
        // 当不存在servlet相关类时,返回非web应用
        for (String className : SERVLET_INDICATOR_CLASSES) {
           if (!ClassUtils.isPresent(className, null)) {
              return WebApplicationType.NONE;
           }
        }
        // 保底返回servlet应用
        return WebApplicationType.SERVLET;
    }
    // ......
}

SpringBoot就是根据classpath下的类来判断应用类型的;当然也可以通过spring.main.web-application-type配置,此项配置会在prepareEnvironment阶段被应用

.run(String... )

在run方法中,最主要的两个语句是prepareContext()refreshContext();当然其他代码的作用,这里也将会讲解。

/**
 * 运行SpringApplication 创建并刷新一个新的{@link ApplicationContext}.
 * @param args 应用程序参数(通常从Java main方法传递)
 * @return 运行中的{@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {
    // startup 本质上是一个计时器,当前生成的类为StandardStartup
    Startup startup = Startup.create();
    // 注册关闭钩子
    if (this.properties.isRegisterShutdownHook()) {
       SpringApplication.shutdownHook.enableShutdownHookAddition();
    }
    // 创建BootstrapContext
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    // 配置是否有UI
    configureHeadlessProperty();
    // 创建RunListeners,参数最终用于`this.constructor.newInstance(args);`
    // 运行监听器主要是用于监听SpringApplication的运行过程
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 触发启动中事件
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
       // 将args封装参数applicationArguments
       ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
       // 准备环境
       ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
       // Banner就是启动时打印的logo,返回 打印完对象的banner
       // 优先级:spring.banner.location > banner.txt > fallbackBanner > defaultBanner
       Banner printedBanner = printBanner(environment);
       // applicationContextFactory根据应用类型创建不同的ApplicationContext
       context = createApplicationContext();
       context.setApplicationStartup(this.applicationStartup);  // 度量各个阶段的性能
       // 预处理上下文
       prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
       // 刷新上下文
       refreshContext(context);
       // 刷新后处理
       afterRefresh(context, applicationArguments);
       startup.started();
       if (this.properties.isLogStartupInfo()) {
          new StartupInfoLogger(this.mainApplicationClass, environment).logStarted(getApplicationLog(), startup);
       }
       // 触发启动完成事件
       listeners.started(context, startup.timeTakenToStarted());
       callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
       throw handleRunFailure(context, ex, listeners);
    }
    try {
       if (context.isRunning()) {
          listeners.ready(context, startup.ready());
       }
    }
    catch (Throwable ex) {
       throw handleRunFailure(context, ex, null);
    }
    return context;
}

Startup

/**
* 这是新版写法
*/
Startup startup = Startup.create();
// statement
startup.started();
// statement
listeners.started(context, startup.timeTakenToStarted());
/**
* 这是旧版写法
*/
long startTime = System.nanoTime();
// statement
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
// statement
listeners.started(context, timeTakenToStartup);

Startup 本质上是拿来计时的;用来计算从run开始到applicationContext构建完成所需的时间

BootstrapContext

run()方法执行之初,ApplicationContext构建之前,SpringBoot会先去构建BootstrapContext

BootstrapContext是一个简单的引导上下文,其在启动以及 Environment 后处理期间可用,直到ApplicationContext可用为止。

可以提供对单例的惰性访问,这些单例可能创建成本很高;或者在ApplicationContext可用之前,需要共享一些单例

DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// statement
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
    // statement
    ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    // statement
    prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    // statement
}
/**
 * 创建BootstrapContext,并使用所有的BootstrapRegistryInitializer进行初始化
 */
private DefaultBootstrapContext createBootstrapContext() {
    DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
    this.bootstrapRegistryInitializers
          .forEach((initializer) ->
                initializer.initialize(bootstrapContext));
    return bootstrapContext;
}

上面的代码十分简单,new一个BootstrapContext,然后使用initializers进行初始化;在此我们可以自己定义一些初始化类进行初始化。

// 触发所有SpringApplicationRunListener#starting()
listeners.starting(bootstrapContext, this.mainApplicationClass);

详细SpringApplicationRunListener结构,见附录

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
       DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    // statement
    listeners.environmentPrepared(bootstrapContext, environment);
    // statement
}

prepareEnvironment内,涉及到bootstrapContext仅有一句,用于触发environmentPrepared阶段

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
       ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
       ApplicationArguments applicationArguments, Banner printedBanner) {
    // statement
    // 发布BootstrapContextClosedEvent事件
    // 事件参数有bootstrapContext与ApplicationContext
    bootstrapContext.close(context);
    // statement
}

而在prepareContext内,也仅有一句bootstrapContext.close(context);,该代码会触发 new BootstrapContextClosedEvent(bootstrapContext, applicationContext)事件。

小结

从上述我们可以看出,bootstrapContext在从创建到关闭,与之相关的语句都与listenter相关; 所有我们推测bootstrapContext只是在applicationContext创建前,SpringBoot提供的一个简单容器。

在创建时我们可以实现初始化器自定义该容器,在starting, enironmentPrepared时自定义RunListenter自定义处理将环境信息添加进入bootstrapContext,在close时,可以将bootstrapContext中的数据转入applicationContext

我们将在下面的SpringApplicationRunListeners章节具体讲解如何使用这个类。

configureHeadlessProperty();

/**
 * 根据系统属性设置是否启用headless模式
 * headless是应用的一种配置模式。在服务器可能缺少显示设备、键盘、鼠标等外设的情况下使用这种模式。
 * 1. 如果应用不需要任何head,那么有无这个配置没有任何影响;
 * 2. 如果应用有弹出窗口之类的操作,那么在headless模式下这种操作会被阻止。
 * 默认情况下等价于 {@code System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, "true")}
 */
private void configureHeadlessProperty() {
    System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
          System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

想修改,就去自己在启动时修改VM option-Djava.awt.headless=false

SpringApplicationRunListeners

SpringApplicationRunListeners是用来监听run函数执行状态的监听器,涉及到此监听器的有以下几条语句,其中bootstrapContext指的是BootstrapContext;context指的是ApplicationContext

public ConfigurableApplicationContext run(String... args) {
    // 运行监听器主要是用于监听SpringApplication.run()的运行阶段
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 触发启动中阶段
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
       // ......
       ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
       {
           listeners.environmentPrepared(bootstrapContext, environment);
       }
       // ......
       prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
       {
            // ......
            // 监听器触发上下文准备好阶段
            listeners.contextPrepared(context);
            // ......
            // 触发上下文加载完成阶段
            listeners.contextLoaded(context);
            // ......
       }
       // ......
       // 触发启动完成阶段
       listeners.started(context, startup.timeTakenToStarted());
       // ......
    }
    catch (Throwable ex) {
       // 触发run失败阶段
       throw handleRunFailure(context, ex, listeners);
    }
    try {
       if (context.isRunning()) {
          // 触发准备好阶段
          listeners.ready(context, startup.ready());
       }
    }
    catch (Throwable ex) {
       // 触发run失败阶段
       throw handleRunFailure(context, ex, null);
    }
    return context;
}

其中getRunListeners(args)代码如下

private SpringApplicationRunListeners getRunListeners(String[] args) {
    ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this);
    argumentResolver = argumentResolver.and(String[].class, args);
    // 从META-INF/spring.factories中加载SpringApplicationRunListener实现类
    List<SpringApplicationRunListener> listeners = getSpringFactoriesInstances(SpringApplicationRunListener.class,
          argumentResolver);
    // 默认为null
    SpringApplicationHook hook = applicationHook.get();
    SpringApplicationRunListener hookListener = (hook != null) ? hook.getRunListener(this) : null;
    if (hookListener != null) {
       listeners = new ArrayList<>(listeners);
       listeners.add(hookListener);
    }
    return new SpringApplicationRunListeners(logger, listeners, this.applicationStartup);
}

SpringApplicationRunListener类主要来自于两部分

  1. 来自于META-INF/spring.factories
  2. 来自于hook.getRunListener(this);默认为null

在默认情况下,仅存在EventPublishingRunListener,其会包装成事件去触发我们初始化SpringAppicaiton是加载的ApplicationListener

// 加载META-INF/spring.factories中的ApplicationListener实现类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

image.png

小结

根据以上信息可以得知,我们是无法通过仅仅在本地创建对应实现类来自定义监听的;必须将对应的实现类添加到META-INF/spring.factories

  1. 实现自己的SpringApplicaitonRunListener类
package smoketest.tomcat.runListenters;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplicationRunListener;
public class MyListener implements SpringApplicationRunListener {
    @Override
    public void starting(ConfigurableBootstrapContext bootstrapContext) {
       System.out.println("smoketest.tomcat.runListenters.MyListener");
    }
}
  1. 在本项目resource下添加spring.factories

image.png

当然你也可以利用EventPublishingRunListener去触发ApplicationListener;但要做的事也是一样的。

DefaultApplicationArguments(args);

封装args用的;不细讲

prepareEnvironment(...)

用于初始化应用环境, 还涉及到加载多数据源spring.main前缀的配置加载到ApplicationProperties,还涉及到数据源优先级排序

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
       DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    // 根据web应用类型创建不同的Environment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 根据args配置Environment
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach(environment);
    // 监听器触发环境准备好事件
    listeners.environmentPrepared(bootstrapContext, environment);
    // 将环境中的applicationInfo信息放在环境信息的最后
    ApplicationInfoPropertySource.moveToEnd(environment);
    // 将环境中的defaultProperties放在环境信息的最后
    DefaultPropertiesPropertySource.moveToEnd(environment);
    Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
          "Environment prefix cannot be set via properties.");
    // 将environment中spring.main开头的属性绑定到 ApplicationProperties
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
       EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
       // 如果有必要, 将environment转换为deduceEnvironmentClass()类型
       environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
    }
    // 将configurationProperties绑定到environment, 放在environment的最前面
    ConfigurationPropertySources.attach(environment);
    return environment;
}

一共有以下几种数据源 image.png

咱们最常用的application.properties在第6个,为OriginTrackedMapPropertySource读属性时,使用迭代器模式,依次搜索,直到找到对应属性或者结束

image.png

printBanner(environment)

打印logo,springboot在启动时可以去打印对应的logo;如果大家有自定义logo删除logo的需求,可以看这边

private Banner printBanner(ConfigurableEnvironment environment) {
    // 查询是否有关闭logo的设置
    if (this.properties.getBannerMode(environment) == Banner.Mode.OFF) {
       return null;
    }
    // 加载器
    ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
          : new DefaultResourceLoader(null);
    // 创建打印类
    SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
    // 选择用logger打印,还是out流打印
    if (this.properties.getBannerMode(environment) == Mode.LOG) {
       return bannerPrinter.print(environment, this.mainApplicationClass, logger);
    }
    return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
/**
 * 判断是否开启banner
 * 如果this.bannerMode不为空,返回this.bannerMode
 * 如果开启了structuredLoggingEnabled,返回Mode.OFF
 * 否则返回Mode.CONSOLE
 */
Mode getBannerMode(Environment environment) {
    if (this.bannerMode != null) {
       return this.bannerMode;
    }
    boolean structuredLoggingEnabled = environment
       .containsProperty(LoggingSystemProperty.CONSOLE_STRUCTURED_FORMAT.getApplicationPropertyName());
    return (structuredLoggingEnabled) ? Mode.OFF : Banner.Mode.CONSOLE;
}

Mode getBannerMode(Environment environment) {
    if (this.bannerMode != null) {
       return this.bannerMode;
    }
    boolean structuredLoggingEnabled = environment
       .containsProperty(LoggingSystemProperty.CONSOLE_STRUCTURED_FORMAT.getApplicationPropertyName());
    return (structuredLoggingEnabled) ? Mode.OFF : Banner.Mode.CONSOLE;
}

如果允许打印,在bannerPrinter.print内的getBanner设置了banner的优先级顺序

/**
 * 如果没有spring.banner.location与banner.txt
 * 去加载 fallbackBanner,如果fallbackBanner也没有,就去用默认的
 */
private Banner getBanner(Environment environment) {
    Banner textBanner = getTextBanner(environment);
    if (textBanner != null) {
       return textBanner;
    }
    if (this.fallbackBanner != null) {
       return this.fallbackBanner;
    }
    return DEFAULT_BANNER;  // 加载默认logo
}
/**
 * 先去读取environment中spring.banner.location指定的位置
 * 如果为空则读取resourceLoader下的banner.txt
 * 如果存在banner.txt就去封装返回
 * 如果不存在就返回null
 */
private Banner getTextBanner(Environment environment) {
    String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION);
    Resource resource = this.resourceLoader.getResource(location);
    try { // 默认resource.exists() == false
       if (resource.exists() && !resource.getURL().toExternalForm().contains("liquibase-core")) {
          return new ResourceBanner(resource);
       }
    }
    catch (IOException ex) {
       // Ignore
    }
    return null;
}
小结

关闭banner: 2选1 根据上面的源码,我们可以得出

  1. 配置spring.main.banner-mode=off,在prepareEnvironment阶段,该配置会被写入ApplicationProperties
  2. 配置logging.structured.format.console=ecs,该配置会将log以json形式输出,导致关闭banner

如果只是单纯关闭banner,只需要选1

自定义banner 根据上面的源码,我们可以得出springboot加载banner的优先级 spring.banner.location > banner.txt > fallbackBanner > defaultBanner

  1. 配置spring.banner.location,指定banner位置
  2. 直接在resource下创建banner.txt文件

banner字符画在线生成网站:www.bootschool.net/ascii

ApplicationContext

ApplicationContext主要涉及到以下语句,其中最重要的是prepareContext(...)与refreshContext(...)

// 创建上下文
context = createApplicationContext();
// applicationStartup用于检查应用程序的启动阶段
context.setApplicationStartup(this.applicationStartup);
// 预处理上下文,重要功能是将主类注册到BeanFactory中,但未去解析主类
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新上下文,会去解析主类,在这里会将所有的bean加载到context中
// 此时context已经可用
refreshContext(context);
// 刷新后处理
afterRefresh(context, applicationArguments);
// ......
callRunners(context, applicationArguments);
prepareContext(...)

在该方法中,Spring Boot 会对应用程序上下文 (ApplicationContext) 进行一系列的准备工作,确保在应用程序启动之前,上下文已经正确配置并准备好处理请求。这个方法是 Spring Boot 启动流程中的一个重要步骤。其中前部分都只是对context的配置

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
       ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
       ApplicationArguments applicationArguments, Banner printedBanner) {
    // 应用上下文设置环境
    context.setEnvironment(environment);
    // 后处理应用上下文
    // 1. 如果beanNameGenerator不为null,则将其注册为单例bean,注册进context
    // 2. 如果resourceLoader不为null,则将它的加载器设置为resourceLoader或resourceLoader的加载器
    // 3. 如果addConversionService为true,则配置Object转换服务
    postProcessApplicationContext(context);
    // 将AOT生成的初始化器添加进初始化器列表,默认没必要
    addAotGeneratedInitializerIfNecessary(this.initializers);
    // 应用初始化器,对context进行初始化
    applyInitializers(context);
    // 触发发布上下文准备好阶段
    listeners.contextPrepared(context);
    // 发布BootstrapContextClosedEvent事件,告知已经关闭的bootstrapContext和启用的applicationContext
    bootstrapContext.close(context);
    if (this.properties.isLogStartupInfo()) {
       logStartupInfo(context.getParent() == null);
       logStartupInfo(context);
       logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    // 将Arguments注册为进BeanFactory
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
       // 将打印的banner注册进BeanFactory
       beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    // 如果beanFactory是AutowiredCapableBeanFactory,则设置是否允许循环引用,
    // 可使用spring.main.allow-circular-references配置
    if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {
       autowireCapableBeanFactory.setAllowCircularReferences(this.properties.isAllowCircularReferences());
       // 如果beanFactory是ListableBeanFactory,则设置是否允许BeanDefinition覆盖
       // 可使用spring.main.allow-bean-definition-overriding配置
       if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {
          listableBeanFactory.setAllowBeanDefinitionOverriding(this.properties.isAllowBeanDefinitionOverriding());
       }
    }
    // 如果允许lazy初始化,则添加LazyInitializationBeanFactoryPostProcessor
    // 可使用spring.main.lazy-initialization配置
    if (this.properties.isLazyInitialization()) {
       context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    // 如果允许keepAlive,则添加KeepAlive监听器
    // 可使用spring.main.keep-alive配置
    if (this.properties.isKeepAlive()) {
       context.addApplicationListener(new KeepAlive());
    }
    // 添加PropertySourceOrderingBeanFactoryPostProcessor
    context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
    // 如果没有使用生成的Artifacts
    if (!AotDetector.useGeneratedArtifacts()) {
       // Load the sources; 最重要的部分
       // 1. 主类一定是source
       // 2. 其他的source,来自于spring.main.sources配置
       // source值springboot开始扫描的起点
       Set<Object> sources = getAllSources();
       Assert.notEmpty(sources, "Sources must not be empty");
       // 将sources集合添加进applicationContext
       load(context, sources.toArray(new Object[0]));
    }
    // 触发上下文加载完成阶段
    listeners.contextLoaded(context);
}

下面是 Set<Object> sources = getAllSources();所执行的方法。

/**
 * 返回一个不可变的 source 集合,集合内的source将被添加进applicationContext。集合会将构造函数中
 * 指定的任何主source与配置的的其他source组合在一起。
 */
public Set<Object> getAllSources() {
    Set<Object> allSources = new LinkedHashSet<>();
    if (!CollectionUtils.isEmpty(this.primarySources)) {
       allSources.addAll(this.primarySources);
    }
    if (!CollectionUtils.isEmpty(this.properties.getSources())) {
       allSources.addAll(this.properties.getSources());
    }
    return Collections.unmodifiableSet(allSources);
}

默认情况下, sources 中只添加标有@SpringBootApplication的类

/**
 * 将beans加载到applicationContext中
 * 此时仅将sources加载到context中,还未刷新context
 * @param context the context to load beans into
 * @param sources the sources to load
 */
protected void load(ApplicationContext context, Object[] sources) {
    // ......
    BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator != null) {
       loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
       loader.setResourceLoader(this.resourceLoader);
    }
    if (this.environment != null) {
       loader.setEnvironment(this.environment);
    }
    loader.load();
}

loader.load();最终会去将bean注册进beanDefinitionMap,其最下层执行的函数如下所示

public void registerBean(Class<?> beanClass) {
    doRegisterBean(beanClass, null, null, null, null);
}
/**
 * 注册给定的bean,从类的注解上解析其元数据
 * @param beanClass bean的类型
 * @param name bean的名字,当前为null
 * @param qualifiers 类上注解,当前为null
 * @param supplier 创建bean的回调,当前为null
 * @param customizers one or more callbacks for customizing the factory's
 * {@link BeanDefinition}, for example, setting a lazy-init or primary flag, 依然是回调,当前为null
 */
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
       @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
       @Nullable BeanDefinitionCustomizer[] customizers) {

    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
       return;
    }
    // 设置一下属性信息
    abd.setAttribute(ConfigurationClassUtils.CANDIDATE_ATTRIBUTE, Boolean.TRUE);
    abd.setInstanceSupplier(supplier);
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    abd.setScope(scopeMetadata.getScopeName());
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    // 如果该类上存在注解(当前为null)
    if (qualifiers != null) {
       for (Class<? extends Annotation> qualifier : qualifiers) {
          // 此注解为 Primary
          if (Primary.class == qualifier) {
             abd.setPrimary(true);
          }
          // 此注解为 Fallback
          else if (Fallback.class == qualifier) {
             abd.setFallback(true);
          }
          // 此注解为 Lazy
          else if (Lazy.class == qualifier) {
             abd.setLazyInit(true);
          }
          // 保底操作
          else {
             abd.addQualifier(new AutowireCandidateQualifier(qualifier));
          }
       }
    }
    // 如果存在创建回调
    if (customizers != null) {
       for (BeanDefinitionCustomizer customizer : customizers) {
          customizer.customize(abd);
       }
    }
    // 将元信息与name封装起来
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    // registry就是context,在servlet下,其applicationContext实现了对应接口
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

此类中,会解析sources中类,并将信息转换成BeanDefinition,注册进context的beanDefinitionMap

context的beanfactory前后对比

image.png

image.png

小结

prepareContext核心操作就是把每个source加入context的beanFactory中,作为springboot去扫描类的起点。

refreshContext(...)

SpringBoot的run方法下的refreshContext,该函数会负责刷新ApplicationContext,确保所有 Bean 都被正确初始化并准备好处理请求,其中最主要的是invokeBeanFactoryPostProcessors(...)其会触发自动加载。以及registerBeanPostProcessors(...)不过这部分比较复杂,同时还涉及到自动加载与配置,准备在下一篇进行补充。

private void refreshContext(ConfigurableApplicationContext context) {
    // 如果注册了关闭钩子,则将其注册进context
    if (this.properties.isRegisterShutdownHook()) {
       shutdownHook.registerApplicationContext(context);
    }
    refresh(context);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
    applicationContext.refresh();
}
public final void refresh() throws BeansException, IllegalStateException {
    try {
       super.refresh();
    }
    catch (RuntimeException ex) {
       WebServer webServer = this.webServer;
       if (webServer != null) {
          webServer.stop();
          webServer.destroy();
       }
       throw ex;
    }
}
// 下面这一步是是spring-core包内的方法,也是真正的refresh操作,属于AbstractApplicationContext类
// 故只在此做简要介绍,具体将在下一节。
public void refresh() throws BeansException, IllegalStateException {
    this.startupShutdownLock.lock();
    try {
       this.startupShutdownThread = Thread.currentThread();

       StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

       // 为fresh做准备
       prepareRefresh();

       // 获取beanFactory
       ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

       // 准备beanFactory,设置了一些配置,比如添加类加载器,设置忽略哪些类等
       prepareBeanFactory(beanFactory);

       try {
          // Allows post-processing of the bean factory in context subclasses.
          postProcessBeanFactory(beanFactory);

          StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
          
          // 重要:Invoke factory processors registered as beans in the context.
          invokeBeanFactoryPostProcessors(beanFactory);
          // 重要:Register bean processors that intercept bean creation.
          registerBeanPostProcessors(beanFactory);
          
          beanPostProcess.end();

          // 初始化消息源
          initMessageSource();

          // 初始化事件传播器
          initApplicationEventMulticaster();

          // Initialize other special beans in specific context subclasses.
          onRefresh();

          // 检查监听器bean,并将其注册.
          registerListeners();

          // Instantiate all remaining (non-lazy-init) singletons.
          finishBeanFactoryInitialization(beanFactory);

          // 最后一步,发布对应的事件。Last step: publish corresponding event.
          finishRefresh();
       }

       catch (RuntimeException | Error 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 {
          contextRefresh.end();
       }
    }
    finally {
       this.startupShutdownThread = null;
       this.startupShutdownLock.unlock();
    }
}

callRunners(...)

参数ConfigurableApplicationContext context, ApplicationArguments args

该方法中,会去执行context中的所有ApplicationRunnerCommandLineRunner。默认情况下是没有的,通过定义runner可以针对一些args做一些操作。

非核心,不细讲

总结

SpringBoot在启动阶段,主要是做了创建bootStrapContext临时使用RunListener监听启动步骤准备环境,读取多数据源配置初始化ApplicationContext并根据初始化信息加载对应的类