2020-08-24---springboot启动流程(粗略)

SpringBoot启动流程

本章, 会粗略的讲一讲 main 函数 入口, 进入 springboot 启动流程

也仅仅是大概的提一下流程. 不会进行深入 . 勿喷

以后有空 会来细填这个坑的…

那么! 先把我总结出的流程列出来, 后面会跟上我的源码解析(粗略) 有不正确的地方请指出

总的流程 :

  1. main函数入口 , 调用SpringApplication的静态run方法
  2. 首先会对SpringApplication进行构造
    • 会对基本的属性进行赋值
    • 然后对当前的应用类型进行判断
    • 通过反射得到 META-INF/spring.factories 里面的 ApplicationContextInitializer 和 ApplicationListener的实例
    • 找到具有main方法的类
  3. 然后调用SpringApplcaiton实例的run方法
    • 得到一些用来监听容器运行的类. 让他们工作起来 (META-INF/spring.factories)
    • 对命令行参数进行包装, 得到一个可用的环境
    • 打印横幅…
    • 创建对应的上下文
    • 创建一些异常的报告者
    • 对上下文的启动前处理
      • 上下文属性的设置
      • 加载一些特殊的bean
      • 解析资源
      • 触发容器监听者的load回调方法
    • 启动上下文的refresh方法, 让ioc容器工作起来 (明天有空 , 我也会进行理一理, refresh的粗略流程)
    • 触发一个可扩展的钩子方法, 容器启动后~
    • 回调容器监听者的 started 方法
    • 如果都没有出错. 回调 监听者 的 running方法, 表示应用进入正常运行阶段~ 结束~

接下来就是 我根据源码 理解的一些注释了

main函数的流程

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

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

public SpringApplication(Class<?>... primarySources) {
    this((ResourceLoader)null, primarySources);
}

这是一个正常的spingboot启动入口


之后它会先创建 springapplicaiton 的应用

我们来看一下源代码先 ( 就挑出一些重要的 )

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // ... 省略掉了.  一堆属性 设置默认值... 
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));		//这个东西就是标注了@SpringBootApplication 的启动类 , 标记一下
    
    // 判断当前是不是 web 应用
    /**	有这3种类型
    NONE,	
    SERVLET,
    REACTIVE; 
    */
    // 通过判断当前类加载的范围内, 是否存在一些特殊的类 来进行判断
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    
    // 这俩注意 : 回去获得 类路径下的 META-INF/spring.factories 文件里的类名 , 进行反射获得实例 (这个东西 也是自动配置的原理之一)
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    
    // 找到具有main方法的类
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

小总结 :

​ 在创建SpringApplication 的过程中,

  • 会对基本的属性进行赋值
  • 然后对当前的应用类型进行判断
  • 通过反射得到 META-INF/spring.factories 里面的 ApplicationContextInitializer 和 ApplicationListener的实例
  • 找到具有main方法的类

既然有了一个 SpringApplication , 接着我们来看 run 方法

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);
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
        this.configureIgnoreBeanInfo(environment);
        //准备出一个环境--↑↑↑
        
        // 打印横幅... 就是 启动的时候 显示的 SpringBoot 的log图案
        Banner printedBanner = this.printBanner(environment);
        
        // 创造一个 上下文 这个方法会去看一眼... 
        context = this.createApplicationContext();
        
        // 又是从 spring.factories 得到 这个 异常报告的 类...的实例集合
        exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
        
        // 在这里 , 主要把 环境放入上下文中, 然后 设置一下上下文的一些属性, 把 applicationArguments 和 printedBanner 注册进 容器中 , 让上下文去加载资源  最后调用 listerners 的 contextLoaded 回调
        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        
        // 这个方法会去调用 上下文十分有名的方法, refresh() .. 也就是到这里  ioc 容器就启动了
        this.refreshContext(context);
        
        // 这是一个空方法 , protected , 意味着 可以让我们自己去改写....扩展
        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);
    }
}


//创建上下文..
protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;		//看看是不是由用户指定了一个上下文...
    if (contextClass == null) {	// 如果没有..
        
        //其实这个方法就这点东西
        /**
        如果指定了 , 就用指定的
        如果没有指定 --
        	且是SERVLET类型的, 就使用 AnnotationConfigServletWebServerApplicationContext
        	是REACTIVE类型的, 使用 AnnotationConfigReactiveWebServerApplicationContext
        	不是WEB应用,  就是使用 AnnotationConfigApplicationContext
        
        */
        try {
            switch(this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                    break;
                case REACTIVE:
                    contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                    break;
                default:
                    contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
            }
        } catch (ClassNotFoundException var3) {
            throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
        }
    }

    return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}

小总结 :

​ 在这个run方法中, 我们大方向出发 ,完成了 上下文的创建, 资源的加载, 和上下文的启动 . 细节

  1. 得到一些用来监听容器运行的类. 让他们工作起来
  2. 对命令行参数进行包装, 得到一个可用的环境
  3. 打印横幅…
  4. 创建对应的上下文
  5. 创建一些异常的报告者
  6. 对上下文的启动前处理
    • 上下文属性的设置
    • 加载一些特殊的bean
    • 解析资源
    • 触发容器监听者的load回调方法
  7. 启动上下文的refresh方法, 让ioc容器工作起来
  8. 触发一个可扩展的钩子方法, 容器启动后~
  9. 回调容器监听者的 started 方法
  10. 如果都没有出错. 回调 监听者 的 running方法, 表示应用进入正常运行阶段~ 结束~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值