SpringBoot的入口是从SpringApplication.run( )方法传入我们的主启动类开始的
@SpringBootApplication
public class Web01Application {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(Web01Application.class, args);
}
}
run( )方法一路点进去我们就都能得到,系统为我们创建了一个SpringBootApplication,然后把它的run( )方法跑起来了
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
我们这次探索SpringBoot启动流程就从这里开始,我们把我们探索的步骤分为两块:
- SpringBootApplication对象的创建做了啥
- SpringBootApplication的run( )方法做了啥
SpringBootApplication对象的创建
我们就理解成SpringBoot这个应用的创建,它的构造方法如下。这里比较简单,大概做了四个事情:
- 把我们的web应用设置成基于Servlet实现的web应用(一个标识而已)
- 加载系统中引导器Bootstrapper(从系统的META-INFO/spring.factories文件中读取)
- 加载初始化器ApplicationContextInitializer(从系统的META-INFO/spring.factories文件中读取)
- 加载监听器ApplicationListener(从系统的META-INFO/spring.factories文件中读取)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//把我们的web应用设置成基于Servlet实现的web应用(一个标识而已)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//加载系统中引导器Bootstrapper(从系统的META-INFO/spring.factories文件中读取)
this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
//加载初始化器ApplicationContextInitializer(从系统的META-INFO/spring.factories文件中读取)
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//加载监听器ApplicationListener(从系统的META-INFO/spring.factories文件中读取)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
SpringBootApplication实例就创建好了,具体就是往刚刚创建的SpringBootApplication的实例中添加了一些启动器的监听、初始化器的监听、监听器,然后在下面的SpringBoot应用跑起来的过程状态、Spring的IOC容器回调出去,让编程人员做定制,做扩展
SpringBootApplication对象的run( )方法
run ( )方法内容比较多,个人总结下,这里做的事情:
- 通过各种的初始化器、监听器、暴露SpringBoot应用的创建过程的状态
- SpringBoot前期前期创建组件、环境交给Spring接管
public ConfigurableApplicationContext run(String... args) {
//计时开始
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//创建了一个DefaultBootstrapContext 对象,同时将初始化的开始的状态暴露出去
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
//设置成headless模式
configureHeadlessProperty();
//从META-INFO/spring.factories文件中获取SpringApplicationRunListener,用于暴露SpringBoot应用的运行状态
SpringApplicationRunListeners listeners = getRunListeners(args);
//把SpringBoot开始的状态暴露出去
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//把main( )方法传递过来的参数封一些
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//加载当前系统环境变量、和SpringBoot的配置文件的信息
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
//属性spring.beaninfo.ignore默认为true
configureIgnoreBeanInfo(environment);
//对应banner,debug的时候你放行这一步,Spring的banner就打印出来了
Banner printedBanner = printBanner(environment);
//返回一个基于注解的IOC容器,是AnnotationConfigServletWebServerApplicationContext类的实例
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
//这一步做的事情主要是将刚刚加载的环境变量信息、SpringBoot的配置信息给到context、还有将一对的用于类型转换的Converter给到BeanFactory
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//接下来就是Spring的IOC容器创建过程(就是Spring的refresh()方法),这里可以参考我之前的分析Spring的IOC容器创建过程:https://blog.csdn.net/qq_27062249/article/details/118123237
refreshContext(context);
//空实现,可以重写、扩展
afterRefresh(context, applicationArguments);
//停止计时
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//调SpringApplicationRunListener的started( )方法将应用已经跑起来的状态回调出去,也是从META-INFO/spring.factories文件中获取的listener
listeners.started(context);
//这里调用了ApplicationRunner的实例和CommandLineRunner的实例的run()方法,这里两个类的实例都是从Spring的IOC容器中获取的
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//过程中出错就通过SpringApplicationRunListeners的failed( )方法将错误信息回调出去
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
//通过SpringApplicationRunListener将应用已经跑起来的状态回调出去
listeners.running(context);
}
catch (Throwable ex) {
//出错回调
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
//返回当前的Spring的IOC容器。我们在SpringBoot主启动类中可以接收试试,对应的类是:ConfigurableApplicationContext
return context;
}
这里需要注意的一点,如果你想自定义启动器、初始化器、监听器来监听SpringBoot的启动过程的话,一定要弄清楚SpringBoot在获取对应的实例,然后调用对应的方法的时候,是从哪里获取的。文中提到了两种方式:
- spring.factories文件中声明
- 从Spring的IOC容器中获取