走心Springboot源码解析: 二、SpringApplication.run()方法上部分

打个广告

个人想写《springboot源码解析》这一系列很久了,但是一直角儿心底的知识积累不足,所以一直没有动笔。

所以想找一些小伙伴一起写这一系列,互相纠错交流学习。

如果有小伙伴有兴趣一起把这一系列的讲解写完的话,加下我微信:13670426148,我们一起完成,当交流学习。

后期还想写一系列介绍rpc框架的,不过要再过一阵子了,先把springboot的写完,坚持一周一更,周末更新

前言

上篇回顾
讲了SpringApplication的实例化,其中最主要的是加载Spring的Listener和Initializer,Listener为10个,Initializer为6个,详细查看 走心Springboot源码解析: 一、SpringApplication的实例化

上篇写完之后才发现之前用的是springboot1.5的版本,但是现在大家都是用springboot2.+,所以我找了个springboot2.0+ 的项目来进行解析,但是第一篇就还没改变,到时候有空再更新,不过大致的内容还是差不多的。

这篇主要讲run()方法,分成两部分,这篇先讲上半部分,主要聚焦在run()方法中涉及到的 事件-监听器-广播 机制,直到创建上下文部分(context = this.createApplicationContext())。

run()方法方法总览

 public ConfigurableApplicationContext run(String... args) {
        //这是一个计时器,stopWatch.start()开始计时,到后面stopWatch.stop()停止计时。
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        //这个ConfigurableApplicationContext就是我们说的 上下文,后面preContext()的时候会详细解析,这个是springboot中最重要的一个类了
        ConfigurableApplicationContext context = null;
        //自定义SpringApplication启动错误的回调接口 
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        //此为设置 System 里面有一个properties的里的值 "java.awt.headless" ,设置为默认值true
        this.configureHeadlessProperty();
        //这个SpringApplicationRunListeners可跟前面的10个Listener不一样,这里面封装了一个广播。后面会讲他的实例化过程
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        //开始进行事件广播,下面详细解析
        listeners.starting();
        Collection exceptionReporters;
        try {
            //获取初始化参数,就是我们运行时的初始化参数,比如“java -jar --port=8080” 其中port就是一个参数了   
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //environment是运行时环境
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            //打印出springboot的标志,这个可以自己选择要打印输出的文本.
            Banner printedBanner = this.printBanner(environment);
            //创建根上下文,这个方法特别重要,将是下章讲解的重点
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            //准备环境,下面先不讲了。
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            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:一个存SpringApplicationRunListener的集合,里面有些方法,后续都会讲到;

SpringApplicationRunListeners : 
    | SpringApplicationRunListener
        |SimpleApplicationEventMulticaster 
SimpleApplicationEventMulticaster
    才是真正的广播,SpringApplicationRunListeners只不过是对其的一层封装

SpringApplicationRunListeners的实例化过程

private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
        //这里就不赘述了,META-INF/spring.factories下 
        //1. 获取key为SpringApplicationRunListener类路径为key 对应的 value
        //2. 实例化value对应的类,最后得到的类是EventPublishingRunListener.class,,如图1.1所示
        return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }

图1.1:
走心Springboot源码解析: 二、SpringApplication.run()方法上部分
所以说得到的 SpringApplicationRunListeners 里面有一个实例变量,内容是一个 EventPublishingRunListener 组成的 List

  • EventPublishingRunListener继承于SpringApplicationRunListener。
  • SpringApplicationRunListeners里面有一个实例变量,是多个EventPublishingRunListener组成的1个List。

SpringApplicationRunListener的结构

从命名我们就可以知道它是一个监听者,那纵观整个启动流程我们会发现,它其实是用来在整个启动流程中接收不同执行点事件通知的监听者,SpringApplicationRunListener接口规定了SpringBoot的生命周期,在各个生命周期广播相应的事件,调用实际的ApplicationListener类。
接下来看SpringApplicationRunListener
SpringApplicationRunListener接口规定了SpringBoot的生命周期

public class SpringApplicationRunListener{
     //刚执行run方法时
    void started();
     //环境建立好时候
    void environmentPrepared(ConfigurableEnvironment environment);
     //上下文建立好的时候
    void contextPrepared(ConfigurableApplicationContext context);
    //上下文载入配置时候
    void contextLoaded(ConfigurableApplicationContext context);
    //上下文刷新完成后,run方法执行完之前
    void finished(ConfigurableApplicationContext context, Throwable exception);
}

它定义了5个步骤:

  • started() -> run方法执行的时候立马执行;对应事件的类型是ApplicationStartedEvent,通知监听器,SpringBoot开始执行
  • environmentPrepared() -> ApplicationContext创建之前并且环境信息准备好的时候调用;对应事件的类型是ApplicationEnvironmentPreparedEvent), 通知监听器,Environment准备完成
  • contextPrepared -> ApplicationContext创建好并且在source加载之前调用一次;没有具体的对应事件), 通知监听器,ApplicationContext已经创建并初始化完成
  • contextLoaded -> ApplicationContext创建并加载之后并在refresh之前调用;对应事件的类型是ApplicationPreparedEvent), 通知监听器,ApplicationContext已经完成IoC配置价值
  • finished -> run方法结束之前调用;对应事件的类型是ApplicationReadyEvent或ApplicationFailedEvent), 通知监听器,SpringBoot启动完成

