SpringBoot的启动流程

文章目录

  • [一、创建一个简单的 SpringBoot 项目](#一、创建一个简单的 SpringBoot 项目)
  • [二、SpringApplication 的 run() 方法调用](#二、SpringApplication 的 run() 方法调用)
  • [三、SpringApplication 的构造函数](#三、SpringApplication 的构造函数)
  • 四、启动流程

一、创建一个简单的 SpringBoot 项目

java 复制代码
package org.yoel.qyyboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class QyybootApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(QyybootApplication.class, args);
        Object myService = run.getBean("myService");
        System.out.println("myService = " + myService);
    }
}

二、SpringApplication 的 run() 方法调用

java 复制代码
	// 第一步
    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);
    }
	// 第三步
    public ConfigurableApplicationContext run(String... args) {
    }

三、SpringApplication 的构造函数

java 复制代码
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 初始化源集合(通常是主配置类)为空的有序集合
    this.sources = new LinkedHashSet<>();
    
    // 设置启动横幅模式为控制台输出
    this.bannerMode = Mode.CONSOLE;
    
    // 设置是否记录启动信息,默认为开启
    this.logStartupInfo = true;
    
    // 设置是否将命令行参数作为环境变量添加到上下文中,默认为开启
    this.addCommandLineProperties = true;
    
    // 设置是否自动注册类型转换服务,默认为开启
    this.addConversionService = true;
    
    // 设置是否以无头模式运行,默认为开启
    this.headless = true;
    
    // 设置是否注册关闭钩子,默认为开启
    this.registerShutdownHook = true;
    
    // 初始化额外的配置文件名集合为空集合
    this.additionalProfiles = Collections.emptySet();
    
    // 标记是否使用了自定义的环境配置,默认为否
    this.isCustomEnvironment = false;
    
    // 设置懒加载初始化标志,默认为不懒加载
    this.lazyInitialization = false;
    
    // 设置默认的应用上下文工厂
    this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
    
    // 设置默认的应用启动行为
    this.applicationStartup = ApplicationStartup.DEFAULT;
    
    // 设置资源加载器
    this.resourceLoader = resourceLoader;
    
    // 验证主源配置类不能为空
    Assert.notNull(primarySources, "PrimarySources must not be null");
    
    // 将主源配置类存储到有序集合中
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    
    // 初始化环境。环境分为三种 非web环境、web环境、reactive环境三种。其判断逻辑就是判断是否存在指定的类,默认是Servlet 环境,我们这也是Servlet
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    
    // 获取并存储所有 BootstrapRegistryInitializer 实例
    this.bootstrapRegistryInitializers = new ArrayList<>(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    
    // getSpringFactoriesInstances 方法加载了 spring.factories文件。在这里进行了首次加载spring.factoies文件。设置 ApplicationContextInitializer
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    
    // 获取监听器,也加载了spring.factories文件
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    
    // 推断主应用程序类
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

四、启动流程

java 复制代码
public ConfigurableApplicationContext run(String... args) {
    // 创建一个 SpringApplication 的 Startup 实例来跟踪启动过程
    Startup startup = SpringApplication.Startup.create();
    
    // 如果设置了注册关闭钩子,则允许添加关闭钩子
    if (this.registerShutdownHook) {
        shutdownHook.enableShutdownHookAddition();
    }

    // 创建 Bootstrap 上下文
    DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
    
    // 初始化上下文对象为 null,稍后会创建
    ConfigurableApplicationContext context = null;
    
    // 如果设置了无头模式,则配置相应的系统属性
    this.configureHeadlessProperty();
    
    // 创建并初始化 Run Listeners
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    
    // 在启动过程开始时通知 Run Listeners
    listeners.starting(bootstrapContext, this.mainApplicationClass);

    Throwable ex;
    try {
        // 解析命令行参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        
        // 准备环境配置
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        
        // 输出横幅信息
        Banner printedBanner = this.printBanner(environment);
        
        // 创建应用上下文
        context = this.createApplicationContext();
        
        // 设置上下文启动策略
        context.setApplicationStartup(this.applicationStartup);
        
        // 准备上下文,包括初始化 BeanFactory、设置环境等
        this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        
        // 刷新上下文,完成 Bean 的初始化
        this.refreshContext(context);
        
        // 上下文刷新后的操作
        this.afterRefresh(context, applicationArguments);
        
        // 启动完成后调用 Startup 的 started 方法
        startup.started();
        
        // 如果设置了记录启动信息,则记录应用启动的日志
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), startup);
        }

        // 通知 Run Listeners 启动完成
        listeners.started(context, startup.timeTakenToStarted());
        
        // 执行任何定义好的 Runner 接口实现
        this.callRunners(context, applicationArguments);
    } catch (Throwable var10) {
        // 捕获异常,用于处理启动失败的情况
        ex = var10;
        
        // 抛出处理后的异常
        throw this.handleRunFailure(context, ex, listeners);
    }

    try {
        // 如果上下文正在运行,则通知 Run Listeners 已经准备就绪
        if (context.isRunning()) {
            listeners.ready(context, startup.ready());
        }

        // 返回创建的上下文
        return context;
    } catch (Throwable var9) {
        // 捕获异常,用于处理启动成功后可能出现的问题
        ex = var9;
        
        // 抛出处理后的异常
        throw this.handleRunFailure(context, ex, (SpringApplicationRunListeners)null);
    }
}

