前言
大家学习的时候,别忽视了我写的代码的注释,对应整体程序的理解会有一定帮助的。这篇文章开始,我们正式开始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类型的上下文对象;
浙公网安备 33010602011771号