一、组成部分

spring的事件监听有三个部分组成,事件(ApplicationEvent)、监听器(ApplicationListener)和事件发布操作。

二、具体实现

事件

事件对象就是一个简单的po实体类

1、需要实现接口ApplicationEvent,

2、构造方法需要设置super

3、我们可以另外拓展一些子类来区分不同事件的实体

public abstract class BaseEvent<T> extends ApplicationEvent {

    protected T eventData;
protected String topic; /**
* Create a new {@code ApplicationEvent}.
*
* @param source 事件产生处所在类的对象
* @param eventData 表示在事件传播过程中,需要传输的对象,随意设置数据类型
* @param topic topic
*/
public BaseEvent(Object source, T eventData,String topic) {
super(source);
this.eventData = eventData;
this.topic = topic;
} public T getEventData() {
return eventData;
}
public String getTopic(){
return topic;
}
}

子类举例

public class SimpleEvent<T> extends BaseEvent {
//需要传输的事件主题
private String topic;
//用于接受事件的返回值
private List result;
public SimpleEvent(Object source) {
super(source);
}
public SimpleEvent(Object source, T eventData) {
super(source, eventData);
}
public SimpleEvent(Object source, T eventData, String topic) {
super(source,eventData);
this.topic = topic;
}
public void setResult(List<String> result) {
this.result = result;
}
public String getTopic() {
return topic;
}
public List<String> getResult() {
return result;
}
}

发布者

发布者很简单,需要注意三点

1、注入到spring容器中

2、引入应用上下文让我们的事件发布到流程中

3、调用 publishEvent 时传入事件po对象

@Component
public class SimpleEventPublisher<T> {
@Resource
ApplicationContext applicationContext;
public void publish(T msg) {
applicationContext.publishEvent(new SimpleEvent<>(this, msg));
}
public void publish(T msg, String topic) {
applicationContext.publishEvent(new SimpleEvent<>(this, msg, topic));
}
public List<String> publishAndReceive(T msg, String topic) {
SimpleEvent<T> simpleEvent = new SimpleEvent<>(this, msg, topic);
applicationContext.publishEvent(simpleEvent);
return simpleEvent.getResult();
}
}

监听者

两种实现方式

1、@EventListener 注解实现

  @EventListener 所在的监听方法是根据参数列表的EventPo类型来进行监听的,正常来讲对不同的事件po监听需要对应不同的监听方法。

  @EventListener 也有两个参数 classes(事件po类) 和 condition(满足条件走此方法) ,这两个参数默认不用填写

  如果多个监听方法的入参事件po列表一致,就会重复监听同一个事件。

2、类实现

  实现 ApplicationListener<事件PO>  并重写方法就行

@Component
public class SimpleEventListener {
public static final String eventBindTopic ="event#setNeedBindInfo";
public static final String eventBindBackTopic ="event#setBindBackTopic";
@Order(0)
@EventListener
public void doMsg(SimpleEvent<String> simpleEvent){
if (simpleEvent.getTopic().equals(eventBindTopic)){
UserEventDto eventData = (UserEventDto) simpleEvent.getEventData();
log.info("普通监听器-》接受到事件[{}],数据体【{}】",simpleEvent.getTopic(),eventData);
}else if (simpleEvent.getTopic().equals("event#setBindBackTopic")){
// 由于传入的事件与前面发布的事件内存地址指向的是同一个,这里可以设置值用于返回
simpleEvent.setResult(Collections.singletonList("这里是返回参数pong"));
log.info("有返回值监听器-》 topic【{}】,全数据体【{}】",simpleEvent.getTopic(), simpleEvent);
}else {
log.info("无匹配监听器 topic【{}】,数据体【{}】", simpleEvent.getTopic(),simpleEvent.getEventData());
}
}
}

三、原理探寻

一、解析@EventListener前的准备工作

