springboot2.0.6 启动过程(二)事件发布

springboot启动二

  • 上一章说到了run方法,主要分了10个步骤下面我就分别看下这些步骤都做了什么

    • 属性配置不用说了 直接看第二步事件发布过程。
      主要代码如下
    //获取监听容器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    //发布事件
    listeners.starting();
    
    • 这里可以看到,事件发布有两个步骤,第一获取监听,第二发布事件。我们先看如何拿到监听的。
        private SpringApplicationRunListeners getRunListeners(String[] args) {
            Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
            //返回监听容器(getSpringFactoriesInstances方法加载了监听)
            return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
                    SpringApplicationRunListener.class, types, this, args));
        }
        .....
        代码
        .....
        //这里是SpringApplicationRunListeners的构造函数
        SpringApplicationRunListeners(Log log,
    		Collection<? extends SpringApplicationRunListener> listeners) {
    	this.log = log;
    	this.listeners = new ArrayList<>(listeners);
    }
    
    
    • 是不是有看到了熟悉的getSpringFactoriesInstances方法,这个方法按类型加载对应的spring.factories文件内的类并实例化,此步骤就是将监听全部加载实例化后,添加到了监听容器中即SpringApplicationRunListeners,下面我们事件发布starting()方法做了什么。
    	public void starting() {
    	for (SpringApplicationRunListener listener : this.listeners) {
    		listener.starting();
    	}
    }
        //跟代码进去后可以找到EventPublishingRunListener,此类作用主要是发布SpringApplicationEvent事件
        ,EventPublishingRunListener有好多种事件发布方法,针对启动过程不同而定的。比如 environmentPrepared contextLoaded等
       // 我们先看下构造方法
        	public EventPublishingRunListener(SpringApplication application, String[] args) {
    	this.application = application;
    	this.args = args;
        // 事件发布器
    	this.initialMulticaster = new SimpleApplicationEventMulticaster();
        // 监听器初始化,将之前set到SpringApplication内的监听复制到发布器属性内
    	for (ApplicationListener<?> listener : application.getListeners()) {
    		this.initialMulticaster.addApplicationListener(listener);
    	}
    }
       //我们看下事件如何发布的
    	@Override
        public void starting() {
            this.initialMulticaster.multicastEvent(
            //这里可以看到,我们发布的是ApplicationStartingEvent事件
                    new ApplicationStartingEvent(this.application, this.args));
        }
    
    • 我们看下SimpleApplicationEventMulticaster如何发布的,主要看如下方法
         
        @Override
        public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
     	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        //getApplicationListeners(event, type)是整个过程关键一步,筛选出符合的listner,通过事件类型
     	for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            //获取异步执行器,这里可以自己添加如果没有直接同步执行
     		Executor executor = getTaskExecutor();
     		if (executor != null) {
     			executor.execute(() -> invokeListener(listener, event));
     		}
     		else {
     			invokeListener(listener, event);
     		}
     	}
       } 
        //这里可以看下getApplicationListeners实现
       	protected Collection<ApplicationListener<?>> getApplicationListeners(
     		ApplicationEvent event, ResolvableType eventType) {
         //获取到事件发生的原始类,这里其实就是SpringApplication
     	Object source = event.getSource();
         //获取到SpringApplication的class对象
     	Class<?> sourceType = (source != null ? source.getClass() : null);
     	ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
    
     	// Quick check for existing entry on ConcurrentHashMap...
     	ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
     	if (retriever != null) {
     		return retriever.getApplicationListeners();
     	}
         //这里是缓存listner的缓存过程 这里会检查给定的类在给定的上下文中是否是缓存安全的
     	if (this.beanClassLoader == null ||
     			(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
     					(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
     		// Fully synchronized building and caching of a ListenerRetriever
     		synchronized (this.retrievalMutex) {
     			retriever = this.retrieverCache.get(cacheKey);
     			if (retriever != null) {
     				return retriever.getApplicationListeners();
     			}
     			retriever = new ListenerRetriever(true);
     			Collection<ApplicationListener<?>> listeners =
     					retrieveApplicationListeners(eventType, sourceType, retriever);
     			this.retrieverCache.put(cacheKey, retriever);
     			return listeners;
     		}
     	}
     	else {
     		// No ListenerRetriever caching -> no synchronization necessary
             //主要看这个方法
     		return retrieveApplicationListeners(eventType, sourceType, null);
     	}
     }
     // retrieveApplicationListeners这里就是开始筛选listener,里面调用了supportsEvent方法
     	protected boolean supportsEvent(
     		ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {
         //判断具体是什么类型的Listener 
     	GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
     			(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
     	return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
     }
    
    • 这里其实就是调用每个具体listener的方法,来判断是否满足给定的条件,这里我们可以确定是SpringApplication与
      ApplicationStartingEvent类型参数,那我们就明确了,直接找在SpringApplication发起的并且是
      ApplicationStartingEvent类型事件的监听,我们以LoggingApplicationListener为例查看
     	public boolean supportsEventType(ResolvableType resolvableType) {
       return isAssignableFrom(resolvableType.getRawClass(), EVENT_TYPES);
      }
     /**我们发现 这里是listener已经定义好 它需要监听什么类型事件的集合 EVENT_TYPES 并且监听的是什么地方发生的事件的 
     *  集合SOURCE_TYPES。这里不一一列举了可以查看源码 。然后能找到了listener,我们就需要调用onApplicationEvent开始执行逻辑了
     */
     
    
    • 这里我们就可以大概了解到,springboot事件发布的过程了。其实原理就是在哪里发生的什么事。监听器呢自己定义好,我要管那里的什么事就ok了。
    • 这里我们写程序的时候就可以根据这个特点自定义监听,随着spring启动过程,做各种自己想要干的事情,是不是很舒服。
    • 自定义事件可以参考者篇 博客,笔者认为已经写的非常透彻了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值