注:本次基于SpringBoot 2.3.7.RELEASE版本。
入口
找一个Spring Boot项目,打开启动类,就会看到类似如下的代码,最好提前把source都下载下来。
@SpringBootApplication
public class StartApp {
public static void main(String[] args) {
SpringApplication.run(StartApp.class, args);
}
}
在IDE中点击run()方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
继续点击run()方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 创建SpringApplication对象,并调用其run方法
// 1.先看下构造方法中的逻辑
// 2.然后再看run方法的逻辑
return new SpringApplication(primarySources).run(args);
}
点击SpringApplication,进入构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 默认传进来的为空
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 设置主方法的配置类名称
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 设置当前项目的类型,deduceFromClasspath()方法比较简单,
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 加载配置在spring.factories文件中的ApplicationContextInitializer对应的类型并实例化, 并将加载的数据设置到initializers
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 加载配置在spring.factories文件中的ApplicationListener对应的类型并实例化,并将加载的数据设置到listeners。
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断main方法所在的Class对象 并设置mainApplicationClass,deduceMainApplicationClass()就是通过StackTrace判断哪个类里面包含mian方法
this.mainApplicationClass = deduceMainApplicationClass();
}
跳回到SpringApplication类的run()方法
public ConfigurableApplicationContext run(String... args) {
// 创建秒表
StopWatch stopWatch = new StopWatch();
//秒表启动
stopWatch.start();
//创建ConfigurableApplicationContext 对象
ConfigurableApplicationContext context = null;
//创建SpringBootExceptionReporter容器,保存启动错误的回调接口
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置java.awt.headless的系统属性,即使没有检测到显示器,也允许其启动.对于服务器来说,是不需要显示器的,所以要这样设置.
configureHeadlessProperty();
// 加载配置在spring.factories文件中的SpringApplicationRunListeners 对应的类型并实例化和排序,并将加载的数据设置到listeners。
SpringApplicationRunListeners listeners = getRunListeners(args);
//遍历启动所有的监听器SpringApplicationRunListener
listeners.starting();
try {
// 创建应用程序的参数持有类
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 创建并配置环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 配置需要忽略的BeanInfo信息
configureIgnoreBeanInfo(environment);
// 打印Banner信息
Banner printedBanner = printBanner(environment);
// 根据应用类型(在创建SpringApplication那一步里面已经设置成功)创建应用上下文对象
context = createApplicationContext();
// 加载配置在spring.factories文件中的SpringBootExceptionReporter对应的类型并实例化,并将加载的数据设置到exceptionReporters。 不同的配置getSpringFactoriesInstances()会被重复调用。
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 刷新前操作,里面包含了注册启动需要的单例Bean(springApplicationArguments、springBootBanner)
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新应用上下文 完成Spring容器的初始化,包含启动内置容器,里面会执行Spring的AbstractApplicationContext类refresh(),下面会有详细介绍,refresh()就是在Spring启动过程中最重要的一步,扫描到系统中所有要加载的Bean,然后解析Bean之间的依赖,最后实例化Bean存储在BeanFactory中。下面专门补充了大概的解释。
refreshContext(context);
// 刷新后操作,空操作,模板方法
afterRefresh(context, applicationArguments);
// 秒表停止
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//监听器启动
listeners.started(context);
//调用ApplicationRunner
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;
}
Spring的AbstractApplicationContext类refresh()方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 忽略掉不重要!启动阶段标记,初始化时标记位为false,执行refresh完毕后修改为true(最后的end方法).
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 1. 准备刷新上下文方法,用于设置准备刷新前的一些参数:
// 程序启动标志位/上下文拓展资源加载/上下文环境准备情况验证/监听器监听事件容器初始化准备
prepareRefresh();
// 2. 获取BeanFactory,内部调用refreshBeanFactory()和getBeanFactory()均由子类实现
// 告知子类刷新Bean工厂(设置序列号ID--> 参考GenericApplicationContext)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 初始化Bean工厂,设置基础属性: ClassLoader、SPEL(SPring表达式)解析器、属性编辑器(自定义属性覆盖默认属性)、
// 添加系统BeanPostProcessor`ResourceEditorRegistrar(初始化前执行一些Aware即invokeAwareInterfaces方法)`、
// 忽略一些系统级接口装配依赖、注入一些不能自动创建的Bean依赖(Bean工厂,ResourceLoader(加载资源文件),事件发布类,上下文)、
// 加系统BeanPostProcessor`ApplicationListenerDetector(Bean初始化后执行,判断是否是单例监听器加到上下文中)`、
// 加入AspectJ静态代理支持、系统环境Bean检查注册
prepareBeanFactory(beanFactory);
try {
// 4. BeanFactory配置好的后置拓展操作.由子类拓展.可在这里提前加入自定义BeanFactoryPostProcess
postProcessBeanFactory(beanFactory);
// 忽略掉不重要!启动阶段标记
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 5. 执行BeanFactory后置处理. Spring的SPI机制保障(可看我自动装载文章).
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册BeanPostProcessors到BeanFacotry中并排序,并未执行.涉及到Bean生命周期执行
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 7. 初始化MessageSource,用于消息国际化处理
initMessageSource();
// 8. 初始化上下文事件广播器
initApplicationEventMulticaster();
// 9. 子类实现,springbootstarterweb在此创建web容器,并提前生成容器所需的Bean及其对应生命周期
onRefresh();
// 10. 给广播器中注册监听器,执行初期事件
registerListeners();
// 11. 初始化所有非懒加载单例Bean
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新,发布上下文刷新完毕事件
finishRefresh();
}catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 刷新异常,销毁已经创建的Bean
destroyBeans();
// 取消刷新,设置active为false
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
// 刷新结束,设置标志位为true
contextRefresh.end();
}
}
}
到此为止。