EventPublishingRunListener的构造器

我们来看EventPublishingRunListener的构造器。还有他的两个重要的方法:

EventPublishingRunListener类 实现了SpringApplicationRunListener,它具有广播事件的功能。

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    //这个是参数传进来的,也就是SpringApplication.class
    private final SpringApplication application;
    //这个是初始化参数
    private final String[] args;
    //真正的广 播
    private final SimpleApplicationEventMulticaster initialMulticaster;

    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
         //新建立广播器,这个广播器特别重要,下面继续解析
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        //application.getListeners()就是获取10个初始化的Listenter,具体可以参见上一篇了解哪10个Listener
        Iterator var3 = application.getListeners().iterator();
        
        while(var3.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var3.next();
            //添加到内部的defaultRetriever里,详细代码可以自己翻进去查看,这里不拓展,
            //后面需要根据事件类型推测有哪些监听器需要被触发,所以就得存把所有的监听器先存起来,称作一个注册表吧
            this.initialMulticaster.addApplicationListener(listener);
        }

    }
}

就是说,EventPublishingRunListener里面有一个广播器,结合上面的SpringApplicationRunListener接口声明的方法,我们可以得到其机制大概是:

  1. run()方法是用来在整个启动流程中接收不同执行点事件通知的监听者,唤醒监听者
  2. 而再进去就是调用EventPublishingRunListener的started()、environmentPrepared()等唤醒的,这里面就会有根据不同的方法,started(),或者environmentPrepared()等,生成不同的事件。传递给广播器。
  3. 而广播器SimpleApplicationEventMulticaster initialMulticaster, 就从注册表中(上文代码说了注册表的形成是那段代码),根据Event的类型,找到那些监听器是需要被触发的,执行其 multicastEvent(ApplicationEvent event) 方法,内部是invokeListener(listener, event);

广播器

multicastEvent() 方法就是广播方法
SimpleApplicationEventMulticaster广播出去,广播出去的事件对象会被SpringApplication中的listeners属性进行处理。

下面先解析一下广播方法multicastEvent()执行方法 invokeListener()


public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        //去注册表中根据相应的事件,获取Event对应的Listener。后面会简略讲讲获取的过程
        Iterator var4 = this.getApplicationListeners(event, type).iterator();

        while(var4.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var4.next();
            Executor executor = this.getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> {
                    this.invokeListener(listener, event);
                });
            } else {
                //最后的执行方法是这个,参数是“监听器和事件”
                this.invokeListener(listener, event);
            }
        }

    }
//执行方法
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        ErrorHandler errorHandler = this.getErrorHandler();
        if (errorHandler != null) {
            try {
                //看多了源码你们会发现,spring的源码很喜欢这么写,一般invokeListener的时候不是真的invokeListener的真实过程
                //而是会把真正的逻辑放到 doInvokeListener(do****,这里就是doInvokeListener)中执行.
                 //1. 而在invokeListener中只是在真正处理方法前做一点数据封装,
                 //2. 或者异常检查
                 // 目的:个人感觉主要的目的是把真正处理过程的代码缩减,使得真正的处理逻辑变得简洁易懂,不会有多余的代码加重理解难度
                this.doInvokeListener(listener, event);
            } catch (Throwable var5) {
                errorHandler.handleError(var5);
            }
        } else {
            this.doInvokeListener(listener, event);
        }
    }    
    
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
        //这里就很明白了,执行监听器的方法
            listener.onApplicationEvent(event);
        } catch (ClassCastException var6) {
            String msg = var6.getMessage();
            if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
                throw var6;
            }

            Log logger = LogFactory.getLog(this.getClass());
            if (logger.isDebugEnabled()) {
                logger.debug("Non-matching event type for listener: " + listener, var6);
            }
        }
    }

所以总结一下
两种监听器和广播器的关系如下:
SpringApplicationRunListeners、SpringApplicationRunListener、SimpleApplicationEventMulticaster

