SpringBoot启动流程

109 阅读3分钟

启动类

@SpringBootApplication
public class SpringmvcApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringmvcApplication.class, args);
    }
}

@SpringBootApplication

首先看@SpringBootApplication注解的源码。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

}

很明显,@SpringBootApplication注解由三个注解组合而成,分别是:

  • @ComponentScan
  • @EnableAutoConfiguration
  • @SpringBootConfiguration

2.1 @ComponentScan

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

}

这个注解的作用是告诉Spring扫描哪个包下面类,加载符合条件的组件(比如贴有@Component和@Repository等的类)或者bean的定义。

所以有一个basePackages的属性,如果默认不写,则从声明@ComponentScan所在类的package进行扫描。

所以启动类最好定义在Root package下,因为一般我们在使用@SpringBootApplication时,都不指定basePackages的

2.2 @EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    

这是一个复合注解,看起来很多注解,实际上关键在@Import注解,它会加载
AutoConfigurationImportSelector类,然后就会触发这个类的selectImports()方法。根据返回的String数组(配置类的Class的名称)加载配置类

public class AutoConfigurationImportSelector implements DeferredImportSelectorBeanClassLoaderAware,
ResourceLoaderAwareBeanFactoryAwareEnvironmentAwareOrdered {
    //返回的String[]数组,是配置类Class的类名
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
        //返回配置类的类名
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

我们一直点下去,就可以找到最后的幕后英雄,就是SpringFactoriesLoader类,通过loadSpringFactories()方法加载META-INF/spring.factories中的配置类。 从源码去分析SpringBoot启动流程 从源码去分析SpringBoot启动流程 这里使用了spring.factories文件的方式加载配置类,提供了很好的扩展性。

所以@EnableAutoConfiguration注解的作用其实就是开启自动配置,自动配置主要则依靠这种加载方式来实现

2.3 @SpringBootConfiguration

@SpringBootConfiguration继承自@Configuration,二者功能也一致,标注当前类是配置类, 并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名。

2.4 小结

从源码去分析SpringBoot启动流程

SpringApplication类

image.png

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    //断言primarySources不能为null,如果为null,抛出异常提示
    Assert.notNull(primarySources, "PrimarySources must not be null");
    //启动类传入的Class
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //判断当前项目类型,有三种:NONE、SERVLET、REACTIVE
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //设置ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //设置监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //判断主类,初始化入口类![从源码去分析SpringBoot启动流程](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d26f256ca9324e73bd4dedd9f4304657~tplv-k3u1fbpfcp-zoom-1.image)
    创建了SpringApplication实例之后,就完成了SpringApplication类的初始化工作,这个实例里包括监听器、初始化器,项目应用类型,启动类集合,类加载器。如图所示
    ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4a3a135d03f54b34828feb9ffcd117b1~tplv-k3u1fbpfcp-zoom-1.image)
    得到SpringApplication实例后,接下来就调用实例方法run()。继续看
    创建了SpringApplication实例之后,就完成了SpringApplication类的初始化工作,这个实例里包括监听器、初始化器,项目应用类型,启动类集合,类加载器。如图所示。

![从源码去分析SpringBoot启动流程](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3e46b54187cc4122adea3763c0bd2b88~tplv-k3u1fbpfcp-zoom-1.image)

得到SpringApplication实例后,接下来就调用实例方法run()。继续看。

public ConfigurableApplicationContext run(String... args) {     //创建计时器     StopWatch stopWatch = new StopWatch();     //开始计时     stopWatch.start();     //定义上下文对象     ConfigurableApplicationContext context = null;     Collection exceptionReporters = new ArrayList<>();     //Headless模式设置     configureHeadlessProperty();     //加载SpringApplicationRunListeners监听器     SpringApplicationRunListeners listeners = getRunListeners(args);     //发送ApplicationStartingEvent事件     listeners.starting();     try {         //封装ApplicationArguments对象         ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);         //配置环境模块         ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);         //根据环境信息配置要忽略的bean信息         configureIgnoreBeanInfo(environment);         //打印Banner标志         Banner printedBanner = printBanner(environment);         //创建ApplicationContext应用上下文         context = createApplicationContext();         //加载SpringBootExceptionReporter         exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,                                                          new Class[] { ConfigurableApplicationContext.class }, context);         //ApplicationContext基本属性配置         prepareContext(context, environment, listeners, applicationArguments, printedBanner);         //刷新上下文         refreshContext(context);         //刷新后的操作,由子类去扩展         afterRefresh(context, applicationArguments);         //计时结束         stopWatch.stop();         //打印日志         if (this.logStartupInfo) {             new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);         }         //发送ApplicationStartedEvent事件,标志spring容器已经刷新,此时所有的bean实例都已经加载完毕         listeners.started(context);         //查找容器中注册有CommandLineRunner或者ApplicationRunner的bean,遍历并执行run方法         callRunners(context, applicationArguments);     }     catch (Throwable ex) {         //发送ApplicationFailedEvent事件,标志SpringBoot启动失败         handleRunFailure(context, ex, exceptionReporters, listeners);         throw new IllegalStateException(ex);     }

    try {         //发送ApplicationReadyEvent事件,标志SpringApplication已经正在运行,即已经成功启动,可以接收服务请求。         listeners.running(context);     }     catch (Throwable ex) {         //报告异常,但是不发送任何事件         handleRunFailure(context, ex, exceptionReporters, null);         throw new IllegalStateException(ex);     }     return context; }

    this.mainApplicationClass = deduceMainApplicationClass();
}

以上就是创建SpringApplication实例做的事情,下面用张图来表示一下 从源码去分析SpringBoot启动流程 创建了SpringApplication实例之后,就完成了SpringApplication类的初始化工作,这个实例里包括监听器、初始化器,项目应用类型,启动类集合,类加载器。如图所示

得到SpringApplication实例后,接下来就调用实例方法run()。继续看。

public ConfigurableApplicationContext run(String... args) {
    //创建计时器
    StopWatch stopWatch = new StopWatch();
    //开始计时
    stopWatch.start();
    //定义上下文对象
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    //Headless模式设置
    configureHeadlessProperty();
    //加载SpringApplicationRunListeners监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    //发送ApplicationStartingEvent事件
    listeners.starting();
    try {
        //封装ApplicationArguments对象
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //配置环境模块
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        //根据环境信息配置要忽略的bean信息
        configureIgnoreBeanInfo(environment);
        //打印Banner标志
        Banner printedBanner = printBanner(environment);
        //创建ApplicationContext应用上下文
        context = createApplicationContext();
        //加载SpringBootExceptionReporter
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                                         new Class[] { ConfigurableApplicationContext.class }, context);
        //ApplicationContext基本属性配置
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        //刷新上下文
        refreshContext(context);
        //刷新后的操作,由子类去扩展
        afterRefresh(context, applicationArguments);
        //计时结束
        stopWatch.stop();
        //打印日志
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        //发送ApplicationStartedEvent事件,标志spring容器已经刷新,此时所有的bean实例都已经加载完毕
        listeners.started(context);
        //查找容器中注册有CommandLineRunner或者ApplicationRunner的bean,遍历并执行run方法
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        //发送ApplicationFailedEvent事件,标志SpringBoot启动失败
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        //发送ApplicationReadyEvent事件,标志SpringApplication已经正在运行,即已经成功启动,可以接收服务请求。
        listeners.running(context);
    }
    catch (Throwable ex) {
        //报告异常,但是不发送任何事件
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

从源码去分析SpringBoot启动流程