ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。事件发布者并不需要考虑谁去监听,监听具体的实现内容是什么,发布者的工作只是为了发布事件而已。

Spring提供的内置事件:

  • ContextRefreshedEvent:容器刷新事件
  • ContextStartedEvent:容器启动事件
  • ContextStoppedEvent:容器停止事件
  • ContextClosedEvent:容器关闭事件

如何使用

监听容器的刷新事件

自定义一个ApplicationListener,指定监听的事件类型ContextRefreshedEvent:

package com.morris.spring.listener;import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;public class ContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {System.out.println("context refresh");}
}

注入到容器中:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(ContextRefreshedListener.class);
applicationContext.refresh();

applicationContext.refresh()内部会发送容器刷新的事件。

自定义事件

自定义的事件需要继承ApplicationEvent:

package com.morris.spring.event;import org.springframework.context.ApplicationEvent;public class CustomEvent extends ApplicationEvent {public CustomEvent(Object source) {super(source);}
}

监听的时候使用ApplicationEvent的子类CustomEvent:

package com.morris.spring.listener;import com.morris.spring.event.CustomEvent;
import org.springframework.context.ApplicationListener;public class CustomEventListener implements ApplicationListener<CustomEvent> {@Overridepublic void onApplicationEvent(CustomEvent event) {System.out.println("custom event: " + event.getSource());}
}

可以使用AnnotationConfigApplicationContext发布事件:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(CustomEventListener.class);
applicationContext.refresh();
applicationContext.publishEvent(new CustomEvent("custom event"));

可以向bean中注入一个ApplicationEventPublisher来发布事件:

package com.morris.spring.service;import com.morris.spring.event.CustomEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;public class CustomEventService {@Autowiredprivate ApplicationEventPublisher applicationEventPublisher;public void publishEvent() {applicationEventPublisher.publishEvent(new CustomEvent("自定义事件"));}
}

可以通过实现ApplicationEventPublisherAware接口注入ApplicationEventPublisher来发布事件:

package com.morris.spring.service;import com.morris.spring.event.CustomEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;public class CustomEventService2 implements ApplicationEventPublisherAware {private ApplicationEventPublisher applicationEventPublisher;public void publishEvent() {applicationEventPublisher.publishEvent(new CustomEvent("自定义事件"));}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}
}

由于ApplicationContext实现了ApplicationEventPublisher接口,也可以直接注入ApplicationContext来发布事件。

使用@EventListener监听事件

在监听事件时,由于类需要实现ApplicationListener接口,对代码有很大的侵入性,可以使用@EventListener注解随时随地监听事件,这样一个Service中可以监听多个事件:

package com.morris.spring.listener;import com.morris.spring.event.CustomEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;public class CustomEventListener2 {@EventListenerpublic void listenContextRefreshedEvent(ContextRefreshedEvent event) {System.out.println("context refresh");}@EventListenerpublic void listenCustomEvent(CustomEvent event) {System.out.println("custom event: " + event.getSource());}}

还可以在@EventListener注解上指定监听的事件类型:

package com.morris.spring.listener;import com.morris.spring.event.CustomEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;public class CustomEventListener3 {@EventListener({ContextRefreshedEvent.class, CustomEvent.class})public void listenEvent(ApplicationEvent event) {System.out.println(event);}
}

异步发送消息

spring消息的发送默认都是同步的,如果要异步发送消息,首先要在配置类上开启异步功能@EnableAsync:

package com.morris.spring.config;import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;@Configuration
@EnableAsync // 开启异步
public class EventListenerConfig {}

在监听的方法上加上@Async:

package com.morris.spring.listener;import com.morris.spring.event.CustomEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;@Slf4j
public class AsyncCustomEventListener {@EventListener({ContextRefreshedEvent.class, CustomEvent.class})@Async // 异步public void listenEvent(ApplicationEvent event) {log.info("receive event: {}", event);}
}

也可以自定义执行异步消息的线程池(默认就是SimpleAsyncTaskExecutor):

@Bean
public TaskExecutor executor() {return new SimpleAsyncTaskExecutor("eventListen-");
}

异步消息只是借用spring的异步执行机制,在方法上加上@Async注解,方法都会异步执行。

ApplicationListener原理分析

发布消息的入口

发布消息入口:
org.springframework.context.support.AbstractApplicationContext#publishEvent(org.springframework.context.ApplicationEvent)

