美文网首页
SpringBoot执行原理

SpringBoot执行原理

作者: david161 | 来源:发表于2022-06-11 09:09 被阅读0次

每个SpringBoot项目都有一个主程序启动类,在主程序启动类中有一个启动项目的main()方法,在该方法中通过执行SpringApplication.run()即可启动整个SpringBoot程序。
SpringApplication.run()方法到底是如何做到启动SpringBoot项目的呢?下面我们查看run()方法内部的源码,核心代码具体如下:

@SpringBootApplication
public class SpringbootDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootDemoApplication.class, args);
    }
}
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
    return run(new Class[]{primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
    return (new SpringApplication(primarySources)).run(args);
}

从上述源码可以看出,SpringApplication.run()方法内部执行了两个操作,分别是SpringApplicaiotn实例的初始化创建和调用run()启动项目,这两个阶段的实现具体说明如下:

SpringApplication实例的初始化创建

查看SpringApplication实例对象初始化创建的源码信息,核心代码具体如下:

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 = new HashSet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        //把项目启动类.class设置为属性存储起来
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        //判断当前webApplicatonType应用的类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
       //设置初始化器(Initializer),最后会调用这些初始化器
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //设置监听器(Listener)
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        //用于推断并设置项目main()方法启动的主程序启动类
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

从上述源码可以看出,SpringApplication的初始化过程主要包括4部分,具体说明如下。
1)this.webApplicationType = WebApplicationType.deduceFromClasspath()
用于判断当前webApplicaitonType应用的类型。deduceFromClasspath()方法用于查看Classpath类路径下是否存在某个特征类,从而判断当前webApplicationType类型是SERVLET应用(Spring5之前的传统MVC应用)还是REACTIVE应用(Spring5开始出现的WebFlux交互式应用)
2)this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class))
用于SpringApplication应用的初始化器设置。在初始化器设置过程中,会使用Spring类加载器SpringFactoriresLoader从META-INF/spring.factories类路径下的META-INF下的spring.factories文件中获取所有可用的应用初始化器类ApplicationContextInitializer。
3)this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class))
用于SpringApplication应用的监听器设置。监听器设置的过程与上一步初始化器设置的过程基本一样,也是使用SpringFactoriesLoader从META-INF/spring.factories类路径下的META-INF下的spring.factores文件中获取所有可用的监听器类ApplicationListener。
4)this.mainApplicationClass = this.deduceMainApplicationClass()
用于推断并设置项目main()方法启动的主程序启动类

项目的初始化启动

分析完(new SpringApplication(primarySources)).run(args)源码前一部分SpringApplication实例对象的初始化创建后,查看run(args)方法执行的项目初始化启动过程,核心代码具体如下:

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new  ArrayList();
    this.configureHeadlessProperty();

    // 第一步,获取并启动监听器
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    listeners.starting();
    Collection exceptionReporters;
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //第二步:根据SpringApplicationRunListeners以及参数来准备环境
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
        this.configureIgnoreBeanInfo(environment);
         // 准备Banner打印器-就是启动Spring Boot的时候打印在console上的ASCII艺术字体
        Banner printedBanner = this.printBanner(environment);
        //第三步:创建Spring容器
        context = this.createApplicationContext();
        exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class,
        new Class[]{ConfigurableApplicationContext.class}, new Object[]{context});
        //第四步:Spring容器前置处理
        this.prepareContext(context, environment, listeners,applicationArguments, printedBanner);
        //第五步:刷新容器
       this.refreshContext(context);
       //第六步:Spring容器后置处理
       this.afterRefresh(context, applicationArguments);
       stopWatch.stop();
       if(this.logStartupInfo) {
           (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
       }
       //第七步:发出结束执行事件
       listeners.started(context);
      //返回容器
      this.callRunners(context, applicationArguments);
      } catch (Throwable var10) {
         this.handleRunFailure(context, var10, exceptionReporters, listeners);
         throw new IllegalStateException(var10);
     }
     try {
         listeners.running(context);
         return context;
     } catch (Throwable var9) {
         this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
         throw new IllegalStateException(var9);
     }
 }

从上述源码可以看出,项目初始化启动过程大致包括以下部分:
1)第一步:获取并启动监听器
this.getRunListeners(args)和listeners.starting()方法主要用于获取SpringApplication实例初始化过程中初始化的SpringApplicationRunListener监听器并运行。
2)第二步:根据SpringApplicationRunListeners以及参数来准备环境
this.prepareEnvironment(listeners, applicationArguments)方法主要用于对项目运行环境进行预设值,同时通过this.configureIgnoreBeanInfo(environment)方法排除一些不需要的运行环境。
3)第三步:创建Spring容器
根据webApplicationType进行判断,确定容器类型,如果该类型为SERVLET类型,会通过反射装载对应的字节码,也就是AnnotationConfigServletWebServerApplicationContext,接着使用之前初始化设置的context(应用上下文环境)、environment(项目运行环境)、listeners(运行监听器)、applicationArguments(项目参数)和printedBanner(项目图标信息)进行应用上下文的组装配置,并刷新配置。
4)第四步:Spring容器前置处理
这一步主要是在容器刷新之前的准备动作。设置容器环境,包括各种变量等等,其中包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
5)第五步:刷新容器
开启刷新spring容器,通过refresh方法对整个IOC容器的初始化(包括bean资源的定位,解析,注册等等),同时向JVM运行时注册一个关键钩子,在JVM关机时会关闭这个上下文,除非当时它已经关闭
6)第六步:Spring容器后置处理
扩展接口,设计模式中的模板方法,默认为空实现。如果有自定义需求,可用重写该方法。比如打印一些启动结束log,或者一些其他后置处理。
7)第七步:发出结束执行的事件
获取EventPulishingRunListener监听器,并执行其started方法,并且将创建的Spring容器传进去了,创建一个ApplicationStartedEvent事件,并执行ConfigurableApplicationContext的publishEvent方法,也就是说这里在Spring容器中发布事件,并不是在SpringApplication中发布事件,和前面的starting是不同的,前面的starting是直接向SpringApplication中的监听器发布启动操作。
8)第八步:执行Runners
用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序。其中,SpringBoot提供的执行器接口有ApplicationRunner和CommandLineRunner两种,在使用时只需要自定义一个执行器类实现其中一个接口并重写对应的run()方法接口,然后SpringBoot项目启动后会立即执行这些特定程序。


image.png

相关文章

网友评论

      本文标题:SpringBoot执行原理

      本文链接:https://www.haomeiwen.com/subject/jrgmmrtx.html