月夜星空-liulq

--愿美好接踵而至

前言

    大家学习的时候,别忽视了我写的代码的注释,对应整体程序的理解会有一定帮助的。这篇文章开始,我们正式开始run方法的学习,先上代码。

    public ConfigurableApplicationContext run(String... args) {
//开启计时器,和下面的stopWatch.stop之间,记录启动的时间 StopWatch stopWatch
= new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null;
//记录程序启动的异常报告 这里是个空集合,后面会用到 Collection
<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
//向系统中设置一个headless的属性,不用管,对整体没啥用
this.configureHeadlessProperty();
//重要方法,下面《SpringApplicationRunListeners监听器》详细说明 发布监听器,并开始监听,从配置文件读取到EventPublishingRunListener 然后使用它的多播器,存储监听器,实现监听 SpringApplicationRunListeners listeners
= this.getRunListeners(args); listeners.starting(); Collection exceptionReporters; try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//读取系统的环境变量 暂时先管 ConfigurableEnvironment environment
= this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment); Banner printedBanner = this.printBanner(environment);
//重要方法《context = this.createApplicationContext() 创建应用上下文》详细说明 根据应用类型,创建上下文应用 context
= this.createApplicationContext();
//大哥,熟悉不,出现四五次了,读取配置文件,读取全类名,反射完成实例化。 这里加载了一些异常处理类 exceptionReporters
= this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//重要方法,在《SpringBoot源码系列(四)》详细讲解 加载了一些准备信息,包括我们的启动类
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//重要方法,在《SpringBoot源码系列(五)》详细讲解
this.refreshContext(context); 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); } }

SpringApplicationRunListeners 监听器

     SpringApplicationRunListeners listeners= this.getRunListenters(args);进入这个getRunListenters方法,里面调用了一个非常熟悉的方法 -- getSpringFactoriesInstances,这个方法在《SpringBoot源码系列(二)》中大家都见过了,就是按照我们传进去的class,从配置文件中找到相应的类的全类名,完成实例化。这里读取到的就一个类 --EventPublishingRunListener(就是从META-INF/spring.factories里面读取回来的,读取全类名,完成实例化)。把前面SpringApplication的构造函数中取到的监听器装载到这个类的多播器的容器中。然后调用starting方法,这个方法对外发布了ApplicationStartingEvent事件,有监听这个事件的,就会收到通知了。如果对监听器不是很理解,可以看看我的《SpringBoot源码加餐--监听器》

 context = this.createApplicationContext() 创建应用上下文

    首先看看这个context是什么,代码往上找找,ConfigurableApplicationContext context = null,是我们创建的一个ConfigurableApplicationContext 类型的空对象,我们查看这个类的类图:

 

 

    可以看出,我们的ConfigurableApplicationContext 接口继承自BeanFactory,至此,我们经常听到的BeanFactory在源码中首次出现在了我们的视线中,接下来我们再看看这个this.createApplicationContext方法又做了什么呢:

 protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            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);
    }

    其实就是根据this.webApplicationType来判断创建那个类型的实例,这个this.webApplicationType讲解构造函数的章节《SpringBoot源码系列(二)》中有提到过,就是一个servlet类型,所以这里我们会创建一个org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext类型的实例(BeanUtils.instantiateClass创建的实例),至此,我们知道我们的context就是一个AnnotationConfigServletWebServerApplicationContext的实例。我们现在有了上下文对象,就可以向这个对象里面设置信息了,下面的代码也会针对这个context进行一系列的处理。

    注意,我们创建的context实际上是一个beanFactory接口的子类,包含了我们的beanFactory的信息,经过这步的创建之后,context里面有beanFactory信息:

 

     本来这个AnnotationConfigServletWebServerApplicationContext类不准备详细讲解的,但是这里涉及到了他的两个父类,这两个父类里面又有一些重要的点需要拿出来说,所以还是讲讲这个类吧,首先看这个类的类图:

 

 

    来,告诉我,你蒙圈了,这么复杂的类图,我也蒙圈,没事,咱们接下来主要看的就两个,一个是GenericApplicationContext,另一个是AbstractApplicationContext,这两个的关系在类图中有所体现,在上面我们说过,会创建AnnotationConfigServletWebServerApplicationContext的实例,创建他的实例的时候,会遍历他的父级,创建实例,所以这两个类也都会被创建实例,AbstractApplicationContext是一个非常重要的类,我们后面会讲到Spring的核心 -- refresh方法,这个方法就是在AbstractApplicationContext内部的。

    我们再来看看GenericApplicationContext这个类的构造函数:

    public GenericApplicationContext() {
        this.customClassLoader = false;
        this.refreshed = new AtomicBoolean();
//beanFactory是在这里创建的,是一个DefaultListableBeanFactory的实例
this.beanFactory = new DefaultListableBeanFactory(); } public GenericApplicationContext(DefaultListableBeanFactory beanFactory) { this.customClassLoader = false; this.refreshed = new AtomicBoolean(); Assert.notNull(beanFactory, "BeanFactory must not be null"); this.beanFactory = beanFactory; }

    可以看到,我们后面用到的beanFactory就是在GenericApplicationContext的构造函数中完成的实例化的。

总结

    目前为止,算上一篇《SpringBoot源码加餐 -- 监听器》,已经完成了四篇文章了,不过可以告诉大家,我们的慢慢长征路刚刚迈出了第一步,真正的核心源码还没有开始。下篇开始就进入了我们的核心源码的学习了,在开始核心源码之前,先回顾下目前为止我们都学到了哪些内容吧。

    1 在SpringApplication的构造函数中,我们从META-INF/spring.factories配置文件中加载到了初始化器和监听器;

    2 run方法中加载了SpringApplicationRunListeners监听器,并发布了一个事件,监听该事件的监听器会做出响应;

    3 监听器的启动过程;

    4 创建了一个AnnotationConfigServletWebServerApplicationContext类型的上下文对象;

    

 

posted on 2022-01-16 17:10  月夜星空-liulq  阅读(457)  评论(0)    收藏  举报