SpringBoot 启动流程解析大全

前言

了解 SpirngBoot 的加载流程,可以让我们更好地自定义 SpringBoot 加载流程,并且对自己开发相关架构有一定的借鉴意义。本文旨在从 SpringBoot 的启动类出发,了解 SpringBoot 是怎样完成加载环境、设置上下文、注入 bean 等步骤。

由于本人水平有限,分析过程中可能有遗漏和错误,希望大家可以直接指出来,一起学习,一起进步。

run

不管自己怎么定制 SpringBoot 的启动流程,最后总是通过 run() 方法开始运行的。示例如下:

public static void main(String[] args) throws NotSuchUserIdException {
    SpringApplication springApplication = new SpringApplication(ApiApplication.class);
    springApplication.addListeners(new ApplicationStartup());
    springApplication.run(ApiApplication.class, args);
}

很显然,在 run 方法里 SpringBoot 完成了环境配置,上下文创建等相关任务。我们就进入这个方法看看。在第二次跳转后,我们来到了这个方法:

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

这个方法完成了两个步骤,一个是创建了 SpringApplication,一个是执行了 SpringApplication.run(args)方法。

我们先来看看 SpringApplication 的构造方法里做了什么:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 这里传过来的是一个 null
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
   /**
    * 确认 Web 的应用类型,一共有三种:
    *     1. reactive
    *     2. servlet
    *     3. none
    * 实际是通过 SpringBoot 自己的 forName 方法判断对应 Web 的类是否存在,
    * 比如 reactive 相关的 jar 包存在就是 reactive web 类型。
    */
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
   /**
    * setInitializers 就是将一些变量赋值,完成初始化操作。
    * 下面两个方法是这个方法的分析。
    */
   setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
   // 跟 getSpringFactoriesInstances 方法执行逻辑和 getSpringFactoriesInstances 差不多
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   // 获取当前启动类的类对象
   this.mainApplicationClass = deduceMainApplicationClass();
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[] {});
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    // 根据当前线程获取到其类的加载器
    ClassLoader classLoader = getClassLoader();
    /**
     * 这个方法会从 META-INF/spring.factories 配置文件中以 type
     * (就是ApplicationContextInitializer的全路径:
     * org.springframework.context.ApplicationContextInitializer)
     * 作为 key 获取所有的值
     */
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    /**
     * 在 createSpringFactoriesInstances() 方法中,
     * 通过反射的方式创建出各个 ApplicationContextInitializer 
     * 所有实现类的实例,然后添加到 List 中返回。
     */
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

然后我们把注意力放在 run 方法上:

public ConfigurableApplicationContext run(String... args) {
    // 启动计时器,用来计算应用启动花费的时长
    StopWatch stopWatch = new StopWatch();
    // 开始计时
    stopWatch.start();
    // 环境上下文变量
    ConfigurableApplicationContext context = null;
    // 错误报告,当启动错误时会将错误上报给报告
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    // 设置 java.awt.headless 属性
    // 这是用于在操作系统处于 HeadLess 模式时使用的
    // 可以查看博客:http://jimolonely.github.io/2019/03/26/java/039-java-headless/
    configureHeadlessProperty();
    // 用于获取运行时监听器,在这里我把它们称为监听器组
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 开启监听器组
    listeners.starting();
    // 创建 ApplicationArguments 对象,此时 applicationArguments 变量里面只有 args 值
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    // 准备上下文刷新的环境属性
    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    // 设置环境变量中的 spring.beaninfo.ignore 为 true/false
    configureIgnoreBeanInfo(environment);
    // 打印 printBanner
    Banner printedBanner = printBanner(environment);
    // 根据 web 类型创建不同的上下文对象
    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);
    }
    // 开始 started 事件传播
    listeners.started(context);
    // 调用 ApplicationRunner  和 CommandLineRunner 
    callRunners(context, applicationArguments);

    // 开始 running 事件传播
    listeners.running(context);
    return context;
}

可以看出 SpringBoot 做了很多处理,我们一步步对重要的方法进行分析。

创建监听器

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    /**
     * new SpringApplicationRunListeners 创建一个监听器组。
     * getSpringFactoriesInstances 方法在前面分析过了,在这里
     * 的作用是找到在 META-INF/spring.factories 里 key 为
     * org.springframework.boot.SpringApplicationRunListener
     * 的类
     */
    return new SpringApplicationRunListeners(logger,
            getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

进行环境的加载配置

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

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // 这里获取之前创建的 DefaultApplicationArguments
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // getSourceArgs 返回 run 方法中传入的 args
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    /**
     * 该监听器组会通过 ApplicationEnvironmentPreparedEvent
     * 这一事件拿到对应的监听器,并执行监听方法。这个对应的监听器为
     * ConfigFileApplicationListener,它会配置文扩展名以及默认配置文件名 application,
     * 在 classpath:/、classpath:/config/、file:./、file:./config/ 下面寻找配置文件。
     * 默认的配置文件后缀有 properties、yaml、yml、xml。
     */
    listeners.environmentPrepared(environment);
    // 将环境绑定到应用中
    bindToSpringApplication(environment);
    // this.isCustomEnvironment 为 false
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
    }
    /**
     * 属性源中获取是否存在 configurationProperties 的属性源,
     * 不存在则通过 ConfigurationPropertySourcesPropertySource 包装<configurationProperties, SpringConfigurationPropertySources>,
     * 然后添加到 MutablePropertySources 中的集合第一个位置上。
     */
    ConfigurationPropertySources.attach(environment);
    return environment;
}

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    // this.addConversionService 为 true
    // 这里通过单例模式获取一个 ConversionService
    // 并设置到环境中
    if (this.addConversionService) {
        ConversionService conversionService = ApplicationConversionService.getSharedInstance();
        environment.setConversionService((ConfigurableConversionService) conversionService);
    }
    // 查看有没有指定新的配置源(通过命令行指定)
    configurePropertySources(environment, args);
    // 通过配置中的 spring.profiles.active 获取 activeProfile
    configureProfiles(environment, args);
}

