Spring的事件发布与监听

11 篇文章 0 订阅

一. 引言

​ 最近几天在深入学习了解下MQ相关的知识,做一个博客专栏。但最近在阅读RabbitMQ客户端代码的时候,遇到了很多Spring框架相关的陌生知识点,不断地去google,反倒是加深了对Spring相关的了解,比如今天在看到RabbitMQ的消息监听实现时,发现自己虽然在Spring源码解析时读过这一知识点,但在理解Rabbit消息监听实现时对事件的发布和监听还是没有头绪,所以在此好好学习记录下。

​ Spring构件的应用程序中,适当地使用事件发布与监听机制可以使我们的代码灵活度更高,降低耦合度。Spring提供了事件发布与监听的模型,在该模型中,事件发布方只需要将事件发布出去,无需关心有多少个对应的监听器;监听器无需关心是谁发布了事件,并且可以同时监听来自多个事件发布方的事件,通过这种机制,事件发布与监听是解耦的。

二.事件发布监听的案例

  • 自定义事件

    public class MyEvent extends ApplicationEvent {
        public MyEvent(Object source) {
            super(source);
        }
    }
    

    source指事件发布源头,通常是ApplicationContext

  • 事件发布

    @Component
    @Slf4j
    public class MyEventPublisher implements ApplicationEventPublisherAware, ApplicationContextAware {
        private ApplicationContext applicationContext;
        private ApplicationEventPublisher applicationEventPublisher;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    
        @Override
        public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
            this.applicationEventPublisher = applicationEventPublisher;
        }
    
        public void publishMyEvent(){
            log.info("发布事件开始");
            MyEvent myEvent = new MyEvent(applicationContext);
            applicationEventPublisher.publishEvent(myEvent);
            log.info("发布事件结束");
        }
    }
    

    自定义事件发布器MyEventPublisher,事件发布时需要使用到ApplicationEventPublisher进行发布。通过实现ApplicationEventPublisherAware接口,调用回调setApplicationEventPublisher方法,为applicationEventPublisher赋值;同样在构造事件时需要传入Spring上下文,通过实现ApplicationContextAware接口,在回调setApplicationContext方法时对applicationContext进行赋值。

  • 事件监听

    • 注解监听

      @Component
      @Slf4j
      public class MyAnnotationListener {
          @EventListener
          public void onMyEventPublished(MyEvent event){
              log.info("收到自定义事件MyEvent--MyAnnotationListener");
          }
      }
      

      通过@EventListener注解标注的方法入参为MyEvent类型,所以只要MyEvent事件被发布了,该监听器就会起作用,即该方法被回调。

    • 编程式监听

      @Component
      @Slf4j
      public class MyEventlistener implements ApplicationListener<MyEvent> {
          @Override
          public void onApplicationEvent(MyEvent event) {
              log.info("收到自定义事件MyEvent--MyEventlistener");
          }
      }
      

      通过实现ApplicationListener接口实现事件的监听,泛型的类型为监听的消息类型。

    • 测试

      @SpringBootApplication
      public class MyApplication {
      
          public static void main(String[] args) {
              ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args);
              MyEventPublisher publisher = context.getBean(MyEventPublisher.class);
              publisher.publishEvent();
          }
      }
      

三.事件发布监听过程

