Spring 事件监听

Posted 街头卖艺的肖邦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 事件监听相关的知识,希望对你有一定的参考价值。

  Spring 的核心是 ApplicationContext,它负责管理 Bean的完整生命周期;当加载 Bean 时,ApplicationContext 发布某些类型的事件;例如,当上下文启动时,ContextStartedEvent 发布消息,当上下文停止时,ContextStoppedEvent 发布消息;

  通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件;如果一个 Bean 实现 ApplicationListener 接口,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 Bean实例会被通知;类似MQ的发布订阅;

   

  ApplicationEvent UML图如下:

  

 

  • Spring常用事件

序号Spring 事件
1 ContextRefreshedEvent:ApplicationContext 被初始化或刷新时,该事件被发布;这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生;容器刷新完成(所有bean都完全创建)会发布这个事件;
2 ContextStartedEvent:当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被发布;
3 ContextStoppedEvent:当使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 时,发布这个事件;
4 ContextClosedEvent:当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布;
5 RequestHandledEvent:在Web应用中,当一个http请求结束触发该事件

 

  

  • org.springframework.context.support.AbstractApplicationContext#refresh

  

  AbstractApplicationContext#initApplicationEventMulticaster 用于初始化事件多播器;

  AbstractApplicationContext#registerListeners 用于将事件监听器注册到多播器;

 

  

  • AbstractApplicationContext#initApplicationEventMulticaster

  ApplicationEventMulticaster该接口定义了具体事件监听器的注册管理以及事件发布的方法 ;

  

 

  

  ApplicationEventMulticaster有一抽象实现类AbstractApplicationEventMulticaster,它实现了事件监听器的管理 功能;出于灵活性和扩展性考虑,事件的发布功能则委托给了其子类,即ApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)

  SimpleApplicationEventMulticaster 是 Spring 提 供 的 AbstractApplicationEventMulticaster的一个子类实现,添加了事件发布功能的实现;

 

  • AbstractApplicationContext#registerListeners

  

  这里画了三个大框,分别做不同的事情;

    • 第一个大框,里面处理的是将容器中ApplicationListener的实现类注册到多播器中;
    • 第二大个框,里面处理的是将自定义的ApplicationListenr的实现类注册到多播器中;

      DefaultListableBeanFactory#doGetBeanNamesForType

      自定义ApplicationListener的实现类最终会在这里组装到List集合;

      里面调用的AbstractBeanFactory#isTypeMatch,用于判断要查询的beanName的实例与该匹配的类型是否一致;

  

    • 第三个大框,earlyApplicationEvents是早期事件,而早期事件是指多播器未创建完成前的事件,也就AbstractApplicationContext#initApplicationEventMulticaster方法执行前,将没有多播器则将之前的事件保存,之后调用getApplicationEventMulticaster().multicastEvent(earlyEvent)方法发布事件;

 

  • SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)

  

  getTaskExecutor(),多播器中是否支持线程池异步发送事件;

 

  • SimpleApplicationEventMulticaster#invokeListener

  调用SimpleApplicationEventMulticaster#doInvokeListener

  SimpleApplicationEventMulticaster#doInvokeListener

  

  listener.onApplicationEvent(event),调用对于listener的onApplicationEvent事件;

  Spring的ApplicationContext容器内的事件发布机制,主要用于单一容器内的简单消息通知和处理,并不适合分布式、多进程、多容器之间的事件通知 ;

 

  可以通过如下两种方式为我们的业务对象注入ApplicationEventPublisher的依赖

  • 使用ApplicationEventPublisherAware接口;在ApplicationContext类型的容器启动时,会自动识别该类型的bean定义并将ApplicationContext容器本身作为ApplicationEventPublisher注入当前对象,而ApplicationContext容器本身就是一个ApplicationEventPublisher;

  • 使用ApplicationContextAware接口; ApplicationContext本身就是一个ApplicationEventPublisher,通过ApplicationContextAware几乎达到第一种方式相同的效果;

 

  • ApplicationListener,ApplicationEvent,ApplicationEventMulticaster,ApplicationEventPublisher类关系图

  

 

 

  测试代码如下:

  配置类

@Configuration
@Import(value = { DemoApplicationListener1.class })
public class ListenerConfig1 {

}

  

  事件监听类