(1) SpringApplicationRunListeners是SpringApplicationRunListener的封装。pringApplicationRunListeners中包含多个SpringApplicationRunListener,
是为了批量执行的封装,SpringApplicationRunListeners与SpringApplicationRunListener生命周期相同,调用每个周期的各个SpringApplicationRunListener
然后广播利用SimpleApplicationEventMulticaster进行广播。

(2)SimpleApplicationEventMulticaster是封装在SpringApplicationRunListener里面的广播器,

通过上面的3个特点可以看出SpringApplicationRunListener。springboot启动的几个主要过程的监听通知都是通过他来进行回调。
流程图如图1.2所示

走心Springboot源码解析: 二、SpringApplication.run()方法上部分
其中就可以看出他们之间的关系了。

开始跑第一个事件started

直接把代码看到listeners.starting() 这一行

class SpringApplicationRunListeners {
     public void starting() {
        //这里获取SpringApplicationRunListeners里面的List<SpringApplicationRunListener> 
        Iterator var1 = this.listeners.iterator();
        //一般情况下,他只是一个,虽然这里是个list,所以这个while里面只会走1次
        while(var1.hasNext()) {
            SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
            //这个就进去到SpringApplicationRunListener里面去了,而此时SpringApplicationRunListener的实现类是EventPublishingRunListener
            listener.starting();
        }
    }
}
/**
**  EventPublishingRunListener实现了SpringApplicationRunListener,所以就得实现里面的starting(), environmentPrepared()等,贯穿整个springboot启动流程,而starting()只是他其中最开始的一步,看最上面整体的代码部分就知道了,后面还有environmentPrepared(), contextPrepared()等
**/
pubic class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    
    public void starting() {
        //1. 广播器执行广播事件方法
        //2. 参数是包装出来的 ApplicationStartingEvent ,跟下面的environmentPrepared,contextPrepared方法比,他们的事件都类型都不同的
       
        //(1) starting对应的是ApplicationStartingEvent
        //(2) environmentPrepared对应的是 ApplicationEnvironmentPreparedEvent
        //(3) ApplicationContextInitializedEvent对应的是 ApplicationContextInitializedEvent
        this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
    }
    
     public void environmentPrepared(ConfigurableEnvironment environment) {
        this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
    }

    public void contextPrepared(ConfigurableApplicationContext context) {
        this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
    }
    
    //省略****
}


public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {   
    //开始进行广播方法,因为此时是staring()方法出发的,所以这里的envent是在EventPublishingRunListener中new ApplicationContextInitializedEvent,
    public void multicastEvent(ApplicationEvent event) {
        //1. 第二个参数推断出eventType,,在真正执行广播
        this.multicastEvent(event, this.resolveDefaultEventType(event));
    }
    
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {   //推断出类型
        ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        //从注册表中根据event类型,获得所有对应的监听器,
        //结果获得的所有监听器有:
        //(1) LoggingApplicationListener、
        //(2) BackgroundPreinitializer、
        //(3) DelegatingApplicationListener
        //(4) LiquibaseServiceLocatorApplicationListener
        //(5)EnableEncryptablePropertiesBeanFactoryPostProcessor
        //五种类型的对象。这五个对象的onApplicationEvent都会被调用。
        Iterator var4 = this.getApplicationListeners(event, type).iterator();

        while(var4.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var4.next();
            Executor executor = this.getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> {
                    this.invokeListener(listener, event);
                });
            } else {
                //直接看调用方法
                this.invokeListener(listener, event);
            }
        }
    }
//这就是最终的调用点,Listener的onApplicationEvent(ApplicationEvent) 方法。
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            listener.onApplicationEvent(event);
        } catch (ClassCastException var6) {
            String msg = var6.getMessage();
            if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
                throw var6;
            }

            Log logger = LogFactory.getLog(this.getClass());
            if (logger.isDebugEnabled()) {
                logger.debug("Non-matching event type for listener: " + listener, var6);
            }
        }
    }
}

那么这五个监听器的onApplicationEvent都做了些什么了,我这里大概说下,细节的话大家自行去跟源码。

(1) LoggingApplicationListener:初始化日志系统,默认是logback,支持3种,优先级从高到低:logback > log4j > javalog

(2) BackgroundPreinitializer:启动多个线程执行相应的任务,包括验证器、消息转换器等等

(3) DelegatingApplicationListener:此时什么也没做

(4) LiquibaseServiceLocatorApplicationListener:此时什么也没做

(5)EnableEncryptablePropertiesBeanFactoryPostProcessor:仅仅打印了一句日志,其他什么也没做
对了,补充一点注册器的代码
喜欢就看,不喜欢就跳过,

