Spring的事件发布与监听方式案例讲解

 更新时间:2023年03月27日 11:16:16   作者:T.Y.Bao  
今天去官网查看spring boot资料时,在特性中看见了系统的事件及监听章节,所以下面这篇文章主要给大家介绍了关于SpringBoot事件发布和监听的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

事件

主要代码在org.springframework.contextorg.springframework.context.event包中

事件发布与监听主要包含以下角色:

  • 事件:ApplicationEvent
  • 事件监听器:ApplicationListener SmartApplicationListener GenericApplicationListener
  • 事件发布器:ApplicationEventPublisher
  • 事件广播器:ApplicationEventMulticaster

引入ApplicationListener有两种方式:

  • spring spi
  • 手动注入bean

手动注入bean有两种方式:

  • 类上注解@Component等注解+实现ApplicationListener接口
  • 类上注解@Component等注解+方法上注解@EventListener

案例如下:

// bean注入方式一,实现ApplicationListener+@Component注入bean
@Component
public class HelloEventListener implements SmartApplicationListener {
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return false;
    }
    @Override
    public void onApplicationEvent(ApplicationEvent event) {

    }
}
// bean注入方式二,@EventListener+@Component
@Component
public class Test {
    @EventListener
    public void listen(Object obj){
        System.out.println("listening");
    }
    @EventListener(classes={ApplicationEvent.class},condition="springEL")
    public void listen(ApplicationEvent event){
        System.out.println("listening");
    }
}

关于@EventListener注解方法注入是通过EventListenerMethodProcessor的一个SmartInitializingSingleton,同时该类也是一个BeanFactoryPostProcessor,但扫描@EventListener方法和注入逻辑不在该接口的postProcess方法中,而是SmartInitializingSingleton接口的afterSingletonsInstantiated方法。

关于SmartInitializingSingleton的接口作用注释如下:

Callback interface triggered at the end of the singleton pre-instantiation phase during BeanFactory bootstrap. This interface can be implemented by singleton beans in order to perform some initialization after the regular singleton instantiation algorithm, avoiding side effects with accidental early initialization (e.g. from ListableBeanFactory.getBeansOfType calls). In that sense, it is an alternative to InitializingBean which gets triggered right at the end of a bean’s local construction phase.

看到其作用和 InitializingBean 类似,用于构造函数后的初始化操作,不过该接口是所有bean被创建之后被调用。在所有 bean的构造方法、初始化(@PostConstructInitializingBean)、BeanPostProcessor都执行完毕后再执行该接口方法,注意是所有bean都执行完这些方法。

Invoked right at the end of the singleton pre-instantiation phase, with a guarantee that all regular singleton beans have been created already.

public class EventListenerMethodProcessor
		implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
	// 负责设置EventListenerFactory
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		// 回调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);
		// 设置eventListenerFactories
		this.eventListenerFactories = factories;
	}
	@Override
	public void afterSingletonsInstantiated() {
		...
		processBean(beanName, type);
		...
	}
	private void processBean(final String beanName, final Class<?> targetType) {
		if (
				// 不包含@EventListener的类的备忘录是否有该类型
				!this.nonAnnotatedClasses.contains(targetType) &&
				// 该类型的type, method or field 是否能被注解@EventListener
				AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
				// 不能是org.springframework开头的类,或者被注解了@Component,注意是或者
				!isSpringContainerClass(targetType)
			) {
			// 提取所有的方法
			Map<Method, EventListener> annotatedMethods = null;
			try {
				annotatedMethods = MethodIntrospector.selectMethods(targetType,
						(MethodIntrospector.MetadataLookup<EventListener>) method ->
								AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
			}
			...
			if (CollectionUtils.isEmpty(annotatedMethods)) {
				// 备忘录,加入已扫描的没有注解@EventListener的类
				this.nonAnnotatedClasses.add(targetType);
				...
			}
			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<?> applicationListener =
									factory.createApplicationListener(beanName, targetType, methodToUse);
							if (applicationListener instanceof ApplicationListenerMethodAdapter) {
								((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
							}
							context.addApplicationListener(applicationListener);
							break;
						}
	...

ApplicationListener监听到事件后的执行是同步过程,如果需要异步,可搭配@Async+@EventListener

事务消息监听器

spring-tx包下提供TransactionalApplicationListener接口和@TransactionalEventListener注解。

TransactionalApplicationListener接口:An ApplicationListener that is invoked according to a TransactionPhase. NOTE: Transactional event listeners only work with thread-bound transactions managed by a PlatformTransactionManager.

到此这篇关于Spring的事件发布与监听方式案例讲解的文章就介绍到这了,更多相关Spring事件发布与监听内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot整合quartz定时任务框架的完整步骤

    springboot整合quartz定时任务框架的完整步骤

    在做项目时有时候会有定时器任务的功能,比如某某时间应该做什么,多少秒应该怎么样之类的,下面这篇文章主要给大家介绍了关于springboot整合quartz定时任务框架的相关资料,需要的朋友可以参考下
    2022-01-01
  • 详谈Java枚举、静态导入、自动拆装箱、增强for循环、可变参数

    详谈Java枚举、静态导入、自动拆装箱、增强for循环、可变参数

    下面小编就为大家带来一篇详谈Java枚举、静态导入、自动拆装箱、增强for循环、可变参数。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • Spring中Bean的命名方式代码详解

    Spring中Bean的命名方式代码详解

    这篇文章主要介绍了Spring中Bean的命名方式代码详解,具有一定借鉴价值,需要的朋友可以参考下
    2018-01-01
  • java设计模式:建造者模式之生产线

    java设计模式:建造者模式之生产线

    这篇文章主要介绍了Java设计模式之建造者模式,结合具体实例形式分析了建造者模式的概念、原理、实现方法与相关使用注意事项,需要的朋友可以参考下
    2021-08-08
  • java 基本算法之归并排序实例代码

    java 基本算法之归并排序实例代码

    这篇文章主要介绍了java 基本算法之归并排序实例代码的相关资料,需要的朋友可以参考下
    2017-05-05
  • Nacos作为配置中心注册监听器方法

    Nacos作为配置中心注册监听器方法

    本文主要讨论Nacos作为配置中心时,其中配置内容发生更改时,我们的应用程序能够做的事。一般使用监听器来实现这步操作,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-02-02
  • Java中的Semaphore源码分析

    Java中的Semaphore源码分析

    这篇文章主要介绍了Java中的Semaphore源码分析,Semaphore是一个访问公共资源的线程数量如限流、停车等,它是一个基于AQS实现的共享锁,主要是通过控制state变量来实现,需要的朋友可以参考下
    2023-11-11
  • Java equals 方法与hashcode 方法的深入解析

    Java equals 方法与hashcode 方法的深入解析

    面试时经常会问起字符串比较相关的问题,比如:字符串比较时用的什么方法,内部实现如何?hashcode的作用,以及重写equal方法,为什么要重写hashcode方法?以下就为大家解答,需要的朋友可以参考下
    2013-07-07
  • 新手了解java 多线程基础知识

    新手了解java 多线程基础知识

    这篇文章主要介绍了java 基础教程之多线程详解及简单实例的相关资料,线程的基本属性、如何创建线程、线程的状态切换以及线程通信,需要的朋友可以参考下
    2021-07-07
  • Java实现AWT四大事件的详细过程

    Java实现AWT四大事件的详细过程

    AWT的事件处理是一种委派式事件处理方式:普通组件(事件源)将整个事件处理委托给特定的对象(事件监听器);当该事件源发生指定的事件时,就通知所委托的事件监听器,由事件监听器来处理这个事件
    2022-04-04

最新评论