• 走心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:

    所以说得到的 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所示


    其中就可以看出他们之间的关系了。

    开始跑第一个事件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

  • 相关阅读:
    ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint......
    模拟Executor策略的实现
    设计3D标签
    创建被图像填充的组件
    netty基础09_利用EmbeddedChannel做单元测试
    netty基础08_引导类
    netty基础07_Netty提供的消息处理器和编码解码器
    netty基础06_编码器和解码器
    netty基础05_管道和消息处理器
    netty基础04_数据缓冲区
  • 原文地址:https://www.cnblogs.com/disandafeier/p/12081290.html
Copyright © 2020-2023  润新知