public void publishEvent(ApplicationEvent event) {publishEvent(event, null);
}protected void publishEvent(Object event, @Nullable ResolvableType eventType) {.../*** @see SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)*/// 发布消息getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);}
... ...
}

然后调用SimpleApplicationEventMulticaster来进行广播消息:

// org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));// 如果有线程池,将会异步执行Executor executor = getTaskExecutor();for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {if (executor != null) {executor.execute(() -> invokeListener(listener, event));}else {invokeListener(listener, event);}}
}protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {ErrorHandler errorHandler = getErrorHandler();if (errorHandler != null) {try {doInvokeListener(listener, event);}catch (Throwable err) {errorHandler.handleError(err);}}else {// ApplicationListener.调用onApplicationEventdoInvokeListener(listener, event);}
}private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {listener.onApplicationEvent(event);}catch (ClassCastException ex) {String msg = ex.getMessage();if (msg == null || matchesClassCastMessage(msg, event.getClass())) {// Possibly a lambda-defined listener which we could not resolve the generic event type for// -> let's suppress the exception and just log a debug message.Log logger = LogFactory.getLog(getClass());if (logger.isTraceEnabled()) {logger.trace("Non-matching event type for listener: " + listener, ex);}}else {throw ex;}}
}

何时注入SimpleApplicationEventMulticaster

从上面的源码可以发现spring是通过SimpleApplicationEventMulticaster事件多播器来发布消息的,那么这个类是何时注入的呢?容器refresh()时。

org.springframework.context.support.AbstractApplicationContext#initApplicationEventMulticaster

protected void initApplicationEventMulticaster() {ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {this.applicationEventMulticaster =beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);if (logger.isTraceEnabled()) {logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");}}else {// 直接new,然后放入到spring一级缓存中this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);if (logger.isTraceEnabled()) {logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");}}
}

何时注入ApplicationListener

spring在发布消息时,会从SimpleApplicationEventMulticaster中拿出所有的ApplicationListener,那么这些ApplicationListener何时被注入的呢?容器refresh()时。

org.springframework.context.support.AbstractApplicationContext#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) {// 添加到SimpleApplicationEventMulticaster中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);}}
}

@EventListener的原理

@EventListener注解的功能是通过EventListenerMethodProcessor来实现的,EventListenerMethodProcessor这个类在AnnotationConfigApplicationContext的构造方法中被注入。

EventListenerMethodProcessor主要实现了两个接口:SmartInitializingSingleton和BeanFactoryPostProcessor。

先来看看BeanFactoryPostProcessor的postProcessBeanFactory(),这个方法主要是保存beanFactory和eventListenerFactories,后面的方法将会使用到:

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {// 保存beanFactorythis.beanFactory = beanFactory;/*** EventListenerFactory[DefaultEventListenerFactory]在何处被注入?* @see AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)*/Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);List<EventListenerFactory> factories = new ArrayList<>(beans.values());AnnotationAwareOrderComparator.sort(factories);// 保存eventListenerFactoriesthis.eventListenerFactories = factories;
}

再来看看SmartInitializingSingleton的afterSingletonsInstantiated()方法,这个方法会在所有的bean初始化完后执行。

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);}}}}
}
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 {// 获得类中所有的带有@EventListener注解的方法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 methodsConfigurableApplicationContext 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));// 使用factory创建一个ApplicationListenerApplicationListener<?> applicationListener =factory.createApplicationListener(beanName, targetType, methodToUse);if (applicationListener instanceof ApplicationListenerMethodAdapter) {((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);}// 将ApplicationListener添加到Spring容器中context.addApplicationListener(applicationListener);break;}}}if (logger.isDebugEnabled()) {logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +beanName + "': " + annotatedMethods);}}}
}

SpringBoot中的事件

SpringBoot在启动时会按以下顺序发送消息:

  1. ApplicationStartingEvent:在运行开始时发送 ,但在进行任何处理之前(侦听器和初始化程序的注册除外)发送
  2. ApplicationEnvironmentPreparedEvent:当被发送Environment到中已知的上下文中使用,但是在创建上下文之前
  3. ApplicationContextInitializedEvent:在ApplicationContext准备好且已调用ApplicationContextInitializers之后但任何bean定义未加载之前发送
  4. ApplicationPreparedEvent:在刷新开始之前但在加载bean定义之后发送
  5. ApplicationStartedEvent:上下文已被刷新后发送,但是任何应用程序和命令行都被调用前
  6. ApplicationReadyEvent:在所有的命令行应用启动后发送此事件,可以处理请求
  7. ApplicationFailedEvent:在启动时异常发送