1. 事件发布监听原理

  • 事件发布者发布事件

    public void publishMyEvent(){
            log.info("发布事件开始");
            MyEvent myEvent = new MyEvent(applicationContext);
            applicationEventPublisher.publishEvent(myEvent);
            log.info("发布事件结束");
        }
    
  • 进入到AbstractpplicationContext类的publishEvent(ApplicationEvent event) 方法,进而调用publishEvent(Object event, @Nullable ResolvableType eventType)方法:主要是获取事件多播器,然后广播事件

    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
            Assert.notNull(event, "Event must not be null");
            Object applicationEvent;
            if (event instanceof ApplicationEvent) {
                applicationEvent = (ApplicationEvent)event;
            } else {
            //判断event是否是ApplicationEvent类型,是的话使用PayloadApplicationEvent包装
                applicationEvent = new PayloadApplicationEvent(this, event);
                if (eventType == null) {
                    eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
                }
            }
    
            if (this.earlyApplicationEvents != null) {
                this.earlyApplicationEvents.add(applicationEvent);
            } else {
              //获取事件多播器,然后广播事件
                this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
            }
    
            if (this.parent != null) {
                if (this.parent instanceof AbstractApplicationContext) {
                    ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
                } else {
                    this.parent.publishEvent(event);
                }
            }
    
        }
    
  • 获取事件多播器方法:getApplicationEventMulticaster,直接返回abstractApplicationContext的applicationEventMulticaster属性

    ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
            if (this.applicationEventMulticaster == null) {
                throw new IllegalStateException("ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: " + this);
            } else {
                return this.applicationEventMulticaster;
            }
        }
    
  • 调用SimpleApplicationEventMulticaster的multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType)方法进行事件广播

    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
            ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
            Executor executor = this.getTaskExecutor();
            Iterator var5 = this.getApplicationListeners(event, type).iterator();
    
            while(var5.hasNext()) {
                ApplicationListener<?> listener = (ApplicationListener)var5.next();
                if (executor != null) {
                    executor.execute(() -> {
                        this.invokeListener(listener, event);
                    });
                } else {
                    this.invokeListener(listener, event);
                }
            }
    
        }
    }
    
    • 获取事件的所有监听者
    • 若executor不为空,通过executor执行监听方法;否则直接调用监听方法
  • 调用监听方法实际上调用了listener的onApplicationEvent方法

    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()) && (!(event instanceof PayloadApplicationEvent) || !this.matchesClassCastMessage(msg, ((PayloadApplicationEvent)event).getPayload().getClass()))) {
                    throw var6;
                }
    
                Log loggerToUse = this.lazyLogger;
                if (loggerToUse == null) {
                    loggerToUse = LogFactory.getLog(this.getClass());
                    this.lazyLogger = loggerToUse;
                }
    
                if (loggerToUse.isTraceEnabled()) {
                    loggerToUse.trace("Non-matching event type for listener: " + listener, var6);
                }
            }
    
        }
    

2.多播器创建过程

可以看到多播器是从AbstractApplicationContext的applicationEventListener获取到的,接下来展开说明多播器是在何时创建的

  • 通过在AbstractApplicationContext的applicationEventMulticaster属性上打断点,在程序启动时程序跳转到了AbstractApplicationContext的initApplicationEventMulticaster方法

    protected void initApplicationEventMulticaster() {
            ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
            if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
                this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
                }
            } else {
              //===========此处赋值
                this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
                beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("No 'applicationEventMulticaster' bean, using [" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
                }
            }
    
        }
    
  • 跟踪堆栈信息得到调用过程

    • SpringApplication.run()

    • run(String… args)

    • refreshContext(context)

    • AbstractApplicationContext.refresh()

    • initApplicationEventMulticaster()

      protected void initApplicationEventMulticaster() {
              ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
              if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
                  this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
                  if (this.logger.isTraceEnabled()) {
                      this.logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
                  }
              } else {
                //=========此处赋值
                  this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
                  beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);
                  if (this.logger.isTraceEnabled()) {
                      this.logger.trace("No 'applicationEventMulticaster' bean, using [" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
                  }
              }
      
          }
      

3. 监听器获取过程