流程详解:

1、获取监听器

java 复制代码
SpringApplicationRunListeners listeners = this.getRunListeners(args);
java 复制代码
private SpringApplicationRunListeners getRunListeners(String[] args) {
    // 创建一个 ArgumentResolver 实例,用于解析构造方法的参数
    SpringFactoriesLoader.ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this);
    
    // 使用 ArgumentResolver 添加 String[] 类型的参数,即启动时传入的命令行参数
    argumentResolver = argumentResolver.and(String[].class, args);
    
    // 从 spring.factories 文件中获取 SpringApplicationRunListener 的所有实例
    List<SpringApplicationRunListener> listeners = this.getSpringFactoriesInstances(SpringApplicationRunListener.class, argumentResolver);
    
    // 获取当前的 SpringApplicationHook 实例
    SpringApplicationHook hook = (SpringApplicationHook)applicationHook.get();
    
    // 如果存在 SpringApplicationHook,则获取其提供的 SpringApplicationRunListener 实例
    SpringApplicationRunListener hookListener = hook != null ? hook.getRunListener(this) : null;
    
    // 如果 hook 提供了 SpringApplicationRunListener,则将其添加到监听器列表中
    if (hookListener != null) {
        listeners = new ArrayList<>(listeners);
        ((List)listeners).add(hookListener);
    }

    // 创建 SpringApplicationRunListeners 实例,并传递日志记录器、监听器列表和应用启动行为
    return new SpringApplicationRunListeners(logger, (List)listeners, this.applicationStartup);
}
factories 复制代码
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

Spring启动时,通过 spring.factories 文件中获取监听器集合。默认类型为 EventPublishingRunListener。在事件发生时,EventPublishingRunListener 会寻找容器中 ApplicationListener 的bean,并进行事件通知。

2、加载环境配置

application.yml或者application.properties文件

java 复制代码
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
java 复制代码
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    // 获取或创建 ConfigurableEnvironment 实例
    ConfigurableEnvironment environment = this.getOrCreateEnvironment();
    
    // 配置环境,主要是将命令行参数应用到环境中
    this.configureEnvironment(environment, applicationArguments.getSourceArgs());
    
    // 将配置属性源附加到环境
    ConfigurationPropertySources.attach(environment);
    
    // 通知 Run Listeners 环境已准备好
    listeners.environmentPrepared(bootstrapContext, environment);
    
    // 将默认属性移动到最后,确保它们不会覆盖其他来源的属性
    DefaultPropertiesPropertySource.moveToEnd(environment);
    
    // 断言环境前缀不能通过属性设置
    Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
    
    // 将 SpringApplication 的信息绑定到环境中
    this.bindToSpringApplication(environment);
    
    // 如果不是自定义的环境,则尝试转换环境
    if (!this.isCustomEnvironment) {
        // 创建 EnvironmentConverter 实例
        EnvironmentConverter environmentConverter = new EnvironmentConverter(this.getClassLoader());
        
        // 根据需要转换环境
        environment = environmentConverter.convertEnvironmentIfNecessary(environment, this.deduceEnvironmentClass());
    }

    // 再次将配置属性源附加到环境中
    ConfigurationPropertySources.attach(environment);
    
    // 返回准备好的环境
    return environment;
}