public class DemoApplicationListener1 implements ApplicationListener {
	private final static Logger logger = LoggerFactory
			.getLogger(DemoApplicationListener1.class);

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		logger.info("接收到一个事件:" + event);
	}
}

  

  测试类

@Test
public void listenerTest1() {
	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
			ListenerConfig1.class);

	context.publishEvent(new ApplicationEvent("发送测试消息") {
	});


	try {
		TimeUnit.SECONDS.sleep(5);
	}
	catch (InterruptedException e) {
		e.printStackTrace();
	}

	context.close();
}

  运行结果如下:

  从日志可以看出,事件监听处理是在主线程,上面说到的多播器可以支持线程池异步发送事件,在配置类直接的@Bean一个线程池对象是行不通的,这里的对象是直接new出来的,它不能够直接给Spring管理;多播器要支持异步具体有下面两种方式;

  参考:[https://docs.spring.io/spring-framework/docs/5.2.4.RELEASE/spring-framework-reference/core.html#context-functionality-events]

 

  • 方式一

  在AbstractApplicationContext#initApplicationEventMulticaster会判断IOC容器中是否存在beanName为APPLICATION_EVENT_MULTICASTER_BEAN_NAME的多播器对象,即applicationEventMulticaster,因此可以自定义一个多播器并将它的beanName设置为applicationEventMulticaster

  自定义的事件多播器

@Component("applicationEventMulticaster")
public class DemoEventMulticaster extends SimpleApplicationEventMulticaster {

	public DemoEventMulticaster() {
		/**
		 * @See org.springframework.context.support.AbstractApplicationContext#initApplicationEventMulticaster()
		 */

		setTaskExecutor(Executors.newCachedThreadPool());
	}
}

  

  上面的配置类,@Import部分修改如下:

@Import(value = { DemoApplicationListener1.class, DemoEventMulticaster.class })

  

  运行结果如下:

 

  • 方式二

  使用Spring管理的线程池支持事件多播器的异步;

 

  事件监听器

@Component
public class DemoApplicationListener2 implements ApplicationListener  {
	private final static Logger logger = LoggerFactory
			.getLogger(DemoApplicationListener2.class);


	@Override
	@Async
	public void onApplicationEvent(ApplicationEvent event) {
		logger.info("接收到一个事件:" + event);
	}
}

   

  配置类

@EnableAsync
@Configuration
@Import(value = { DemoApplicationListener2.class })
public class ListenerConfig2 implements AsyncConfigurer {
	private final static Logger logger = LoggerFactory.getLogger(ListenerConfig2.class);

	// 获取线程池
	@Override
	public Executor getAsyncExecutor() {
		// 定义线程池
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		// SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();

		// 设置核心线程数
		executor.setCorePoolSize(4);

		// 设置线程池最大线程数
		executor.setMaxPoolSize(10);

		// 设置线程池队列容量
		executor.setQueueCapacity(1000);

		// 初始化
		executor.initialize();

		logger.info(String.valueOf(executor));
		return executor;
	}

}

  

  测试类:

@Test
public void listenerTest2() {
	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
			ListenerConfig2.class);

	context.publishEvent(new ApplicationEvent("发送测试消息") {
	});

	try {
		TimeUnit.SECONDS.sleep(5);
	}
	catch (InterruptedException e) {
		e.printStackTrace();
	}

	context.close();
}

  

  运行结果如下:

 

 

  也可以使用@EventListener事件注解;

  修改上面的事件监听器

@Component
public class DemoApplicationListener2 {
	private final static Logger logger = LoggerFactory
			.getLogger(DemoApplicationListener2.class);


	@Async
	@EventListener
	public void onApplicationEvent(ApplicationEvent event) {
		logger.info("接收到一个事件:" + event);
	}
}

 

  运行结果如下:

  参考:[https://docs.spring.io/spring-framework/docs/5.2.4.RELEASE/spring-framework-reference/core.html#context-functionality-events-annotation]

  

以上是关于Spring 事件监听的主要内容,如果未能解决你的问题,请参考以下文章

在同一个片段中实现多个事件监听器 - Android

Spring 事件发布

Spring 事件发布

Spring 中的事件发布与监听

观察者模式:spring的事件监听

观察者模式:spring的事件监听