【spring】Spring事件监听器ApplicationListener的使用与源码分析相关推荐

  1. spring拦截器覆盖_springmvc拦截器及源码分析

    前言 springmvc拦截器是我们项目开发中用到的一个功能,常常用于对Handler进行预处理和后处理.本案例来演示一个较简单的springmvc拦截器的使用,并通过分析源码来探究拦截器的执行顺序是 ...

  2. Spring配置详解,Spring配置元信息详解,Spring配置大全及源码分析

    文章目录 一.Spring都可以配置哪些元信息 二.Spring Bean 配置元信息 1.GenericBeanDefinition 2.RootBeanDefinition 3.Annotated ...

  3. View事件分发机制(源码分析篇)

    01.Android中事件分发顺序 1.1 事件分发的对象是谁 事件分发的对象是事件.注意,事件分发是向下传递的,也就是父到子的顺序. 当用户触摸屏幕时(View或ViewGroup派生的控件),将产 ...

  4. Spring5源码 - 13 Spring事件监听机制_@EventListener源码解析

    文章目录 Pre 概览 开天辟地的时候初始化的处理器 @EventListener EventListenerMethodProcessor afterSingletonsInstantiated 小 ...

  5. 【Spring框架】 ☞ 项目启动时执行特定处理及ApplicationListener源码分析

    1.背景 在一些业务场景中,在容器启动完成后,需要处理一些诸如:kafka业务注册,数据处理,初始化缓存等的操作. 本文重点介绍如何在服务启动中,或启动完成时执行相关处理. 2.针对上述场景,有如下实 ...

  6. spring boot 源码分析(七) 事件机制 之 SpringApplicationEvent

    2019独角兽企业重金招聘Python工程师标准>>> 一.前言 前面的文章我们讲解了一下spring boot配置文件加载的相关源码分析,下面我们将从源码角度讲解一下spring  ...

  7. spring boot实战(第六篇)加载application资源文件源码分析

    前言 在上一篇中了解了spring配置资源的加载过程,本篇在此基础上学习spring boot如何默认加载application.xml等文件信息的. ConfigFileApplicationLis ...

  8. 【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  9. Spring Boot 2.x 启动全过程源码分析(上)入口类剖析

    转载自   Spring Boot 2.x 启动全过程源码分析(上)入口类剖析 Spring Boot 的应用教程我们已经分享过很多了,今天来通过源码来分析下它的启动过程,探究下 Spring Boo ...

最新文章

  1. vivado烧写bin文件到flash 中
  2. 在图数据上做机器学习,应该从哪个点切入?
  3. Vue中数组赋值问题
  4. Swift 4.2正式发布
  5. ITK:复制过滤器filter
  6. C--数据结构--树的学习
  7. 火力发电厂工控系统网络安全解决方案 - 对比分析
  8. Android 动画 Kotlin 教程
  9. Java8 新特性之 Stream 练习题
  10. 区块链和人工智能是否可以保护森林不再被砍伐?
  11. 爬取分析拉勾网招聘信息
  12. Microsoft Visual Studio 2003 2005 2008 2010 2012 下载
  13. 微信公众平台网页授权
  14. ubuntu系统怎么更换搜狗输入法键盘皮肤+windows系统下.ssf文件无法打开
  15. 图片怎么转换文字?识别渠道一览
  16. 微信防屏蔽域名防封 爆红域名如何在微信打开
  17. win10系统桌面应用图标显示不出来的问题
  18. python爬取IT之家业界新闻
  19. a1 抛光等级spi_SPI 抛光的表面等级对应的砂纸和钻石膏
  20. Go语言fmt包Printf方法格式化参数详解

热门文章

  1. 小米6cpu体质测试软件,小米6的CPU是什么?CPU主频是多少?
  2. 【转】用ATL创建COM组件详细解说
  3. 查看数据库锁表以及解锁
  4. C# 读取DXF全套实例 图形输出 控制台输出 dxf全部信息
  5. 自定义周次开始时间,结束时间,计算一年中的周次(非自然周次)
  6. 使用SurfaceView+Camera模仿照相机拍照功能
  7. 科技云报道:构建可观测性的核心能力是什么?
  8. 工商银行APP流水申请
  9. Unity3d-粒子光环
  10. Brainf**k 程序设计