在 listeners.environmentPrepared(environment); 时会发送环境准备事件,环境准备事件要通知七个监听器。对于 Springboot 的配置文件application.yml或者application.properties文件的加载实际上是通过发布环境准备事件完成的,完成这项功能的就是 EnvironmentPostProcessorApplicationListener

factories 复制代码
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

EnvironmentPostProcessorApplicationListener 的 onApplicationEvent()方法

java 复制代码
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent environmentPreparedEvent) {
            this.onApplicationEnvironmentPreparedEvent(environmentPreparedEvent);
        }

        if (event instanceof ApplicationPreparedEvent) {
            this.onApplicationPreparedEvent();
        }

        if (event instanceof ApplicationFailedEvent) {
            this.onApplicationFailedEvent();
        }
    }

调用下面的方法:

java 复制代码
    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        SpringApplication application = event.getSpringApplication();
        Iterator var4 = this.getEnvironmentPostProcessors(application.getResourceLoader(), event.getBootstrapContext()).iterator();

        while(var4.hasNext()) {
            EnvironmentPostProcessor postProcessor = (EnvironmentPostProcessor)var4.next();
            postProcessor.postProcessEnvironment(environment, application);
        }

    }
java 复制代码
public interface EnvironmentPostProcessor {
    void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application);
}

有8个实现类

3、创建上下文

java 复制代码
context = this.createApplicationContext();
java 复制代码
    protected ConfigurableApplicationContext createApplicationContext() {
        return this.applicationContextFactory.create(this.webApplicationType);
    }

有三个上下文创建工厂

java 复制代码
// 创建的ServletWebServerApplicationContext
    private ConfigurableApplicationContext createContext() {
        return (ConfigurableApplicationContext)(!AotDetector.useGeneratedArtifacts() ? new AnnotationConfigServletWebServerApplicationContext() : new ServletWebServerApplicationContext());
    }

4. 上下文准备

java 复制代码
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
java 复制代码
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    // 设置环境到应用上下文中
    context.setEnvironment(environment);
    
    // 执行自定义的应用上下文后处理器
    this.postProcessApplicationContext(context);
    
    // 如果有必要,添加AOT(Ahead-Of-Time)生成的初始化器
    this.addAotGeneratedInitializerIfNecessary(this.initializers);
    
    // 应用所有注册的初始化器
    this.applyInitializers(context);
    
    // 通知监听器上下文已经准备好了
    listeners.contextPrepared(context);
    
    // 关闭引导上下文并传递应用上下文
    bootstrapContext.close(context);
    
    // 如果允许打印启动信息,则打印
    if (this.logStartupInfo) {
        // 打印启动信息,如果父上下文不存在则更详细
        this.logStartupInfo(context.getParent() == null);
        
        // 打印激活的配置文件信息
        this.logStartupProfileInfo(context);
    }

    // 获取应用上下文中的bean工厂
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    
    // 注册applicationArguments为单例bean
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    
    // 如果有打印的banner,则也注册为单例bean
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }

    // 如果bean工厂支持自动装配能力,则设置是否允许循环依赖
    if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {
        autowireCapableBeanFactory.setAllowCircularReferences(this.allowCircularReferences);
        
        // 如果是默认的bean工厂,则设置是否允许覆盖bean定义
        if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {
            listableBeanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
    }

    // 如果是懒加载模式,则添加懒加载的bean工厂后处理器
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }

    // 如果需要保持存活,则添加KeepAlive应用监听器
    if (this.keepAlive) {
        context.addApplicationListener(new KeepAlive());
    }

    // 添加一个用于处理属性源顺序的bean工厂后处理器
    context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
    
    // 如果没有使用AOT生成的类,则加载所有的配置源
    if (!AotDetector.useGeneratedArtifacts()) {
        Set<Object> sources = this.getAllSources();
        // 断言配置源不为空
        Assert.notEmpty(sources, "Sources must not be empty");
        
        // 加载配置源
        this.load(context, sources.toArray(new Object[0]));
    }

    // 通知监听器上下文已经加载完毕
    listeners.contextLoaded(context);
}

其中 loader.load(); 会跳转到 BeanDefinitionLoader#load(java.lang.Class<?>) 方法中。

java 复制代码
    private void load(Class<?> source) {
        if (this.isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
            GroovyBeanDefinitionSource loader = (GroovyBeanDefinitionSource)BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
            ((GroovyBeanDefinitionReader)this.groovyReader).beans(loader.getBeans());
        }

        if (this.isEligible(source)) {
            this.annotatedReader.register(new Class[]{source});
        }

    }