创建应用上下文

/**
 * 这个没什么好说的,就是根据 this.webApplicationType
 * 类型创建不同的默认 Web 上下文。
 */
context = createApplicationContext();

创建 bean 对象

// 容器刷新前的进行上下文配置
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 这个是知识点的重灾区,后面会详细进行分析
refreshContext(context);
// 这是一个空方法
afterRefresh(context, applicationArguments);

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
        SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    // 把之前的环境加载到上下文中
    context.setEnvironment(environment);
    // 注册上下文的后置处理,
    postProcessApplicationContext(context);
    // 执行 ApplicationContextInitializer
    // 可以通过继承 ApplicationContextInitializer 类
    // 重写 initialize 方法,然后将该子类通过 application.addInitializers
    // 注册到 SpringApplication。
    applyInitializers(context);
    // 发布上下文已预加载完成事件
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    // 注册 ApplicationArguments 类
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    // 注册 Banner 类
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    //  SpringApplication 提供 setAllowBeanDefinitionOverriding 方法
    //  来设置 BeanDefinition 是否可以被覆盖
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[0]));
    // 发布上下文已加载完成的事件
    listeners.contextLoaded(context);
}

refresh

在前面讲到创建 bean 对象时,我们看到 refreshContext 方法是用于刷新容器的。当我们点进方法后,发现它实际上是调用了 AbstractApplicationContext.refresh 方法,再次进去就看到了容器刷新的核心执行逻辑部分。SpringBoot 采用了模板方法,我们来看看它的执行逻辑:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 预刷新处理
        prepareRefresh();
        // 获取 beanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 对 beanFactory 进行预处理
        prepareBeanFactory(beanFactory);

        try {
            // 对 beanFactory 进行后置处理
            postProcessBeanFactory(beanFactory);
            // 调用 beanFactory 的后置处理器,这里是针对 BeanDefinition 进行处理
            invokeBeanFactoryPostProcessors(beanFactory);
            // 注册 bean 后置处理器
            registerBeanPostProcessors(beanFactory);
            // 国际化处理
            initMessageSource();
            // 初始化应用事件广播器
            initApplicationEventMulticaster();
            // 初始化特殊的 bean,这个方法是空实现
            onRefresh();
            // 注册监听器
            registerListeners();
            // 实例化剩余的 bean,IOC、DI、AOP 都是发生在这里的
            finishBeanFactoryInitialization(beanFactory);
            // 完成刷新,发布事件
            finishRefresh();
        }

        catch (BeansException ex) {
            // 销毁创建的 bean
            destroyBeans();
            // 取消 active 标记
            cancelRefresh(ex);
            throw ex;
        }
        finally {
            // 重置 Spring 内核中的常用自检缓存,清空单例 bean 内缓存
            resetCommonCaches();
        }
    }
}

接下来我们会它的各处理逻辑的实现进行解析。

预刷新上下文

protected void prepareRefresh() {
    // 获取上下文启动时间点
    this.startupDate = System.currentTimeMillis();
    // 设置上下文为活跃状态
    this.closed.set(false);
    this.active.set(true);

    // 初始化上下配置信息,这是一个空方法
    initPropertySources();

    // 检验环境中是否存在必须字段
    getEnvironment().validateRequiredProperties();

    // earlyApplicationListeners 绑定监听器
    if (this.earlyApplicationListeners == null) {
        this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    }
    else {
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);
    }

    this.earlyApplicationEvents = new LinkedHashSet<>();
}

获取 beanFactory

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    return getBeanFactory();
}

protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        // 创建默认 beanFactory
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        // 设置两个属性:
        // 1. allowBeanDefinitionOverriding
        // 2. allowCircularReferences
        customizeBeanFactory(beanFactory);
        // 记载所有的 bean 到 beanFactory 中
        // 主要有两种实现:
        // 1. XmlWebApplicationContext
        // 2. AnnotationConfigWebApplicationContext
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

可以看到在 obtainFreshBeanFactory 方法中完成了三件事:

  1. 创建 beanFactory
  2. 完成了 BeanDefinition 的加载和注册工作。

后面就是 beanFactory 的二次配置,没什么好说的。