a. 编程实现监听器注册过程
  • 先查看获取监听器方法getApplicationListeners

    protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
            Object source = event.getSource();
            Class<?> sourceType = source != null ? source.getClass() : null;
            AbstractApplicationEventMulticaster.ListenerCacheKey cacheKey = new AbstractApplicationEventMulticaster.ListenerCacheKey(eventType, sourceType);
            AbstractApplicationEventMulticaster.CachedListenerRetriever newRetriever = null;
            AbstractApplicationEventMulticaster.CachedListenerRetriever existingRetriever = (AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.get(cacheKey);
            if (existingRetriever == null && (this.beanClassLoader == null || ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
                newRetriever = new AbstractApplicationEventMulticaster.CachedListenerRetriever();
                existingRetriever = (AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
                if (existingRetriever != null) {
                    newRetriever = null;
                }
            }
    
            if (existingRetriever != null) {
                Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
                if (result != null) {
                    return result;
                }
            }
    
            return this.retrieveApplicationListeners(eventType, sourceType, newRetriever);
        }
    
    • 通过事件类型和事件源构件监听器缓存键
    • 通过缓存键从缓存中获取监听器集合
    • 集合为空时,新建一个监听追踪器,放到缓存中,调用retrieveApplicationListeners方法获取当前事件的监听器集合
  • retrieveApplicationListeners

    • 从AbstractApplicationEventMulticaster的DefaultListenerRetriever属性获取所有事件的监听器和监听器Bean名称集合
    • 遍历所有事件监听器,将符合当前事件的监听器集合添加到相应集合中
    • 如果监听器Bean名称集合为空,遍历所有监听器集合,然后通过bean工厂从容器获取相应bean名称,并且类型为ApplicationListener的bean添加到集合中
    • 将过滤后的事件监听器集合赋值给事件缓存追踪对象的相应属性,再次获取时从缓存中获取
  • 最初是从AbstractApplicationEventMulticaster的DefaultListenerRetriever属性获取所有事件的监听器和监听器Bean名称集合,需要搞清楚这些属性是何时被赋值的

    • DefaultListenerRetriever.applicationListeners在addApplicationListener方法中被调用

      public void addApplicationListener(ApplicationListener<?> listener) {
      		synchronized (this.defaultRetriever) {
      			// Explicitly remove target for a proxy, if registered already,
      			// in order to avoid double invocations of the same listener.
      			Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
      			if (singletonTarget instanceof ApplicationListener) {
      				this.defaultRetriever.applicationListeners.remove(singletonTarget);
      			}
      			this.defaultRetriever.applicationListeners.add(listener);
      			this.retrieverCache.clear();
      		}
      	}
      
    • 往上追溯,在applicationContext的registerListeners方法中被调用

      protected void registerListeners() {
      		// Register statically specified listeners first.
      		for (ApplicationListener<?> listener : getApplicationListeners()) {
      			getApplicationEventMulticaster().addApplicationListener(listener);
      		}
      
      		// Do not initialize FactoryBeans here: We need to leave all regular beans
      		// uninitialized to let post-processors apply to them!
      		String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
      		for (String listenerBeanName : listenerBeanNames) {
      			getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
      		}
      
      		// Publish early application events now that we finally have a multicaster...
      		Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
      		this.earlyApplicationEvents = null;
      		if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
      			for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
      				getApplicationEventMulticaster().multicastEvent(earlyEvent);
      			}
      		}
      	}
      
    • 再追溯发现还是在SpringApplication.run方法中一步步调用的refresh方法

      public void refresh() throws BeansException, IllegalStateException {
      		synchronized (this.startupShutdownMonitor) {
      			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
      
      			// Prepare this context for refreshing.
      			prepareRefresh();
      
      			// Tell the subclass to refresh the internal bean factory.
      			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      
      			// Prepare the bean factory for use in this context.
      			prepareBeanFactory(beanFactory);
      
      			try {
      				// Allows post-processing of the bean factory in context subclasses.
      				postProcessBeanFactory(beanFactory);
      
      				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
      				// Invoke factory processors registered as beans in the context.
      				invokeBeanFactoryPostProcessors(beanFactory);
      
      				// Register bean processors that intercept bean creation.
      				registerBeanPostProcessors(beanFactory);
      				beanPostProcess.end();
      
      				// Initialize message source for this context.
      				initMessageSource();
      
      				// Initialize event multicaster for this context.
      				initApplicationEventMulticaster();
      
      				// Initialize other special beans in specific context subclasses.
      				onRefresh();
      
      				// Check for listener beans and register them.
      				registerListeners();
      
      				// Instantiate all remaining (non-lazy-init) singletons.
      				finishBeanFactoryInitialization(beanFactory);
      
      				// Last step: publish corresponding event.
      				finishRefresh();
      			}
      
      			catch (BeansException ex) {
      				if (logger.isWarnEnabled()) {
      					logger.warn("Exception encountered during context initialization - " +
      							"cancelling refresh attempt: " + ex);
      				}
      
      				// Destroy already created singletons to avoid dangling resources.
      				destroyBeans();
      
      				// Reset 'active' flag.
      				cancelRefresh(ex);
      
      				// Propagate exception to caller.
      				throw ex;
      			}
      
      			finally {
      				// Reset common introspection caches in Spring's core, since we
      				// might not ever need metadata for singleton beans anymore...
      				resetCommonCaches();
      				contextRefresh.end();
      			}
      		}
      	}
      
