SpringBoot启动原理和源码初步刨析
1、SpringBoot启动的概念、流程
SpringBoot并没有抛弃Spring,只是在Spring上面又维护了一些更方便的东西,底层还是Spring,类似于下面的图所示:
2、SpringBoot启动源码初步刨析
1、SpringBoot的启动原理,我们肯定是要从下面这个方法进入的:
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisApplication.class, args);
}
2、进入run方法之后,代码如下所示:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 先new一个主配置类,然后再运行run方法
return (new SpringApplication(primarySources)).run(args);
}
2.1、初始化SpringApplication类
1、当我们点击SpringApplication的时候,会发现调用了一个构造方法,如下所示:
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
2、点击this进入之后,代码如下:
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));
// 根据classPath下面的类,推算当前 web 应用
// 推算当前web应用类型(webFlux,servlet)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
// 去 classpath路径下找 spring.factories 文件中获取key:org.springframework.context.ApplicationContextInitializer
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 去 classpath路径下找 spring.factories 文件中获取key:org.springframework.context.ApplicationListener
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 根据main方法推算出mainApplicationClass
this.mainApplicationClass = this.deduceMainApplicationClass();
}
上面获取的org.springframework.context.ApplicationContextInitializer
上面获取的org.springframework.context.ApplicationListener
总结:
实例化SpringApplication类的步骤
1、获取启动类放在this.primarySources中
2、获取web应用类型
3、读取了对外扩展的ApplicationContextInitializer和ApplicationListener
4、根据main推算出所在的类
2.2、执行run方法
1、点击进入run方法,代码如下所示:
public ConfigurableApplicationContext run(String... args) {
// 记录SpringBoot的启动耗时时间
StopWatch stopWatch = new StopWatch();
// 启动起始时间
stopWatch.start();
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
// 它是任何 spring 上下文的接口,所以可以接收任何 ApplicationContext 的实现
ConfigurableApplicationContext context = null;
// 开启了 Headless 模式,
// 这个Headless相当于应用部署到了某个服务器上时需要依赖于某些硬件设备
// 但是现在这个服务器上又缺少某个硬件设备,Headless就会提供一些基本的映射配置
this.configureHeadlessProperty();
// 去 classpath路径下找 spring.factories 文件中获取key:org.springframework.boot.SpringApplicationRunListener
// 启动监听器所用
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 启动监听器/发布器
// 跟进去启动了 ApplicationStartingEvent 事件监听器,走2.2.1
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 根据命令行参数 实例化一个ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 预初始化环境:读取环境变量,读取配置文件信息(基于监听器)
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 忽略实现了 BeanInfo 接口的Bean
this.configureIgnoreBeanInfo(environment);
// 打印Banner横幅
Banner printedBanner = this.printBanner(environment);
// 根据 webApplicationType 创建Spring上下文
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 预初始化上下文,走2.2.3
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 加载Spring Ioc容器,springBoot在Spring上做了扩展
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// SpringBoot第五、六个公告
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, listeners);
throw new IllegalStateException(var10);
}
try {
// SpringBoot的第七、八个公告
listeners.running(context);
return context;
} catch (Throwable var9) {
// SpringBoot的第九个公告
this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
2.2.1、listeners.starting
1、进入listeners.starting中,跟到了这里,找到了发布ApplicationStartingEvent事件监听器的地方。
public void starting(ConfigurableBootstrapContext bootstrapContext) {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
2、进入到multicastEvent,发布的代码如下:
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Executor executor = this.getTaskExecutor();
Iterator var5 = this.getApplicationListeners(event, type).iterator();
// 获取到当前的事件监听器,对所有的监听方法进行回调
while(var5.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var5.next();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
this.invokeListener(listener, event);
}
}
}
2.2.2、this.prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 根据 webApplicationType 创建 Environment,创建就会读取:Java环境变量和系统变量
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
// 将上一步拿出来的命令行参数读取到环境变量中
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
// 将@PropertiesSource的配置信息 放在第一个,
// 因为读取配置文件@PropertiesSource的优先级最低,后面的会覆盖前面的
ConfigurationPropertySources.attach((Environment)environment);
// ApplicationEnvironmentPreparedEvent公告的发布
listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment);
Assert.state(!((ConfigurableEnvironment)environment).containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
// 将spring.main配置开头的全局配置绑定到当前的SpringApplication类中
this.bindToSpringApplication((ConfigurableEnvironment)environment);
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
// 更新@PropertiesSource
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
2.2.3、this.prepareContext
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
// 拿到之前读取的所有 ApplicationContextInitializer 组件
// 循环调用 initialize 方法
this.applyInitializers(context);
// 发布的第三个公告,ApplicationContextInitializedEvent
listeners.contextPrepared(context);
bootstrapContext.close(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
// 获取当前spring上下文beanFactory(负责创建Bean)
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 在Spring下 如果出现两个重名的 Bean,则后面的会覆盖掉前面的
// SpringBoot中,在这里设置了不允许覆盖,如果出现重名,会抛出异常
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 设置当前spring容器是不是要将所有的bean设置为懒加载
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 读取主启动类,加载配置
this.load(context, sources.toArray(new Object[0]));
// SpringBoot的第四个公告
listeners.contextLoaded(context);
}
发布的公告顺序
1、ApplicationStartingEvent运行开始时发送,但在进行任何处理之前发送。
默认的监听器
2、ApplicationEnvironmentPreparedEvent,在创建上下文之前。
默认的监听器
3、ApplicationContextInitializedEvent,准备ApplicationContext并调用ApplicationContextInitializer 之后。
4、ApplicationPreparedEvent,读取完配置类之后发送。
5、ApplicationStartedEvent,在加载完ioc容器之后。
6、AvailabilityChangeEvent,以指示该程序为活动状态。
7、ApplicationReadyEvent,调用任何程序和命令行运行程序之后。
8、AvailabilityChangeEvent,以指示程序已准备就绪,可以处理请求
9、ApplicationFailedEvent,如果启动时报错,则发送。