__Лучшего нет, есть только правильный
предисловие
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) вызываются для завершения определяемого пользователем процесса инициализации.
Увидимся!