SpringBoot启动流程总结

1.导语

//以下是一个SpringBoot应用的标准入口:
public static void main(String[] args) {
    SpringApplication.run(TestApplication.class, args);
}

//SpringApplication.run 内部是这样的:先创建一个 SpringApplication 对象,然后调用他的run方法。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

 所以想要了解清楚 SpringBoot 启动流程,相当于了解 SpringApplication 的 构造方法 和 run方法。

 

2.构造方法详解

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

    // 判断应用类型,会有三种情况:
    // NONE:非web应用
    // SERVLET:普通web应用
    // REACTIVE:响应式web应用
    this.webApplicationType = WebApplicationType.deduceFromClasspath();

    // 从 META-INF/spring.factories 文件中获取如下内容
    // 获取 注册初始化器:在 run 方法中的 createBootstrapContext(); 里会被使用
    this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    // 获取 容器初始化器:在 run 方法中的 prepareContext( ... ); 里会被使用
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 获取 监听器:在 run 方法中 listeners.xxx 时被使用
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

    this.mainApplicationClass = deduceMainApplicationClass();
}

 

3.run方法详解

public ConfigurableApplicationContext run(String... args) {
    long startTime = System.nanoTime();
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    configureHeadlessProperty();

    // 第一步:创建监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);

    // 发布事件:开始启动
    listeners.starting(bootstrapContext, this.mainApplicationClass);

    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

        // 第二步:准备环境变量
        // 把一系列设置整合成 environment,包括 系统变量、JVM参数、启动参数(args)、配置文件(application、bootstrap等)
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

        Banner printedBanner = printBanner(environment);

        // 第三步:创建空容器
        // 根据 new 时判断的 web类型(webApplicationType),创建对应类型的容器
        context = createApplicationContext();

        context.setApplicationStartup(this.applicationStartup);

        // 第四步:准备容器
        // 把前面准备的东西注入进去,包括 环境变量(environment)、事件监听器(listeners)、启动参数(applicationArguments)
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

        // 第五步:启用容器
        // 解析@SpringBootApplication注解,加载自动配置类
        // 进行Bean的扫描、加载、实例化、依赖注入
        // 如果是Web应用,会启动一个内嵌式的Web服务器比如Tomcat
        refreshContext(context);

        // 容器初始化后的操作,是个空方法,要使用需要继承并重写
        afterRefresh(context, applicationArguments);

        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
        }

        // 发布事件:已启动
        listeners.started(context, timeTakenToStartup);

        // 执行启动后的自定方法:
        // 实现 ApplicationRunner、CommandLineRunner 接口
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        if (ex instanceof AbandonedRunException) {
            throw ex; 
        }
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }
    
    try {
        // 检查应用是否还在运行
        if (context.isRunning()) {
            Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
            // 发布事件:准备就绪
            listeners.ready(context, timeTakenToReady);
        }
    }
    catch (Throwable ex) {
        if (ex instanceof AbandonedRunException) {
            throw ex;
        }
        handleRunFailure(context, ex, null);
        throw new IllegalStateException(ex);
    }
    // 返回Bean容器
    return context;
}

 

posted @ 2024-05-31 03:16  Yfeil  阅读(109)  评论(0编辑  收藏  举报