1.1 EventListenerFactory和EventListenerMethodProcessor的注入

  EventListenerFactory 是把 @EventListener 标注的方法变成 ApplicationListener 的关键,其是在容器最初期(refresh方法发生前)就放到容器中去

  EventListenerMethodProcessor 是 @EventListener 的解析类,他是一个 SmartInitializingSingleton 和 BeanFactoryPostProcessor

  ApplicationListener 是把 @EventListener 包装了一层

  初始化的时候先判断,没有就注入 EventListenerMethodProcessor 和  DefaultEventListenerFactory 到容器中

源码如下

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, Object source) {
//获取对象
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); //org.springframework.context.event.internalEventListenerProcessor
//@EventListener注解处理器
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
//org.springframework.context.event.internalEventListenerProcessor
//内部管理的EventListenerFactory的bean名称
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}

1.2 EventListenerMethodProcessor和EventListenerFactory关系的建立

  EventListenerMethodProcessor会在容器启动时被注入到容器中,他是一个BeanFactoryPostProcessor

  EventListenerMethodProcessor和EventListenerFactory关系的建立,发生在其方法postProcessBeanFactory中

  EventListenerFactory的实例化时机只比BeanFactoryPostProcessor晚,比BeanPostProcessor实例化早

EventListenerMethodProcessor源码:

public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
@Nullable
private List<EventListenerFactory> eventListenerFactories;
//初始化eventListenerFactories
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
//获取容器中所有的EventListenerFactory,并把他们实例化
Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false); List<EventListenerFactory> factories = new ArrayList<>(beans.values());
AnnotationAwareOrderComparator.sort(factories);
//将EventListenerFactory储存到缓存eventListenerFactories中,便于后来使用
this.eventListenerFactories = factories;
}
}

二、开始解析@EventListener

  EventListenerMethodProcessor是一个SmartInitializingSingleton,所以他会在所以bean实例化后,执行其afterSingletonsInstantiated方法

  注意:只有单例的SmartInitializingSingleton,才会执行其afterSingletonsInstantiated方法

2.1 基本流程

  获取容器中所有的类,把用 @Component 标注的类上所有的 @EventListener 方法用 EventListenerFactory 解析成一个 ApplicationListener

public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {

    @Override
public void afterSingletonsInstantiated() {
ConfigurableListableBeanFactory beanFactory = this.beanFactory;
Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
// 这里厉害了,用Object.class 是拿出容器里面所有的Bean定义~~~ 一个一个的检查
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) {
if (logger.isDebugEnabled()) {
logger.debug("", 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 {
// 真正处理这个Bean里面的方法们。。。
processBean(beanName, type);
}
catch (Throwable ex) {
throw new BeanInitializationException("", ex);
}
}
}
}
} private void processBean(final String beanName, final Class<?> targetType) {
//类上有@Component注解
if (!this.nonAnnotatedClasses.contains(targetType) &&!targetType.getName().startsWith("java") &&!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);
}
}
//如果annotatedMethods为空,那代表类中没有用@EventListener标注的方法
if (CollectionUtils.isEmpty(annotatedMethods)) {
this.nonAnnotatedClasses.add(targetType);
if (logger.isTraceEnabled()) {
logger.trace("" + targetType.getName());
}
}
else {
// 类中存在用@EventListener标注的方法
ConfigurableApplicationContext context = this.applicationContext;
Assert.state(context != null, "No ApplicationContext set");
//获取容器中所有EventListenerFactory
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)) {
// 简单的说,就是把这个方法弄成一个可以执行的方法(主要和访问权限有关)
// 这里注意:若你是JDK的代理类,请不要在实现类里书写@EventListener注解的监听器,否则会报错的。(CGLIB代理的木关系)
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName)); //利用EventListenerFactory创建ApplicationListener,详情后面说
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, targetType, methodToUse);
//如果ApplicationListener是ApplicationListenerMethodAdapter类,那么执行其init方法
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}
//放到容器中
context.addApplicationListener(applicationListener);
//@EventListener方法只能解析一次
break;
}
}
}
if (logger.isDebugEnabled()) {
logger.debug();
}
}
}
}
}

