Bootstrap

【SpringBoot源码解析】——SpringBoot应用是如何运行起来的?

SpringBoot的主程序类

描述
在这里插入图片描述

SpringApplication的静态方法run() 会做两件事情:

  1. 通过构造方法创建SpringApplication对象 ,并在构造方法中完成初始化在这里插入图片描述
//先调用这个构造方法
public SpringApplication(Class<?>... primarySources) {
        this((ResourceLoader)null, primarySources);
    }
//再加上资源加载器,间接调用这个构造方法(初始化操作!)
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");
        // 在primarySources不为空时(这里是指主程序类的类对象),保存这个主程序类的类对象(是个配置类)
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        // 判断web应用的类型(这里是Servlet)
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //获取BootstrapRegistryInitializer类实例,用于初始化各种BootstrapRegistry
        //从"META-INF/spring.factories"中读取key为BootstrapRegistryInitializer类型的扩展点,
        //并实例化出对应扩展点对象
        //BootstrapRegistry对象的作用:
		//可以用来注册一些对象,这些对象可以用在从SpringBoot启动到Spring容器初始化完成的过程中
		//可以这样理解:没有Spring容器之前就利用BootstrapRegistry来共享一些对象,
		//有了Spring容器之后就利用Spring容器来共享一些对象
        this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
        // 获取并保存"容器初始化类",通常在web应用容器初始化使用
        // 利用loadFactoryNames方法从路径MEAT-INF/spring.factories中找到所有的ApplicationContextInitializer(容器初始化类)
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 获取并保存监听器
    	// 利用loadFactoryNames方法从路径MEAT-INF/spring.factories中找到所有的ApplicationListener
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        // 从堆栈信息获取包含main方法的主配置类
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
这样就成功创建了一个SpringApplication对象,并且完成了初始化!
  1. 利用创建好的SpringApplication对象调用更底层的run方法:
    准备spring的上下文,完成容器的初始化,创建,加载等。会在不同的时机触发监听器的不同事件。
    在这里插入图片描述
public ConfigurableApplicationContext run(String... args) {
        if (this.registerShutdownHook) {
            shutdownHook.enableShutdowHookAddition();
        }

        long startTime = System.nanoTime();
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        // 配置属性
        this.configureHeadlessProperty();
        // 获取监听器
    	// 利用loadFactoryNames方法从路径MEAT-INF/spring.factories中找到所有的SpringApplicationRunListener
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        // 启动监听
    	// 调用每个SpringApplicationRunListener的starting方法
        listeners.starting(bootstrapContext, this.mainApplicationClass);

        try {
        	// 将参数封装到ApplicationArguments对象中
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 准备环境
        	// 触发监听事件——调用每个SpringApplicationRunListener的environmentPrepared方法
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            // 从环境中取出Banner并打印
            Banner printedBanner = this.printBanner(environment);
            // ⭐依据是否为web环境创建web容器或者普通的IOC容器
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            // 准备上下文
        	// 1.将environment保存到容器中
        	// 2.触发监听事件——调用每个SpringApplicationRunListeners的contextPrepared方法
        	// 3.调用ConfigurableListableBeanFactory的registerSingleton方法向容器中注入applicationArguments与printedBanner
        	// 4.触发监听事件——调用每个SpringApplicationRunListeners的contextLoaded方法
          	this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
          	// 刷新容器,完成组件的扫描,创建,加载等
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
            }

            listeners.started(context, timeTakenToStartup);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var12) {
            if (var12 instanceof AbandonedRunException) {
                throw var12;
            }

            this.handleRunFailure(context, var12, listeners);
            throw new IllegalStateException(var12);
        }

        try {
            if (context.isRunning()) {
                Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
                listeners.ready(context, timeTakenToReady);
            }
			// ⭐返回容器
            return context;
        } catch (Throwable var11) {
            if (var11 instanceof AbandonedRunException) {
                throw var11;
            } else {
                this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
                throw new IllegalStateException(var11);
            }
        }
    }

总结:
SpringApplication.run一共做了两件事

  1. 创建SpringApplication对象;在对象初始化时保存事件监听器,容器初始化类以及判断是否为web应用,保存包含main方法的主配置类。
  2. 调用run方法;准备spring的上下文,完成容器的初始化,创建,加载等。会在不同的时机触发监听器的不同事件。
;