postProcessBeanFactory 用于对 beanFactory 的 BeanDefinition 数据进行修改,所以在这里,我们可以自己去注册或修改一些 BeanDefinition。invokeBeanFactoryPostProcessors 方法就是调用 beanFactory 的后置处理器。代码如下:

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    /**
     * getBeanFactoryPostProcessors:拿到上下文的 beanFactoryProcessors
     * invokeBeanFactoryPostProcessors:实例化并调用已注册的 beanFactoryProcessor
     */
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

    /**
     * 判断是否定义了名为 loadTimeWeaver 的 bean,如果定义了则添加 loadTimeWeaver 功能的 
     * beanPostProcessor 扩展,并且创建一个临时的 classLoader 来让其处理真正的bean
     */
    if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
}

registerBeanPostProcessors 用于注册 bean 的后置处理器。

registerBeanPostProcessors 的执行流程如下,省略了部分代码,但是逻辑是不变的:

public static void registerBeanPostProcessors(
            ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

        // 获取 BeanPostProcessor 实现类的 beanName
        String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

        // 注册 PriorityOrdered 实现类
        sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

        // 注册 Ordered 实现类.
        List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
        for (String ppName : orderedPostProcessorNames) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            orderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        sortPostProcessors(orderedPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, orderedPostProcessors);

        // 注册其它 bean 后置处理器
        List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
        for (String ppName : nonOrderedPostProcessorNames) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            nonOrderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

        // 注册所有内部处理器
        sortPostProcessors(internalPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, internalPostProcessors);

        // 重新注册 ApplicationListenerDetector
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
    }

国际化处理

protected void initMessageSource() {
    // 获取 beanFactory
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    /**
     * 判断是否有名称为 messageSource 的 bean
     * 处理逻辑为:
     * 1. 如果存在 MessageSource 的实现类,则对 MessageSource进行初始化并赋值给其成员变量messageSource
     * 2. 如果不存在 MessageSource 实现类,则赋值一个 DelegatingMessageSource,保证正常进行 getMessage 
     * 方法调用。
     */
    if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
        this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
        if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
            HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
            if (hms.getParentMessageSource() == null) {
                hms.setParentMessageSource(getInternalParentMessageSource());
            }
        }
    }
    else {
        DelegatingMessageSource dms = new DelegatingMessageSource();
        dms.setParentMessageSource(getInternalParentMessageSource());
        this.messageSource = dms;
        beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
    }
}

初始化事件监听多路广播器

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    /**
     * 判断容器中是否存在名称为 applicationEventMulticaster
     * 的 ApplicationEventMulticaster 的实现类。如果没有,则采用
     * 默认的 SimpleApplicationEventMulticaster。
     */
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        this.applicationEventMulticaster =
                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
    }
    else {
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
    }
}

初始化特殊的 bean,该方法是一个空方法,用于让 AbstractApplicationContext 的子类重写。

初始化监听器

protected void registerListeners() {
    // getApplicationListeners 获取用户添加的监听器
    for (ApplicationListener<?> listener : getApplicationListeners()) {
        getApplicationEventMulticaster().addApplicationListener(listener);
    }

    // 从容器中获取所有 ApplicationListener 的实现类
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }

    // 发布早期的监听器,默认为空
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (earlyEventsToProcess != null) {
        for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
            getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }
}

实例化 bean

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // 为上下文初始化 conversionService
    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
            beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
        beanFactory.setConversionService(
                beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    }

    // 检查上下文是否有类型转换器 valueResolver
    if (!beanFactory.hasEmbeddedValueResolver()) {
        beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
    }

    // 初始化 LoadTimeWeaverAware bean
    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
    for (String weaverAwareName : weaverAwareNames) {
        getBean(weaverAwareName);
    }

    // 禁止使用临时类加载器进行类型匹配
    beanFactory.setTempClassLoader(null);
    // 允许缓存所有 bean 的定义数据
    beanFactory.freezeConfiguration();
    // 准备实例化 bean
    // 遍历 beanName list 中的每一个 beanName,
    // 然后实例化当前 beanName 相应的bean
    beanFactory.preInstantiateSingletons();
}

容器刷新完成的处理工作

protected void finishRefresh() {
    // 处理资源缓存
    clearResourceCaches();
    // 为上下文初始化生命周期处理器
    initLifecycleProcessor();
    // 将刷新关闭事件传播到生命周期处理器
    getLifecycleProcessor().onRefresh();
    // 推送上下文刷新完毕事件到对应的监听器
    publishEvent(new ContextRefreshedEvent(this));
    // 将上下文注册到 MBean 中,MBean 是 Java 的一种技术
    // 参考博客:https://juejin.im/post/6856949531003748365
    LiveBeansView.registerApplicationContext(this);
}

总结

到此 SpringBoot 启动的主要流程就分析完了。我们可以看出它的结构十分严谨,对并发也进行了相关的处理,并且提供了很多接口供我们实现,以此来定制自己的处理逻辑,其中主要使用的设计模式为工厂模式和模板模式。这些为我们以后设计自己的网站架构有很大的启发。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值