注意:@EventListener方法只要有到一个可以解析他的EventListenerFactory,就不会让其他EventListenerFactory解析他了
所以如果容器中存在多个EventListenerFactory,我要注意他的顺序

2.2 EventListenerFactory解析@EventListener

EventListenerFactory 有2个实现类 DefaultEventListenerFactory 和 TransactionalEventListenerFactory

  DefaultEventListenerFactory:是处理 @EventListener,Spring默认就有

  TransactionalEventListenerFactory:是处理@TransactionalEventListener的,Spring默认是没有的,

  如果想要使用 @TransactionalEventListener 注解,就要注册一个TransactionalEventListenerFactory,也就是在启动类加上 @EnableTransactionManagement 注解

public interface EventListenerFactory {
//是否支持当前方法
boolean supportsMethod(Method method);
//生成一个ApplicationListener
ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method);
}

DefaultEventListenerFactory实现类

public class DefaultEventListenerFactory implements EventListenerFactory, Ordered {
private int order = LOWEST_PRECEDENCE;
@Override
public boolean supportsMethod(Method method) {
return true;
}
@Override
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
return new ApplicationListenerMethodAdapter(beanName, type, method);
} }

ApplicationListenerMethodAdapter 中的一个 ApplicationListener,他是用来包装@EventListener标注的方法

public class ApplicationListenerMethodAdapter implements GenericApplicationListener {

    private final String beanName; //@EventListener方法所属bean的名字
private final Method method;//@EventListener标注的方法
private final Method targetMethod;//@EventListener标注的真实方法对象,防止其是代理方法
//方法申明,如public void demo.Ball.applicationContextEvent(demo.OrderEvent)
private final AnnotatedElementKey methodKey;
private final List<ResolvableType> declaredEventTypes;//存储方法的参数
private final String condition;//@EventListener的condition
private final int order;
private ApplicationContext applicationContext;
private EventExpressionEvaluator evaluator;//@EventListener的EventExpressionEvaluator public ApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method) {
this.beanName = beanName;
this.method = BridgeMethodResolver.findBridgedMethod(method);
this.targetMethod = (!Proxy.isProxyClass(targetClass) ?AopUtils.getMostSpecificMethod(method, targetClass) : this.method);
this.methodKey = new AnnotatedElementKey(this.targetMethod, targetClass);
//获取方法上的@EventListener注解对象
EventListener ann = AnnotatedElementUtils.findMergedAnnotation(this.targetMethod, EventListener.class);
this.declaredEventTypes = resolveDeclaredEventTypes(method, ann);
this.condition = (ann != null ? ann.condition() : null);
this.order = resolveOrder(this.targetMethod);
} public void onApplicationEvent(ApplicationEvent event) {
processEvent(event);
} public void processEvent(ApplicationEvent event) {
Object[] args = resolveArguments(event);
//根据@EventListener的condition,判断是否要处理
if (shouldHandle(event, args)) {
//调用方法
Object result = doInvoke(args);
if (result != null) {
//如果有监听器可以监听这个结果,那么可以触发那个监听器
handleResult(result);
}
else {
logger.trace("No result object given - no result to handle");
}
}
}
}

