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应用启动内置Tomcat的过程源码分析

    SpringBoot应用启动内置Tomcat的过程源码分析

    这篇文章主要介绍了SpringBoot应用启动内置Tomcat的过程分析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-07-07
  • Mybatis-Plus3.x的创建步骤及使用教程

    Mybatis-Plus3.x的创建步骤及使用教程

    MyBatis-Plus是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为 简化开发、提高效率而生,这篇文章主要介绍了Mybatis-Plus3.x的使用,需要的朋友可以参考下
    2023-10-10
  • SpringSecurity6自定义JSON登录的实现

    SpringSecurity6自定义JSON登录的实现

    目前最新版的Spring Boot已经到了3.0.5了,随之而来Spring Security 目前的版本也到了6.0.2了,Spring Security写法的变化特别多,本文就来介绍下,感兴趣的可以了解一下
    2023-12-12
  • Java 存储模型和共享对象详解

    Java 存储模型和共享对象详解

    这篇文章主要介绍了Java 存储模型和共享对象详解的相关资料,对Java存储模型,可见性和安全发布的问题是起源于Java的存储结构及共享对象安全,需要的朋友可以参考下
    2017-03-03
  • 关于文件上传MultipartBody的使用方法

    关于文件上传MultipartBody的使用方法

    这篇文章主要介绍了关于文件上传MultipartBody的使用方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Java权重随机的实现方法

    Java权重随机的实现方法

    这篇文章主要介绍了Java权重随机的实现方法,实例分析了权重随机算法的原理与完整实现方法,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-01-01
  • Java Junit单元测试实例详解

    Java Junit单元测试实例详解

    在本篇文章里小编给大家分享的是关于Java Junit单元测试的相关知识点内容,有兴趣的朋友们学习下。
    2019-11-11
  • Java面向对象之猜拳游戏

    Java面向对象之猜拳游戏

    这篇文章主要为大家详细介绍了Java面向对象之猜拳游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-05-05
  • Spring Boot如何接入Security权限认证服务

    Spring Boot如何接入Security权限认证服务

    Spring Security 是一个高度可定制的身份验证和访问控制的框架,提供了完善的认证机制和方法级的授权功能,本文通过案例将Spring Security整合到SpringBoot中,要实现的功能就是在认证服务器上登录,然后获取Token,再访问资源服务器中的资源,感兴趣的朋友一起看看吧
    2024-07-07
  • 基于Spring的RPC通讯模型的使用与比较

    基于Spring的RPC通讯模型的使用与比较

    这篇文章主要介绍了基于Spring的RPC通讯模型的使用与比较,详细的介绍了RMI、Caucho的Hessian和Burlap以及Spring自带的HTTP invoker,感兴趣的可以了解一下
    2018-09-09

最新评论