// 返回所有的适合于ApplicationStartedEvent的监听器集合
protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
        Object source = event.getSource();
        Class<?> sourceType = source != null ? source.getClass() : null;
        //根据eventType和sourceType组装一个key即是cacheKey
        AbstractApplicationEventMulticaster.ListenerCacheKey cacheKey = new AbstractApplicationEventMulticaster.ListenerCacheKey(eventType, sourceType);
        //retriever里面包装了所有适合的监听器。
        AbstractApplicationEventMulticaster.ListenerRetriever retriever = (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey);
        //下面是双通道机制的单例模式写法,直接看标注注释的那行代码即可
        if (retriever != null) {
            return retriever.getApplicationListeners();
        } else if (this.beanClassLoader == null || ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader))) {
            synchronized(this.retrievalMutex) {
                retriever = (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey);
                if (retriever != null) {
                    return retriever.getApplicationListeners();
                } else {
                    //当第一次执行的时候,创建一个 retriever,
                    retriever = new AbstractApplicationEventMulticaster.ListenerRetriever(true);
                    //下面进行详细解析;
                    //直接在这里获取所有适合于ApplicationStartedEvent的监听器集合,并使用retriever.applicationListeners.add(listener);,添加到retriever中, 
                    Collection<ApplicationListener<?>> listeners = this.retrieveApplicationListeners(eventType, sourceType, retriever);
                    //以cacheKey为key,存到缓存中。
                    this.retrieverCache.put(cacheKey, retriever);
                    //返回所有的适合于ApplicationStartedEvent的监听器集合
                    return listeners;
                }
            }
        } else {
            return this.retrieveApplicationListeners(eventType, sourceType, (AbstractApplicationEventMulticaster.ListenerRetriever)null);
        }
    }

//上面方法的解析,直接在这里获取所有适合于ApplicationStartedEvent的监听器集合。
private Collection<ApplicationListener<?>> retrieveApplicationListeners(ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable 
                                                                        AbstractApplicationEventMulticaster.ListenerRetriever retriever) {
        List<ApplicationListener<?>> allListeners = new ArrayList();
        LinkedHashSet listeners;
        LinkedHashSet listenerBeans;
        synchronized(this.retrievalMutex) {
            //this.defaultRetriever.applicationListeners是所有默认的监听器,就是那10个默认的监听器,如下图所示
            listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);
            listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
        }
        
        Iterator var7 = listeners.iterator();

        while(var7.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var7.next();
            //就是在这里进行判断的,具体怎么判断就不进去了,这个不是重点
            if (this.supportsEvent(listener, eventType, sourceType)) {
                if (retriever != null) {
                    retriever.applicationListeners.add(listener);
                }

                allListeners.add(listener);
            }
        }
    
    //........篇幅有限,这个其实不重要,直接略过即可
}

准备环境 prepareEnvironment

//todo: 这部分可以用“监听器-事件-广播” 解释一下,下周末有时间再进行补充。
prepareEnvironment(listeners, applicationArguments);

?加载SpringBoot配置环境(configurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment。将配置文件(Environment)加入到监听器对象中(SpringApplicationRunListeners)

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // Create and configure the environment
    //如果environment不为空直接返回 || 如果是web环境则直接实例化StandardServletEnvironment类 || 如果不是web环境则直接实例化StandardEnvironment类
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    //配置环境信息
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    //通知所有的监听者,环境已经准备好了
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

总结

本篇主要讲了“监听器-事件-广播”的涉及到的几个类,展示了一个周期 starting()的运行过程。
再总结一下,流程如下:

  1. run()方法是用来在整个启动流程中接收不同执行点事件通知的监听者,唤醒监听者
  2. 而再进去就是调用EventPublishingRunListener的started()、environmentPrepared()等唤醒的,这里面就会有根据不同的方法,started(),或者environmentPrepared()等,生成不同的事件。传递给广播器。
  3. 而广播器SimpleApplicationEventMulticaster initialMulticaster, 就从注册表中(上文代码说了注册表的形成是那段代码),根据Event的类型,找到那些监听器是需要被触发的,执行其 multicastEvent(ApplicationEvent event) 方法,内部是invokeListener(listener, event);

    下期预告

    下期打算讲一讲, context的部分,这个内容就有很多了啊。。

参考内容: 大神写的,分析很透彻,站在巨人的肩膀上:

http://www.mamicode.com/info-detail-2735609.html

https://cloud.tencent.com/developer/article/1333056

走心Springboot源码解析: 二、SpringApplication.run()方法上部分

上一篇:vue中axios的post和get请求示例


下一篇:Android 高效的SQLite型数据库greenDAO使用