spring事件发布与监听的更多相关文章

  1. Spring事件发布与监听机制

    我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. 目录 ...

  2. spring中的事件发布与监听

    点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. spring事件发布与监听的应用场景 当处理完一段代码逻辑,接下来需要同 ...

  3. Spring知识点回顾(07)事件发布和监听

    Spring知识点回顾(07)事件发布和监听 1.DemoEvent extends ApplicationEvent { public DemoEvent(Object source, String ...

  4. spring 自定义事件发布及监听(简单实例)

    前言: Spring的AppilcaitionContext能够发布事件和注册相对应的事件监听器,因此,它有一套完整的事件发布和监听机制. 流程分析: 在一个完整的事件体系中,除了事件和监听器以外,还 ...

  5. SpringBoot系列——事件发布与监听

    前言 日常开发中,我们经常会碰到这样的业务场景:用户注册,注册成功后需要发送邮箱.短信提示用户,通常我们都是这样写: /** * 用户注册 */ @GetMapping("/userRegi ...

  6. SpringBoot | 第三十二章:事件的发布和监听

    前言 今天去官网查看spring boot资料时,在特性中看见了系统的事件及监听章节.想想,spring的事件应该是在3.x版本就发布的功能了,并越来越完善,其为bean和bean之间的消息通信提供了 ...

  7. JavaEE开发之Spring中的事件发送与监听以及使用@Profile进行环境切换

    本篇博客我们就来聊一下Spring框架中的观察者模式的应用,即事件的发送与监听机制.之前我们已经剖析过观察者模式的具体实现,以及使用Swift3.0自定义过通知机制.所以本篇博客对于事件发送与监听的底 ...

  8. 关于JAVA中事件分发和监听机制实现的代码实例-绝对原创实用

    http://blog.csdn.net/5iasp/article/details/37054171 文章标题:关于JAVA中事件分发和监听机制实现的代码实例 文章地址: http://blog.c ...

  9. Pox启动及事件产生、监听分析

        ./pox/pox.py , Pox       实例化core=pox.core.initialize(),即为实例化POXCore类(该类是所有组件的交接点,提供组件注册功能),监听cor ...

  10. apiCloud事件发送与监听

    apiCloud事件发送与监听 1.sendEvent 将任意一个自定义事件广播出去,该事件可在任意页面通过 addEventListener 监听收到. sendEvent({params}) 2. ...

随机推荐

  1. 完整的javaweb文档

    1.index.jsp <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8&qu ...

  2. vim 基础

    光标移动(命令模式的上下左右):k,j,h,l 保存/退出 仅保存::w 退出::q(如果有修改要先保存) 保存并退出::wq(x效果一致) 强制退出::q! 模式 命令模式:esc(当前需要处于插入 ...

  3. FFmpeg 命令行

    FFmpeg命令行帮助 #>ffmpeg -h #>ffmpeg -h long #>ffmpeg -h full 将视频按照指定的宽高输出 #>ffmpeg -i input ...

  4. JavaScript基础知识整理(引用类型-Date)

    Date Date类型可以用来保存时间,保存的日期可以精确到1970年1月1日之后或之前的100000000天. 要创建Date类型对象,可以使用new操作符加构造函数. var now = new ...

  5. ajax异步参数详解及alax错误信息error分析

    一.$.ajax()的参数列表 ↑ 下面是Jquery中AJAX参数详细列表: 参数名 类型 描述 url String (默认: 当前页地址) 发送请求的地址. type String (默认: & ...

  6. Linux系统安装&VMware安装二

    第八步: 完成后,选择刚刚创建好的系统

  7. 关于js闭包的基础理解

    闭包 拿一个可以记录函数调用次数的来进行理解,如下方 let n = 0 function numUp(){ n++ console.log(n) } const fn = numUp() fn() ...

  8. ffmpeg命令参数

    用法 ffmpeg -i hello.mp3 -acodec libfdk_aac hello.aac mp3转aac ffmpeg -i hello.aac -acodec libmp3lame h ...

  9. android charles 抓不到https包,翻了2天资料总算是找到答案了

    问题, android7.0以上有权限限制,导致很多机子无法用Charles 抓ssl的包. 以下是我自己的坑请大家参考 原因一: 用Charles 工具上的地址去安装 chls.pro/ssl 原因 ...

  10. 设置mode='out-on'导致路由切换过快路由加载报错 Failed to execute 'insertBefore' on 'Node'

    原代码: 解决代码: 原因未知