b 注解监听器注册过程
  • 看@RabbitListener注解源码

    /* @author Stephane Nicoll
     * @author Sam Brannen
     * @since 4.2
     * @see EventListenerMethodProcessor
     * @see org.springframework.transaction.event.TransactionalEventListener
     */
    @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface EventListener {
    }
    
  • 在EventListenerMethodProcessor中处理的注解监听,该类实现了SmartInitializingSingleton接口,回调afterSingletonsInstantiated方法

    public void afterSingletonsInstantiated() {
    		ConfigurableListableBeanFactory beanFactory = this.beanFactory;
    		Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
    		String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
    		for (String beanName : beanNames) {
    			if (!ScopedProxyUtils.isScopedTarget(beanName)) {
    				Class<?> type = null;
    				try {
    					type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
    				}
    				catch (Throwable ex) {
    					// An unresolvable bean type, probably from a lazy bean - let's ignore it.
    					if (logger.isDebugEnabled()) {
    						logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
    					}
    				}
    				if (type != null) {
    					if (ScopedObject.class.isAssignableFrom(type)) {
    						try {
    							Class<?> targetClass = AutoProxyUtils.determineTargetClass(
    									beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));
    							if (targetClass != null) {
    								type = targetClass;
    							}
    						}
    						catch (Throwable ex) {
    							// An invalid scoped proxy arrangement - let's ignore it.
    							if (logger.isDebugEnabled()) {
    								logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);
    							}
    						}
    					}
    					try {
    						processBean(beanName, type);
    					}
    					catch (Throwable ex) {
    						throw new BeanInitializationException("Failed to process @EventListener " +
    								"annotation on bean with name '" + beanName + "'", ex);
    					}
    				}
    			}
    		}
    	}
    

    获取所有bean,然后进行遍历,调用processBean方法

    private void processBean(final String beanName, final Class<?> targetType) {
    		if (!this.nonAnnotatedClasses.contains(targetType) &&
    				AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
    				!isSpringContainerClass(targetType)) {
    
    			Map<Method, EventListener> annotatedMethods = null;
    			try {
    				annotatedMethods = MethodIntrospector.selectMethods(targetType,
    						(MethodIntrospector.MetadataLookup<EventListener>) method ->
    								AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
    			}
    			catch (Throwable ex) {
    				// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
    				if (logger.isDebugEnabled()) {
    					logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
    				}
    			}
    
    			if (CollectionUtils.isEmpty(annotatedMethods)) {
    				this.nonAnnotatedClasses.add(targetType);
    				if (logger.isTraceEnabled()) {
    					logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
    				}
    			}
    			else {
    				// Non-empty set of methods
    				ConfigurableApplicationContext context = this.applicationContext;
    				Assert.state(context != null, "No ApplicationContext set");
    				List<EventListenerFactory> factories = this.eventListenerFactories;
    				Assert.state(factories != null, "EventListenerFactory List not initialized");
    				for (Method method : annotatedMethods.keySet()) {
    					for (EventListenerFactory factory : factories) {
    						if (factory.supportsMethod(method)) {
    							Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
    							ApplicationListener<?> applicationListener =
    									factory.createApplicationListener(beanName, targetType, methodToUse);
    							if (applicationListener instanceof ApplicationListenerMethodAdapter) {
    								((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
    							}
    							context.addApplicationListener(applicationListener);
    							break;
    						}
    					}
    				}
    				if (logger.isDebugEnabled()) {
    					logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
    							beanName + "': " + annotatedMethods);
    				}
    			}
    		}
    	}
    
    • 获取被@EventListener注解的方法
    • 如果找到了被@EventListener注解的方法,则通过事件监听工厂创建方法对应的ApplicationListener对象然后通过ApplicationListenerMethodAdapter适配,最终添加到上下文中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值