这次终于把Spring的监听器讲明白了

简介: 监听器可以在使用过程时监听某些事件的发生,然后对这些事件做出响应处理。监听器对应用场景很多,用户的每一个操作都可以被定义为一个事件,通过监听器就能对某些业务场景中的事件进行监听。Spring中提供了ApplicationListener监听事件,本文会从应用出发讲解Spring的监听器,并逐步深入到源码之中。

听说微信搜索《Java鱼仔》会变更强哦!


本文收录于githubgitee ,里面有我完整的Java系列文章,学习或面试都可以看看哦


(一)Spring中的监听器


监听器可以在使用过程时监听某些事件的发生,然后对这些事件做出响应处理。监听器对应用场景很多,用户的每一个操作都可以被定义为一个事件,通过监听器就能对某些业务场景中的事件进行监听。


Spring中提供了ApplicationListener监听事件,本文会从应用出发讲解Spring的监听器,并逐步深入到源码之中。


(二)监听器的使用


首先自定义一个事件,只需要继承 ApplicationEvent 就可以创建一个自己的事件。


publicclassMyApplicationEventextendsApplicationEvent {
publicMyApplicationEvent(Objectsource) {
super(source);
    }
}

接着定义一个事件监听器,实现当监听到事件后在控制台输出一条消息。

@ComponentpublicclassMyListenerimplementsApplicationListener<MyApplicationEvent> {
@OverridepublicvoidonApplicationEvent(MyApplicationEventapplicationEvent) {
System.out.println("事件:"+applicationEvent.toString());
    }
}

在配置类中增加包扫描地址。

@Configuration@ComponentScan(basePackages="com.javayz.listenerDemo")
publicclassMainConfig {
}

网络异常,图片无法展示
|


通过结果可以发现,除了自定义的事件之外,还有一条Spring自带的事件也被打印出来,修改监听器,只打印自定义的事件:

@ComponentpublicclassMyListenerimplementsApplicationListener<ApplicationEvent> {
@OverridepublicvoidonApplicationEvent(ApplicationEventapplicationEvent) {
if (applicationEventinstanceofMyApplicationEvent){
System.out.println("事件:"+applicationEvent.toString());
        }
    }
}

(三)监听器源码分析:


通过 AnnotationConfigApplicationContext 进入Spring源码内部:

publicAnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
   }

这三个方法是Spring源码的核心,和监听器相关的内容在refresh方法中。进入refresh方法,和监听器相关的三个方法是initApplicationEventMulticaster()、registerListeners()和finishRefresh();


网络异常,图片无法展示
|


首先看initApplicationEventMulticaster()


protectedvoidinitApplicationEventMulticaster() {
ConfigurableListableBeanFactorybeanFactory=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 {
this.applicationEventMulticaster=newSimpleApplicationEventMulticaster(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() +"]");
         }
      }
   }

这一段代码很好理解,首先获取ConfigurableListableBeanFactory工厂,然后判断现在Bean工厂里是否有applicationEventMulticaster,如果有的话就获取该多播器,如果没有就新建一个SimpleApplicationEventMulticaster简单多播器并注册到Bean工厂中,这段代码相当于初始化一个多播器。


接下来是registerListeners()方法:


protectedvoidregisterListeners() {
// 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 (StringlistenerBeanName : 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 (ApplicationEventearlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
         }
      }
   }

上面这段注册监听器的代码一共做了三件事情,第一件事情是容器中已有的监听器注册到多播器中;第二件事情是将Bean定义中的事件,也就是我们自己定义的监听器注册到多播器中;第三件事情是发布早期待处理事件,这些早期事件我们定义的监听器是监听不了的,这里发布早期待处理事件时通过multicastEvent()方法进行发布,这个方法会和下面一起讲。


最后看finishRefresh方法,该方法调用的publishEvent方法正式将我们定义的事件发布出去。



protectedvoidfinishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).clearResourceCaches();
// Initialize lifecycle processor for this context.initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.getLifecycleProcessor().onRefresh();
// Publish the final event.publishEvent(newContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.if (!NativeDetector.inNativeImage()) {
LiveBeansView.registerApplicationContext(this);
   }
}

进入publishEvent方法内部,可以看到它的内部同样调用了multicastEvent()方法,说明所有的真正发布动作都是由multicastEvent()完成的。


网络异常,图片无法展示
|


因此我们有必要来看一下multicastEvent()的发布过程,这里的代码也通俗易懂,判断是否有Executor,如果有的话异步加载invokeListener方法,没有的话同步调用invokeListener方法


publicvoidmulticastEvent(finalApplicationEventevent, @NullableResolvableTypeeventType) {
ResolvableTypetype= (eventType!=null?eventType : resolveDefaultEventType(event));
Executorexecutor=getTaskExecutor();
for (ApplicationListener<?>listener : getApplicationListeners(event, type)) {
if (executor!=null) {
executor.execute(() ->invokeListener(listener, event));
      }
else {
invokeListener(listener, event);
      }
   }
}

在invokeListener()方法中,终于看到了熟悉的onApplicationEvent方法,这个方法就是我们在自定义监听器时重写的方法,也正是在这里监听器调用了我们自己定义的onApplicationEvent(),实现了自定义的一些功能。


网络异常,图片无法展示
|


(四)总结


通过代码的实践以及源码的解读,监听的原理其实已经很明朗了,Spring的监听器源码可以算是观察者模式的最佳实践,我建议你照着本文的逻辑看一遍监听器源码,肯定会有新的收获。



相关文章
|
6月前
|
Web App开发 监控 Java
|
1月前
|
设计模式 Java Spring
Spring Boot监听器的底层实现原理
Spring Boot监听器的底层实现原理主要基于观察者模式(也称为发布-订阅模式),这是设计模式中用于实现对象之间一对多依赖的一种常见方式。在Spring Boot中,监听器的实现依赖于Spring框架提供的事件监听机制。
26 1
|
3月前
|
缓存 Java 数据库
Spring Boot中使用监听器
系统的介绍了监听器原理,以及在 Spring Boot 中如何使用监听器,列举了监听器的三个常用的案例,有很好的实战意义。最后讲解了项目中如何自定义事件和监听器,并结合微服务中常见的场景,给出具体的代码模型,均能运用到实际项目中去,希望读者认真消化。
|
6月前
|
NoSQL Java Redis
Spring Boot 监听 Redis Key 失效事件实现定时任务
Spring Boot 监听 Redis Key 失效事件实现定时任务
141 0
|
Java Spring
spring boot中提供了一些监听方法,现在我需要在系统启动前完成一些操作。用什么方法实现或者注解?
spring boot中提供了一些监听方法,现在我需要在系统启动前完成一些操作。用什么方法实现或者注解?
|
5月前
|
监控 NoSQL Java
在 Spring Boot 中实现 Redis 的发布/订阅功能可以通过 RedisTemplate 和消息监听器来完成
在 Spring Boot 中实现 Redis 的发布/订阅功能可以通过 RedisTemplate 和消息监听器来完成
256 1
|
6月前
|
Java Spring
flowable 监听器无法获取spring bean ,自动注入bean为null,解决方案
flowable 监听器无法获取spring bean ,自动注入bean为null,解决方案
|
6月前
|
NoSQL Java Redis
Spring boot 实现监听 Redis key 失效事件
【2月更文挑战第2天】 Spring boot 实现监听 Redis key 失效事件
579 0
|
6月前
spring-state-machine监听器
spring-state-machine监听器
45 0