java 复制代码
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier, @Nullable BeanDefinitionCustomizer[] customizers) {
    // 创建一个新的AnnotatedGenericBeanDefinition实例,该实例描述了Bean的元数据
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);

    // 使用ConditionEvaluator检查BeanDefinition的条件注解,确定是否应该跳过此Bean的注册
    if (!this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
        // 标记此BeanDefinition为候选Bean
        abd.setAttribute(ConfigurationClassUtils.CANDIDATE_ATTRIBUTE, Boolean.TRUE);

        // 设置Bean的实例化策略
        abd.setInstanceSupplier(supplier);

        // 解析作用域元数据
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);

        // 设置Bean的作用域
        abd.setScope(scopeMetadata.getScopeName());

        // 生成或使用提供的Bean名称
        String beanName = name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry);

        // 处理一些通用的Bean定义注解,如@DependsOn, @DependsFor等
        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

        // 如果提供了限定符注解数组
        if (qualifiers != null) {
            Class<? extends Annotation>[] var9 = qualifiers;
            int var10 = qualifiers.length;

            // 遍历限定符注解
            for(int var11 = 0; var11 < var10; ++var11) {
                Class<? extends Annotation> qualifier = var9[var11];

                // 如果限定符注解是@Primary,则标记此Bean为首选Bean
                if (Primary.class == qualifier) {
                    abd.setPrimary(true);
                } else if (Lazy.class == qualifier) {
                    // 如果限定符注解是@Lazy,则设置此Bean为延迟初始化
                    abd.setLazyInit(true);
                } else {
                    // 否则,创建一个新的AutowireCandidateQualifier实例并添加到BeanDefinition
                    abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                }
            }
        }

        // 如果提供了BeanDefinitionCustomizer数组
        if (customizers != null) {
            BeanDefinitionCustomizer[] var13 = customizers;
            int var10 = customizers.length;

            // 遍历定制器并应用它们
            for(int var11 = 0; var11 < var10; ++var11) {
                BeanDefinitionCustomizer customizer = var13[var11];
                customizer.customize(abd);
            }
        }

        // 创建一个BeanDefinitionHolder实例,包含BeanDefinition和Bean名称
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);

        // 应用作用域代理模式
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

        // 注册BeanDefinition到BeanFactory
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    }
}

这个方法的主要功能包括:

  1. 创建一个新的AnnotatedGenericBeanDefinition来描述Bean的定义。
  2. 检查条件注解,决定是否应该跳过Bean的注册。
  3. 设置Bean的实例化策略。
  4. 设置Bean的作用域。
  5. 生成或使用提供的Bean名称。
  6. 处理通用的Bean定义注解。
  7. 根据提供的限定符注解设置Bean的优先级和初始化策略。
  8. 应用任何提供的BeanDefinitionCustomizer。
  9. 创建BeanDefinitionHolder,并应用作用域代理模式。
  10. 将BeanDefinition注册到Spring的BeanFactory中。
相关推荐
计算机徐师兄25 分钟前
Java基于SSM框架的无中介租房系统小程序【附源码、文档】
java·微信小程序·小程序·无中介租房系统小程序·java无中介租房系统小程序·无中介租房微信小程序
源码哥_博纳软云26 分钟前
JAVA智慧养老养老护理帮忙代办陪诊陪护小程序APP源码
java·开发语言·微信小程序·小程序·微信公众平台
大梦百万秋1 小时前
Spring Boot实战:构建一个简单的RESTful API
spring boot·后端·restful
忒可君1 小时前
C# winform 报错:类型“System.Int32”的对象无法转换为类型“System.Int16”。
java·开发语言
斌斌_____2 小时前
Spring Boot 配置文件的加载顺序
java·spring boot·后端
路在脚下@2 小时前
Spring如何处理循环依赖
java·后端·spring
一个不秃头的 程序员2 小时前
代码加入SFTP JAVA ---(小白篇3)
java·python·github
丁总学Java2 小时前
--spring.profiles.active=prod
java·spring
苹果醋33 小时前
React系列(八)——React进阶知识点拓展
运维·vue.js·spring boot·nginx·课程设计
上等猿3 小时前
集合stream
java