«Интервью» Что сделал SpringBoot?

интервью
«Интервью» Что сделал 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, что удобно для последующего использования.

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.
			}
		}
	}

конец

На этом этапе инициализация контейнера ioc завершена, springboot успешно запущен, и некоторые другие точки расширения (например, CommandLineRunner) вызываются для завершения определяемого пользователем процесса инициализации.

Увидимся!