【Springboot深入解析】框架启动流程

文章仅从源码的角度探讨springboot2.x的原理,不探讨使用。

我们知道Springboot是靠着这段代码进行启动的。

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

分析源码的话,理所当然从这里下手。

通过定位到源码,我们发现这里有两步。一步是初始化SpringApplication对象,一步是调用run方法进行来完成启动。

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }

首先看一下初始化SpringApplication对象:
对于SpringApplication初始化的源码,主要就是对相关的属性进行赋值。(this.xxx = xxx)

    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 = Collections.emptySet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
        this.applicationStartup = ApplicationStartup.DEFAULT;
        // 【配置资源加载器】
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // 【配置primarySources】
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        // 【应用环境监测】
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 【设置引导注册初始化列表 -- 该方法标记为弃用,可能在某个版本后完全删除】
        this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
        // 【配置系统初始化器】
         this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 【配置应用监听器】
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        // 【配置main方法所在的类】
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

下面看一下run方法,它的内容非常简短:

public ConfigurableApplicationContext run(String... args) {
		// 计时器开始计时 
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        //  Headless 模式赋值 : 工作在一个没有显示器键盘的环境
        this.configureHeadlessProperty();
        // 发送ApplicationStartingEvent -- 监听事件 
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            // 配置环境模块
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 发送 ApplicationEnvironmentPreparedEvent
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            // 打印banner
            Banner printedBanner = this.printBanner(environment);
            // 创建应用上下文对象 
            context = this.createApplicationContext();
            // 初始化失败分析器 
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            // 关联SpringBoot 组件和应用上下文对象、在prepareContext 方法中发送ApplicationContextInitializedEvent 
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            // 加载sources到context当中、发送ApplicationPreparedEvent、刷新上下文
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            // 计时器停止计时
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
			// 发送ApplicationStartedEvent事件
            listeners.started(context);
            // 调用框架启动扩展类、发送ApplicationReadyEvent事件
            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);
        }
    }

表面上看只有三十多行代码,其实背后的代码量是非常高的。

上面我们大致清楚了springboot的启动,本质就是初始化SpringApplication对象后,调用run方法。在扣细节之前,有必要整体认识一下springboot启动做了哪些事情:

框架初始化分为:

  1. 配置资源加载器
  2. 配置primarySources(一般是我们的启动类)
  3. 应用环境的检测(springboot1.x版本有两种环境,标准环境和web环境,spingboot2.x添加了一种Reactive环境)
  4. 配置系统初始化器
  5. 配置应用监听器
  6. 配置main方法所在类

接着就是框架的基本启动

  1. 计时器开始计时
  2. Headless模式赋值
  3. 发送ApplicationStartingEvent
  4. 配置环境模块
  5. 发送ApplicationEnvironmentPreparedEvent
  6. 打印banner
  7. 创建应用上下文对象
  8. 初始化失败分析器
  9. 关联springboot组件与应用上下文对象
  10. 发送ApplicationContextInitalizedEvent
  11. 加载sources到context
  12. 发送ApplicationPreparedEvent
  13. 刷新上下文(完成bean的加载)
  14. 计时器停止计时
  15. 发送ApplicationStartedEvent事件
  16. 调用框架启动扩展类
  17. 发送ApplicationReadyEvent

通过以上步骤完成基本的启动,后面还有框架的自动化装配的内容:
  1. 收集配置文件中的配置工厂类
  2. 加载组件工厂
  3. 注册组件内定义 bean

文字描述,可能不太直观,这里用图示整体梳理一下:
在这里插入图片描述

下面一起扣细节吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值