SpringBoot启动过程--run()方法在启动中做了什么事(SpringBoot 2.7.x)

210 阅读4分钟

SpringBoot在Spring的基础上,提供了自动配置。从之前的文章,我们知道了,SpringBoot在构建SpringApplication的时候,会从META-INF/spring.factories里面去加载定义的实现类。那我们从SpringBoot的run()方法入手,看下在启动的过程中,Springboot又做了哪些事。

SpringApplication#run()

public ConfigurableApplicationContext run(String... args) {
		long startTime = System.nanoTime();
		// 创建一个Bootstrap的上下文
		// 实现了BootstrapRegistryInitializer接口的类会在这里被遍历,执行initialize()方法,最后构建出DefaultBootstrapContext
		// 下面会给出createBootstrapContext()的内部实现
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;
		// 配置java服务在无头模式运行(没有图形界面)
		configureHeadlessProperty();
		// 加载实现了SpringApplicationRunListener.class的类,并且获取他们的实例化对象。其实就是在run()这个过程中事件的监听器		// 方法内部是调用我们的老朋友getSpringFactoriesInstances()
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 内部确认启动的步骤,然后遍历listeners里的监听器,执行他们的starting(bootstrapContext)方法
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
		   // 包装传入的args为ApplicationArguments对象
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 准备环境,里面会存一些环境变量
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			// 创建Application上下文,默认是AnnotationConfigApplicationContext
			// 内部是通过SpringFactoriesLoader.loadFactories加载ApplicationContextFactory.class的实现类
			// 然后根据WebApplicationType确定返回的上下文
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
			// 准备上下文,其实也就是对上下文的一些补充以及执行一些上下文的初始化和调用一下监听
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
			// 刷新上下文
			refreshContext(context);
			// 开始进入bean的创建,内部其实还是走的Spring
                        // 这里执行完,bean就被创建好了
			afterRefresh(context, applicationArguments);
			// 计算启动时间
			Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
			}
			// 启动监听器
			listeners.started(context, timeTakenToStartup);
			// Springboot启动后,执行ApplicationRunner和CommandLineRunner实现类的run方法
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}
		try {
			Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
			listeners.ready(context, timeTakenToReady);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

createBootstrapContext()方法中,我们可以看到,创建了一个DefaultBootstrapContext,然后遍历BootstrapRegistryInitializer的实现类,其实可以看出来,这里是一个组装DefaultBootstrapContext的过程。

private DefaultBootstrapContext createBootstrapContext() {
      // 里面维护着一些BootstrapContextClosedEvent的监听器,和InstanceSupplier的实现等
		DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
		// 可以通过遍历bootstrapRegistryInitializers把需要的事件注册进DefaultBootstrapContext上下文中
		this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
		return bootstrapContext;
	}

这里稍微解释一下listeners.starting方法,这个方法的内部,入参是俩Lambda表达式,不是特别好阅读

void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
    // 这里传入了俩lambda表达式
    // 第一个执行listener.startin()方法,把bootstrapContext上下文传进去
    // 第二个参数则是执行step的tag方法
		doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
				(step) -> {
					if (mainApplicationClass != null) {
						step.tag("mainApplicationClass", mainApplicationClass.getName());
					}
				});
	}
	//doWithListeners方法的实现
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
			Consumer<StartupStep> stepAction) {
		//获取启动步骤
		StartupStep step = this.applicationStartup.start(stepName);
		//遍历listeners,执行传入的lambda表达式,也就是:(listener) -> listener.starting(bootstrapContext)
		this.listeners.forEach(listenerAction);
		if (stepAction != null) {
		  // 执行stepAction
			stepAction.accept(step);
		}
		step.end();
	}

结论

我们从上面的流程可以看到,其实上面的流程主要是围绕ConfigurableApplicationContext这个上下文进行的。在方法中对上下文进行了组装,流程中的refreshContext()方法还执行了ConfigurableApplicationContext内部的refresh()方法,去进行bean的创建。其实整个启动过程,就是ConfigurableApplicationContext的组装和执行的过程。通过new SpringApplication(),加载META-INF/spring.factories中我们配置类,监听器,初始化器等实现类,放入ConfigurableApplicationContext中。让这些类可以在Spring的初始化过程中,被加载。然后通过ConfigurableApplicationContext去创建对应的bean (ps:这里的ConfigurableApplicationContext代指实现了这个接口的类)

  • 那么ConfigurableApplicationContext和BootstrapContext这两个上下文有什么区别?
    • BootstrapContext是Springboot包下的,记录着Springboot项目启动时的上下文信息
    • ConfigurableApplicationContext是Spring的context包下的,记录着Spring启动过程中的上下文信息

Springboot使用的过程中,我们大多用注解的形式定义我们的配置类,故Springboot默认使用的上下文实现就是AnnotationConfigApplicationContext.class(这里指2.7.x,具体从什么版本开始没有去考究了)

这里贴一张AnnotationConfigApplicationContext.class的关系图 image.png

  • 可以看到,我们的context实现了BeanDefinitionRegistry
    • 后续的BeanDefinition,就会注册到context里面的DefaultListableBeanFactory中的beanDefinitionMap里

这里介绍了一下SpringApplication#run()方法主干流程中干了什么,后续会对里面的方法,进行详细的讲解

prepareContext()方法解析