1.调用SpringApplication.run启动springboot应用
@SpringBootApplication
public class SpringbootMdcApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMdcApplication.class, args);
}
}
2.使用自定义SpringApplication进行启动
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
3.创建SpringApplication
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//将启动类放入primarySources
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 根据classpath 下的类,推算当前web应用类型(webFlux, servlet)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 就是去spring.factories 中去获取所有key:org.springframework.context.BootstrapRegistryInitializer
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection)
// 就是去spring.factories 中去获取所有key:org.springframework.context.ApplicationContextInitializer
getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection)
// 就是去spring.factories 中去获取所有key:org.springframework.context.ApplicationListener
getSpringFactoriesInstances(ApplicationListener.class));
//根据main函数推断mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass();
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
org.springframework.context.ApplicationContextInitializer
org.springframework.context.ApplicationListener
#总结
1.获取启动类
2.获取web应用类型
3.读取了对外扩展的ApplicationContextInitializer ,ApplicationListener
4. 根据main推算出所在的类
SpringBoot 事件监听器发布顺序
1.ApplicationStartingEvent在运行开始时发送,但在进行任何处理之前(侦听器和初始化程序的注册除外)发送。
2.在创建上下文之前,将发送ApplicationEnvironmentPreparedEvent。
3.准备ApplicationContext并调用ApplicationContextInitializers之后,将发送ApplicationContextInitializedEvent。
4.读取完配置类后发送ApplicationPreparedEvent。
5.在刷新上下文之后但在调用任何应用程序和命令行运行程序之前,将发送ApplicationStartedEvent。
6.紧随其后发送带有LivenessState.CORRECT的AvailabilityChangeEvent,以指示该应用程序被视为处于活动状态。
7.在调用任何应用程序和命令行运行程序之后,将发送ApplicationReadyEvent。
8.紧随其后发送ReadabilityState.ACCEPTING_TRAFFIC的AvailabilityChangeEvent,以指示应用程序已准备就绪,可以处理请求。如果启动时发生异常,则发送ApplicationFailedEvent
4.run方法
这个方法是启动springboot最核心的逻辑
public ConfigurableApplicationContext run(String... args) {
// 用来记录当前springboot启动耗时
StopWatch stopWatch = new StopWatch();
// 就是记录了启动开始时间
stopWatch.start();
// 它是任何spring上下文的接口, 所以可以接收任何ApplicationContext实现
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 开启了Headless模式:
configureHeadlessProperty();
// 去spring.factroies中读取了SpringApplicationRunListener 的组件, 就是用来发布事件或者运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布1.ApplicationStartingEvent事件,在运行开始时发送
listeners.starting();
try {
// 根据命令行参数 实例化一个ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 预初始化环境: 读取环境变量,读取配置文件信息(基于监听器)
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 忽略beaninfo的bean
configureIgnoreBeanInfo(environment);
// 打印Banner 横幅
Banner printedBanner = printBanner(environment);
// 根据webApplicationType创建Spring上下文
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//预初始化spring上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 加载spring ioc 容器 相当重要 由于是使用AnnotationConfigServletWebServerApplicationContext 启动的spring容器所以springboot对它做了扩展:
// 加载自动配置类:invokeBeanFactoryPostProcessors , 创建servlet容器onRefresh
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
准备环境 prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 根据webApplicationType 创建Environment 创建就会读取: java环境变量和系统环境变量
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 将命令行参数读取环境变量中
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 将@PropertieSource的配置信息 放在第一位, 因为读取配置文件@PropertieSource优先级是最低的
ConfigurationPropertySources.attach(environment);
// 发布了ApplicationEnvironmentPreparedEvent 的监听器 读取了全局配置文件
listeners.environmentPrepared(environment);
// 将所有spring.main 开头的配置信息绑定SpringApplication
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//更新PropertySources
ConfigurationPropertySources.attach(environment);
return environment;
}
预初始化上下文 prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
// 拿到之前读取到所有ApplicationContextInitializer的组件, 循环调用initialize方法
applyInitializers(context);
// 发布了ApplicationContextInitializedEvent
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 获取当前spring上下文beanFactory (负责创建bean)
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 在Spring下 如果出现2个重名的bean, 则后读取到的会覆盖前面
// 在SpringBoot 在这里设置了不允许覆盖, 当出现2个重名的bean 会抛出异常
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 设置当前spring容器是不是要将所有的bean设置为懒加载
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 读取主启动类,将它注册为BD、就像我们以前register(启动类);一个意思 (因为后续要根据配置类解析配置的所有bean)
load(context, sources.toArray(new Object[0]));
//4.读取完配置类后发送ApplicationPreparedEvent。
listeners.contextLoaded(context);
}
刷新 调用ServletWebServerApplicationContext refresh 之后就会调用AbstractApplicationContext的refresh方法
private void refreshContext(ConfigurableApplicationContext context) {
refresh((ApplicationContext) context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
总结:
- 初始化SpringApplication 从spring.factories 读取 listener ApplicationContextInitializer 。
- 运行run方法
- 读取 环境变量 配置信息…
- 创建springApplication上下文:ServletWebServerApplicationContext
- 预初始化上下文 : 读取启动类
- 调用refresh 加载ioc容器,加载所有的自动配置类,创建servlet容器
在这个过程中springboot会调用很多监听器对外进行扩展