【Spring】对 Spring 事件机制及其使用做个小总结 ApplicationEvent ApplicationListener @EventListener

【Spring】对 Spring 事件机制及其使用做个小总结 ApplicationEvent ApplicationListener @EventListener

前言

SpringJDK 的标准事件 EventObject 做了拓展比如各种 ApplicationEvent,并支持基于 ApplicationEvent@EventListener 来监听对应的事件

Spring IoC Container 通过事件组播器 ApplicationEventMulticaster 来负责 ApplicationListener 的维护和事件的发布,这些细节可以在下文了解:

【源码】Spring —— ApplicationEvent ApplicationListener ApplicationEventMulticaster

本文可以理解为上文的一个延申,主要陈述如下内容:

  • 基于 ApplicationListener 接口的自定义监听器拓展
  • 基于 @EventListener 注解方法的自定义监听器拓展

基于 ApplicationListener

demo

@Component
public class CustomListener implements SmartApplicationListener {

    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {

        return CustomEvent.class.isAssignableFrom(eventType);
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("listen a custom event");
    }

}

	@Test
    public void test() {
        AnnotationConfigApplicationContext context
                = new AnnotationConfigApplicationContext("com.example.springdemoall.beanfactory.event.applicationlistener");
        context.publishEvent(new CustomEvent(context, "default"));
    }
  • 这种方式很简单且容易理解,注册一个 ApplicationListener 的组件即可
  • 容器会自动感知并注册这个 ApplicationListener,这主要发生在容器启动时的 AbstractApplicationContext#registerListeners 阶段

AbstractApplicationContext#registerListeners

	protected void registerListeners() {

		// 这里是注册手动 add 的 ApplicationListener
		for (ApplicationListener<?> listener : getApplicationListeners()) {
			getApplicationEventMulticaster().addApplicationListener(listener);
		}

		// 这里是收集容器中所有注册的 ApplicationListener 的 【beanName,即不会初始化】
		String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
		for (String listenerBeanName : listenerBeanNames) {
			// 然后以 addApplicationListenerBean 的方式注册
			// 发布对应事件时才会初始化
			getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
		}

		// ...

	}
  • 容器启动 refresh 阶段的 registerListeners 方法会收集我们自定义 ApplicationListenerbeanName
  • ApplicationEventMulticaster#addApplicationListenerBean 形式即注册 beanName,这样在发布事件时才会初始化对应的 ApplicationListener

基于 @EventListener

@EventListener

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EventListener {

	@AliasFor("classes")
	Class<?>[] value() default {};

	/**
	 * 处理事件的类,单个类型是接受对应的参数,多个类型
	 * 		时不接受参数
	 */
	@AliasFor("value")
	Class<?>[] classes() default {};

	/**
	 * 基于 SpEL 的条件表达式,上下文包含如下变量:
	 * #root.event:事件对象
	 * #root.args:方法参数
	 * #root.args[0] args[0] #a0 #p0:基于下标获取参数
	 */
	String condition() default "";

	String id() default "";

}
  • 我们还可以用 @EventListener 注解普通方法的方式来注册 ApplicationListener 而不实现对应接口
  • 监听单个事件时可以在方法参数指定对应事件类型
  • 也可以监听多个事件,但不能指定参数
  • 支持基于 SpELcondition 表达式,并拥有独立的上下文(具体可见方法注释)
  • 还可以通过指定方法返回值类型在处理完事件后发布新的对应类型的事件

demo

@Component
public class CommonComponent {

    /**
     * 基于 SpEL 的条件表达式指定
     * 这里是要事件的属性 name == default 才监听
     * 单个事件的监听可以指定事件类型的参数,这里是监听 CustomEvent
     * 方法返回值是事件类型时,会在处理完当前事件后再发布对应的事件,
     *      比如这里处理完 CustomEvent 会发布一个 ChangeEvent
     */
    @EventListener(condition = "#event.name == 'default'")
    public ChangeEvent listenCustomEvent(CustomEvent event) {
        System.out.println("listen a custom event");
        return new ChangeEvent(event.getSource());
    }

    // 监听 ChangeEvent
    @EventListener
    public void listenChangeEvent(ChangeEvent event) {
        System.out.println("listen a change event");
    }

    // 可以指定监听多个事件,但不能有参数
    @EventListener(classes = { CustomEvent.class, ChangeEvent.class })
    public void listenBothEvent() {
        System.out.println("listen any event");
    }
}

	@Test
    public void test() {
        AnnotationConfigApplicationContext context
                = new AnnotationConfigApplicationContext("com.example.springdemoall.beanfactory.event.eventlistener");
        context.publishEvent(new CustomEvent(context, "default"));
        System.out.println("=======================");
        context.publishEvent(new CustomEvent(context, "other"));
    }
  • 方法 listenCustomEvent 基于 condition 表达式选择监听对应的 CustomEvent 并在处理后发布新的 ChangeEvent
  • 方法 listenChangeEvent 监听所有 ChangeEvent
  • 方法 listenBothEvent 监听所有 CustomEventChangeEvent

浅析原理

  • Spring 容器启动时会注册的一些内置组件比如 EventListenerMethodProcessor DefaultEventListenerFactory
  • EventListenerMethodProcessor 是一个 BeanFactoryPostProcessor,它在 postProcessBeanFactory 会收集对应的 EventListenerFactory,默认就是 DefaultEventListenerFactory
  • 同时 EventListenerMethodProcessor 也是一个 SmartInitializingSingleton,在 afterSingletonsInstantiated 阶段它会把对应 @EventListener 注解的方法转换成 ApplicationListener 注册
  • DefaultEventListenerFactory 就负责把 @EventListener 注解的方法转换成 ApplicationListener,其本质是基于 适配器模式 把对应方法适配成 ApplicationListenerMethodAdapter

总结

Spring 的事件机制小巧而强大,比如 Spring Boot 就基于该机制大作文章拓展了十分强大的能力

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值