Spring事件
概述
Spring事件是Spring Framework
中观察者模式的体现,其主要应用于ApplicationContext
生命周期中各关键点的通知。
文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/htmlsingle/#context-functionality-events
文档中有一段特别说明,大概的意思就是从4.2版本开始事件得到了巨大的升级,加入了基于注解的使用方式和用任意对象作为事件,下面都会进行讲解。
As of Spring 4.2, the event infrastructure has been significantly improved and offers an annotation-based model as well as the ability to publish any arbitrary event (that is, an object that does not necessarily extend from ApplicationEvent). When such an object is published, we wrap it in an event for you.
分类
从文档章节标题来看,事件分为标准事件和自定义事件。标准事件就是Spring内置的事件,下图就是Spring Framework
中的标准事件;自定义事件就是用户利用框架事件机制创建的事件。
原始用法
最早的实现方式是继承和实现对应的接口,主要包括ApplicationEvent
、ApplicationEventPublisherAware
、ApplicationListener
1. 定义事件
事件需要继承ApplicationEvent
,事件中包含了你需要传递的数据
publicclassBlockedListEventextendsApplicationEvent { privatefinalStringaddress; privatefinalStringcontent; publicBlockedListEvent(Objectsource, Stringaddress, Stringcontent) { super(source); this.address=address; this.content=content; } // accessor and other methods...}
2. 创建事件监听
监听要实现ApplicationListener
接口,实现接收事件方法onApplicationEvent(evetn)
publicclassBlockedListNotifierimplementsApplicationListener<BlockedListEvent> { privateStringnotificationAddress; publicvoidsetNotificationAddress(StringnotificationAddress) { this.notificationAddress=notificationAddress; } publicvoidonApplicationEvent(BlockedListEventevent) { // 处理收到的事件 } }
3. 发布事件
发布事件使用框架内的ApplicationEventPublisher
实现类的实例。由于ApplicationContext
也实现了该接口,所以用ApplicationContext
也可以发布事件,代码applicationContext.publishEvent(event)
。
publicclassEmailServiceimplementsApplicationEventPublisherAware { privateList<String>blockedList; privateApplicationEventPublisherpublisher; publicvoidsetBlockedList(List<String>blockedList) { this.blockedList=blockedList; } publicvoidsetApplicationEventPublisher(ApplicationEventPublisherpublisher) { this.publisher=publisher; } publicvoidsendEmail(Stringaddress, Stringcontent) { if (blockedList.contains(address)) { publisher.publishEvent(newBlockedListEvent(this, address, content)); return; } // send email... } }
4. 总结
这是最原始的使用方式,由于Java是单继承,这样会产生很大的限制,而且继承和实现也会导致框架的侵入性
用注解优化Listener
作为上一种使用方式的升级,注解的加入不但减少了框架的侵入还支持了监听多个事件甚至做到了按条件监听事件
// 基本用法publicclassBlockedListNotifier { publicvoidprocessBlockedListEvent(BlockedListEventevent) { // notify appropriate parties via notificationAddress... } } // 监听多个事件,但注意方法参数ContextStartedEvent.class, ContextRefreshedEvent.class}) ({publicvoidhandleContextStart() { // ...} // 按特定条件接收事件,具体用法见官方文档condition="#blEvent.content == 'my-event'") (publicvoidprocessBlockedListEvent(BlockedListEventblEvent) { // notify appropriate parties via notificationAddress...} // 接收事件后发布另一个事件,注意返回值,甚至你可以返回一批事件。另外,文档中特别注明异步事件不支持publicListUpdateEventhandleBlockedListEvent(BlockedListEventevent) { // notify appropriate parties via notificationAddress and// then publish a ListUpdateEvent...} //
异步事件
默认情况下,Listener
是同步处理事件的,你可以理解为发布事件的方法同步调用处理事件的方法。但通常情况是希望是异步调用。那么实现方式是使用@Async
注解。
publicvoidprocessBlockedListEvent(BlockedListEventevent) { // BlockedListEvent is processed in a separate thread}
最后说明一下,异步事件中,Listener
抛出的异常不会传递到发布方。也无法通过返回值来发布新的事件。
循序监听
当有多个监听器监听同一个事件且需要指定监听的顺序时,需要使用@Order
指定。
42) (publicvoidprocessBlockedListEvent(BlockedListEventevent) { // notify appropriate parties via notificationAddress...}
通过泛型限定监听范围
通过对事件增加泛型,可以控制监听器监听的事件范围
publicvoidonPersonCreated(EntityCreatedEvent<Person>event) { // ...}
如上,当需要定义一个非固定类型的泛型事件时,可以用下面的方式
publicclassEntityCreatedEvent<T>extendsApplicationEventimplementsResolvableTypeProvider { publicEntityCreatedEvent(Tentity) { super(entity); } publicResolvableTypegetResolvableType() { returnResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource())); } }
使用任意类型作为事件
原始用法中说到,作为单继承的语言,事件继承ApplicationEvent
会产生很多限制。而Spring框架也在4.2进行了升级,任意的对象都可以作为一个事件发布出去,也就是说事件不必继承ApplicationEvent
,普通的Java Bean就可以作为事件。
publicclassBlockedListEvent { privatefinalStringaddress; privatefinalStringcontent; publicBlockedListEvent(Stringaddress, Stringcontent) { this.address=address; this.content=content; } // accessor and other methods...}
而ApplicationEventPublisher
也有对应的发布方法