简介
这篇文章我们来讲解Spring
中如何使用事件。
Spring
中使用事件,需要遵守一些规则 :
- 使用以下两种方式之一定义事件
- 继承自
ApplicationEvent
- 不继承自
ApplicationEvent
- 继承自
- 使用以下两种方式之一定义事件发布者
- 注入一个
ApplicationEventPublisher
对象到事件发布者 - 事件发布者实现接口
ApplicationEventPublisherAware
- 注入一个
- 使用如下两种方式之一实现事件监听器
- 实现接口
ApplicationListener
- 使用注解
@EventListener
- 实现接口
自定义一个事件
Spring
允许创建和发布自定义事件,并且缺省情况下,这些事件的处理逻辑的执行都是同步的。这样做有一些优点,比如,监听器中的事件处理逻辑能够参与到事件发布者的事务上下文中来。
注意:这里同步也就意味着事件处理逻辑在同一个线程中发起,执行也会发生在当前线程中(但是如果事件处理逻辑中开发人员发起了其它异步调用,就不属于这种情况了)。
一个简单的应用事件
让我们来创建一个简单的事件类,该事件只携带消息来源对象信息和一条消息。
方法一:继承自 ApplicationEvent
import org.springframework.context.ApplicationEvent;
public class CustomApplicationEvent extends ApplicationEvent {
private String message;
public CustomApplicationEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
方法二:不继承自 ApplicationEvent
public class CustomEvent {
private String message;
Object source;
public CustomEvent(Object source, String message) {
this.source = source;
this.message = message;
}
public String getMessage() {
return message;
}
}
事件发布者
现在让我们创建上面事件的一个发布者。这个事件发布者构造事件对象并将事件发布出去。
要发布事件,发布者可以简单地注入ApplicationEventPublisher
然后调用其事件发布API方法publishEvent()
:
public class CustomSpringEventPublisher {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
/**
* 发布两个事件:
* 1. 继承自 Spring ApplicationEvent 的事件
* 2. 不继承自 Spring ApplicationEvent 的任意Java对象事件
*
* @param message 事件中所要携带的消息
*/
public void publishEvent(final String message) {
System.out.println("Publishing custom ApplicationEvent . ");
CustomApplicationEvent customApplicationEvent =
new CustomApplicationEvent(this, message);
applicationEventPublisher.publishEvent(customApplicationEvent);//发布事件
System.out.println("Publishing custom common event . ");
CustomEvent customEvent = new CustomEvent(this, message);
applicationEventPublisher.publishEvent(customEvent);//发布事件
}
}
除了上面这种方法之外,还有一种方法是发布者类实现接口ApplicationEventPublisherAware
,实现了该接口的话应用程序启动时会向该事件发布者bean注入事件发布器ApplicationEventPublisher
。不过通常情况下,使用@Autowire
注解方式注入事件发布器更简单。
事件监听器
最后让我们创建事件监听器。
ApplicationEvent
子类事件监听器
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener;
// 该例子中提到的两种监听方法可以分开独立使用,相互之间并无关系,
// 本例子将它们放在同一类中仅仅出于演示其监听同一类型事件的目的
@Component
public class CustomApplicationEventListener
implements ApplicationListener<CustomApplicationEvent> {
// 监听方法一:监听器类继承自ApplicationListener
@Override
public void onApplicationEvent(CustomApplicationEvent event) {
System.out.println(
"ApplicationListener -- Received custom spring ApplicationEvent - "
+ event.getMessage());
}
// 监听方法二:使用注解@EventListener
@EventListener
public void handleCustomApplicationEvent(CustomApplicationEvent event) {
System.out.println(
"@EventListener -- Received custom spring ApplicationEvent - "
+ event.getMessage());
}
}
请注意我们的自定义事件监听器使用了自定义事件的泛型作为参数,这样就会让事件监听器的方法onApplicationEvent()
变成类型安全的。同时也避免了在onApplicationEvent()
中检查入参是否是给定的事件类型以及相应的类型转换。
非ApplicationEvent
子类事件监听器
import org.springframework.context.ApplicationListener;
import org.springframework.context.PayloadApplicationEvent;
import org.springframework.context.event.EventListener;
// 该例子中提到的两种监听方法可以分开独立使用,相互之间并无关系,
// 本例子将它们放在同一类中仅仅出于演示其监听同一类型事件的目的
@Component
public class CustomEventListener
implements ApplicationListener<PayloadApplicationEvent<CustomEvent>> {
// 监听方法一:监听器类继承自ApplicationListener
@Override
public void onApplicationEvent(PayloadApplicationEvent<CustomEvent> event) {
System.out.println("ApplicationListener -- Received custom common event - "
+ event.getPayload().getMessage());
}
// 监听方法二:使用注解@EventListener
@EventListener
public void handleCustomEvent(CustomEvent event) {
System.out.println("@EventListener -- Received custom common event - "
+ event.getMessage());
}
}
上面的例子中需要提醒的一点是,如果所发布事件的定义类T
不是继承自ApplicationEvent
,那么它在内部会被包装成一个PayloadApplicationEvent<T>
对象然后发布,但是只有方法onApplicationEvent
的参数为PayloadApplicationEvent<T>
的监听器才能监听到该事件。
另外,再次强调,缺省情况下,Spring
事件是同步执行的,事件发布者的方法publishEvent()
会阻塞直到所有监听器处理完所发布的事件(实际上就是在同一个线程中的同步执行)。
创建异步事件
在某些情况下,同步处理并不是我们真正需要的,我们想要异步。
想做到异步,有两种方法:
配置文件中指定异步任务执行器给事件发布者
你可以在配置中使用任务执行器(TaskExecutor
)创建一个ApplicationEventMulticaster
;我们的例子使用类SimpleAsyncTaskExecutor
来演示这一目的 :
@Configuration
public class AsynchronousSpringEventsConfig {
@Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster
= new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
return eventMulticaster;
}
}
现在,事件,事件发布者和监听器实现都跟之前的一样保持不变,但是监听器中的事件处理逻辑已经会在另外一个线程中处理事件了。
异步注解事件监听器方法
@Async
@EventListener
public void handleCustomSpringEvent(CustomApplicationEvent event) {
System.out.println("Received spring custom event - " + event.getMessage());
}
框架自带事件
Spring自身原本就发布了各种各样的事件。比如,ApplicationContext
会发布各种框架事件,诸如ContextRefreshedEvent
,ContextStartedEvent
,RequestHandledEvent
之类。
这些事件提供给应用程序开发者一个参与到应用和上下文的生命周期中来的机会,开发者可以在需要的地方加入一些自定义的逻辑。
下面是一个监听应用上下文刷新的事件监听器例子:
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
@Component
public class ContextRefreshedListener
implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent cse) {
System.out.println("Handling context refreshed event. ");
}
}
总结
本文主要讲述了Spring框架自身的事件机制。除了使用Spring框架自身的事件机制,实际上开发人员也可以使用其他第三方的事件机制,比如 Guava 事件总线等。
另外,本文中提到的例子在github上都能找到源代码。
#参考文章
Spring Events
Better application events in Spring Framework 4.2
Spring 4.3 – Event Listener
Spring事件机制