ApplicationContext通过ApplicationEvent和ApplicationListiner提供事件处理能力,实现了ApplicationContext的bean会被部署到context中,在任何时候会得到来自ApplicationContext的ApplicationEvent事件,这就是一个标准的观察者模式。
随着Spring 4.2,事件机制有了明显的增强,提供了基于注解的编程模型,而且能发布任意的事件,事件不必再继承ApplicationEvent,当一个对象被发布的时候,会自动被包装为一个事件。
Spring内置事件:
ContextRefreshedEvent:当ApplicationContext被初始化或被刷新的时候,例如调用ConfigurableApplicationContext.refresh(),初始化意味着所有的bean被加载,post-processor beans被检测和激活,单例被预初始化,ApplicationContext对象已经做好了使用的准备。只要ApplicationContext不被关闭,一个refresh可以触发多次,ApplicationContext实际提供了一个热刷新的选择,XmlWebApplicationContext提供了热刷新,但是GenericApplicationContext没有提供热刷新。
ContextStartedEvent:当ApplicationContext启动的时候被发布,使用ConfigurableApplicationContext.start()方法,"Started"意味着所有Lifecycle bean接受一个显式的start信号。这个信号被用于显示关闭之后重启bean,也可能被用于启动没有进行自动配置的组件,例如组件在初始化的时候没有启动。
ContextStoppedEvent:当ApplicationContext被停止的时候发布这个事件,使用ConfigurableApplicationContext.stop()方法,"Stopped"意味着Lifecycle bean接受一个显式的stop信号。一个停止的context可能通过start重新启动。
ContextClosedEvent:当ApplicationContext被关闭的时候发布,使用ConfigurableApplicationContext.close()方法,"Closed"意味着所有的单例bean已经被销毁,一个关闭的context走到了生命的尽头,不能在被refreshed和restarted.
RequestHandledEvent: 一个Web特定事件,告诉所有bean,http request已经被服务。这个事件是在request完成后发布,这个事件仅仅在Web应用的DispatchServlet.
你也能发布自定义事件,通过实现ApplicationEvent,通过RequestHandledEvent.publishEvent进行事件发布。你可能会注册很多事件监听器,但是需要注意,这些监听器默认是同步方式接受事件。这意味着publishEvent会被阻塞直到所有监听器处理完事件。同步的一个优点是,如果是在一个事务上下文内操作,那么这个事务上下文在listener中仍然是可用的。
Spring事件机制只是一个简单的针对于同一个application context中的不同bean之间的一个通讯。
Spring 4.2提供了基于注解的监听器模型,不必再继承ApplicationEvent,不必在实现ApplicationListener,还可以限定事件的类型。
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
...
}
可以通过注解中的condition使用el表达式进行事件过滤,
@EventListener(condition = "#blEvent.test == 'foo'")
public void processBlackListEvent(BlackListEvent blEvent) {
// notify appropriate parties via notificationAddress...
}
异步支持通过@Async
@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
// BlackListEvent is processed in a separate thread
}
异步监听器的一些限制:
1.异常信息无法传播给调用者,通过AsyncUncaughtExceptionHandler获取更详细的信息
2.不能给发送者返回结果
通过@Order限定listener的顺序:
@EventListener
@Order(42)
public void processBlackListEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress...
}
还能支持泛型事件
事件发布机制的利与弊
- 基于类型,因此尽量避免类型继承,事件尽量定义为final,因为事件之间存在复杂的继承关系,将会导致事件传播变得复杂
- 事件机制本意是解耦,但是过多的事件如何维护,代码结构如何控制,难以测试和追踪;事件本身如何管理
- 什么时候应该使用事件机制,使用的时候应该注意什么