SpringBoot启动过程

注:本次基于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();
        }
    }
}

到此为止。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值