转载

《面试》SpringBoot启动做了哪些事?

SpringBoot是现在使用是如此的广泛,几乎所有的后端面试官都会就它的使用和基本原理方面进行考察。

现在就来盘一盘springboot的启动流程!

SpringBoot启动拢共分几步

回答这个问题只需要查看SpringApplication.class就行了,下面截取了主要启动代码片段。

public ConfigurableApplicationContext run(String... args) {
      ...
	try {
	    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
	    // 1. 准备环境变量
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
	    configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
	   // 2. 准备上下文、主要是创建上下文和扩展点调用
        context = createApplicationContext();
	    exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);  
          // 3. 刷新上下文
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
		}
			listeners.started(context);
        // 这里调用了CommandLineRunner.class和ApplicationRunner.class所以我们写的Runner才能自动执行呢
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}
      ...
		return context;
	}
复制代码

1. 准备Environment

把各种渠道配置的变量都整合到Environment对象中,方便后面的使用。

springboot通过事件机制把属性配置进行了解耦,可以方便的扩展其他的属性配置方式。

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
	// 首先创建Environment对象 
	ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 根据main中输入的参数配置变量
	configureEnvironment(environment,applicationArguments.getSourceArgs());
    // 发送ApplicationEnvironmentPreparedEvent事件
    // 他会通知其他监听者完成属性的配置
    // 比如通知BootstrapApplicationListener完成springcloud配置文件的环境配置
    // 通知ConfigFileApplicationListener完成application.yml文件的读取配置 
	listeners.environmentPrepared(environment);
    // 绑定"spring.main"为当前的application
	bindToSpringApplication(environment);
	if (!this.isCustomEnvironment) {
    // 如果自定义Environment就转换成StandardEnvironment
	environment = new EnvironmentConverter(getClassLoader())
					.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
	}
	ConfigurationPropertySources.attach(environment);
	return environment;
}
复制代码

2. 创建并刷新上下文

在容器初始化之前做一些工作,如ApplicationContextInitializer的调用,事件发送。

private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
	context.setEnvironment(environment);
     // 预处理,如果beanNameGenerator、resourceLoader不为空就将其添加到上下文中,并将ConversionService也添加到上下文中
	postProcessApplicationContext(context);
     // 调用所有的ApplicationContextInitializer,在刷新上下文之前做一些可能的工作。
	applyInitializers(context);
     // 发送ApplicationContextInitializedEvent事件
     listeners.contextPrepared(context);
	if (this.logStartupInfo) {
		logStartupInfo(context.getParent() == null);
		logStartupProfileInfo(context);
	}
	   ... 
	// Load the sources
	Set<Object> sources = getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	load(context, sources.toArray(new Object[0]));
	listeners.contextLoaded(context);
}
复制代码

刷新上下文完成ioc容器的初始化!

private void refreshContext(ConfigurableApplicationContext context) {
      // 调用AbstractApplicationContext.refresh()完成ioc容器的初始化 
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}
复制代码
原文  https://juejin.im/post/5e66387ef265da574f354ece
正文到此结束
Loading...