Spring Boot 启动源码的个人分析

首先看一下main函数:

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

很简单的一个函数,直接调用了 SpringApplicationrun 方法,查看 run 方法,如下所示:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class<?>[] { primarySource }, args);
}

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

调用的 run 调用了自身一个重载方法,这个重载方法调用了 SpringApplication 的构造方法来创建一个 SpringApplication 对象,接着再调用这个 SpringApplication 对象的 run 方法,传递运行的指令参数 args(本例中并无运行指令参数)。先看下 SpringApplication 构造方法的实现:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 项目类型判断
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 设置初始化器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 设置监听器
    this.mainApplicationClass = deduceMainApplicationClass(); // main方法判断
}

其中传入的参数 resourceLoadernull,而 primarySources 为只有一个元素的数组,元素为 DemoApplication.class。前三行代码不做说明,主要是赋值和非空校验。

第四行代码:this.webApplicationType = WebApplicationType.deduceFromClasspath();,用来判断当前项目的类型,是 servletreactive 还是就是一个简单的项目,通过检查当前jar包是否存在指定的类来进行区分。代码实现简单,就不再展示。

PS:本文的项目为 servlet 项目,所以 webApplicationTypeWebApplicationType.SERVLET

第五、第六行代码,分别为初始化器、监听器的设置。两者均通过 getSpringFactoriesInstances 方法获取指定类型的实例集合。getSpringFactoriesInstances 方法获取的是 META-INF/spring.factories 文件中的相关配置,代码如下:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader(); // 获取类加载器,由于之前未传递类加载器,这里获取到的是AppClassLoader
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // SpringFactoriesLoader.loadFactoryNames 方法加载 spring.factories 配置信息
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 通过反射创建相关的实例对象
    AnnotationAwareOrderComparator.sort(instances); // 排序
    return instances;
}

这里方法实现都很简单,通过方法名也能很清楚的了解它们的作用。第七行代码 this.mainApplicationClass = deduceMainApplicationClass(); 用来获取 mian 方法所在的类,通过新建异常获取其栈信息,遍历到第一个有 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;
}

以上为 SpringApplication 构造方法中处理的事,主要是各种数据的初始化。接下来的 run 方法才是最主要的实现,先来看下代码:

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch(); // ①
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty(); // ②
    SpringApplicationRunListeners listeners = getRunListeners(args); // ③
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // ④
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment); // ⑤
        context = createApplicationContext(); // ⑥
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // ⑦
        prepareContext(context, environment, listeners, applicationArguments, printedBanner); // ⑧
        refreshContext(context); // ⑨
        afterRefresh(context, applicationArguments); // ⑩
        stopWatch.stop(); // ⑪
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments); // ⑫
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners); // ⑬
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

首先看到 ① 处代码的 StopWatch,其作用就是个计时器,配合 ⑪ 处代码,在日志中打印程序启动耗时,如:

[main] INFO  com.loewi_xi.demo.DemoApplication - Started DemoApplication in 2.002 seconds (JVM running for 2.414)

接着往下看 ② 处代码,方法实现很简单,不在贴出来,其主要确认 java.awt.headless 这个环境变量是否存在,不存在的话则设置默认值 true

当代码执行到 ③ 处的时候,便进入了核心部分了。getRunListeners 方法内部其实只是获取 META-INF/spring.factories 中配置的 org.springframework.boot.SpringApplicationRunListener 相关信息。通过 getRunListeners 获取的监听器,调用其 startingstarted 等方法来告知用户当前程序的状态。

当通过获取到的监听器告诉用户程序正在启动之后,便到了 ④ 处 —— 环境变量和参数的处理。处理完之后,通过 ⑤ 处会在日志中打印相关的logo信息。打印完logo之后便来到关键的 ⑥ 处,创建 ApplicationContextcreateApplicationContext 方法会依赖之前判断项目类型 webApplicationType 的值,来发射生成对应的 ApplicationContext

PS:由于 webApplicationTypeWebApplicationType.SERVLET,所以具体的 ApplicationContextorg.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext

⑦ 处获取 META-INF/spring.factories 中配置的异常报告配置信息,当启动时有异常产生时,通过它以及 ⑬ 处代码的实现,来进行通知处理。

接着看下 ⑧ 处 prepareContext 方法的实现:

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
    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<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[0]));
    listeners.contextLoaded(context);
}

这段代码的要重点关注 load the sources 这段实现,这里会根据 xml 中的配置或者注解配置,把各个 bean 生成 BeanDefinition 并进行注册。注册的实现如下:

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.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);
    if (qualifiers != null) {
        for (Class<? extends Annotation> qualifier : qualifiers) {
            if (Primary.class == qualifier) {
                abd.setPrimary(true);
            } else if (Lazy.class == qualifier) {
                abd.setLazyInit(true);
            } else {
                abd.addQualifier(new AutowireCandidateQualifier(qualifier));
            }
        }
    }
    if (customizers != null) {
        for (BeanDefinitionCustomizer customizer : customizers) {
            customizer.customize(abd);
        }
    }

    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

prepareContext 完成之后,便到了 ⑨ 处:refreshContext,而 refreshContext 的最终实现如下:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 给当前上下文做好刷新准备
        prepareRefresh();
        // 通知子类,刷新内部的 BeanFactory,并返回
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 给 BeanFactory 初始化相关信息,以便当前上下文能正常使用
        prepareBeanFactory(beanFactory);
        try {
            // 在上一步处理的基础上,让各个子类进行各自自定义的处理
            postProcessBeanFactory(beanFactory);
            // 把所有的 BeanFactoryPostProcessor 作为 Bean 注册到上下文中
            invokeBeanFactoryPostProcessors(beanFactory);
            // 注册处理 Bean 创建相关的 Processor
            registerBeanPostProcessors(beanFactory);
            // 初始化当前上下文的消息信息(国际化)
            initMessageSource();
            // 初始化当前上下问的消息传播器
            initApplicationEventMulticaster();
            // 在上面初始化的基础上,让各个子类进行各自自定义的初始化
            onRefresh();
            // 校验监听器,并注册到当前上下文中
            registerListeners();
            // 初始化所有非懒加载的单例 Bean
            finishBeanFactoryInitialization(beanFactory);
            // 无用资源清理以及消息发布
            finishRefresh();
        } catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + ex);
            }
            // 出现异常后销毁所有 Bean
            destroyBeans();
            // 重置 active 标识
            cancelRefresh(ex);
            throw ex;
        }
    }
}

当完成 refreshContext 之后,便来到了 ⑩ 处,该方法暂时未有任何实现,预留用于 refresh 完成之后的后续处理。最后执行到 ⑫ 处。这里的 callRunners 方法,其实就是调用程序中 ApplicationRunnerCommandLineRunner 相关接口的实现,即希望程序启动时需要执行的操作。

以上只是个人的看法,如有问题处,欢迎指出,我会积极改正。以上基于 Spring Boot 2.2.4.RELEASE。