ApplicationContext中的事件处理通过ApplicationEvent类和ApplicationListener接口提供。如果实现ApplicationListener接口的bean注册到ApplicationContext中,则每次ApplicationEvent发布到ApplicationContext时,都会通知该bean。本质上,这是标准的观察者设计模式。从Spring4.2开始,事件结构得到了显著的改进,提供了一个基于注解的模型以及发布任意事件的能力(也就是说,不一定从ApplicationEvent扩展的对象)。当这样一个对象被发布时,我们将它包装在一个事件中。了解Spring的事件机制,对于Spring与其他第三方框架的集成一级Spring的扩展有很大的帮助。在Dubbo就是通过Spring的事件机制发布服务的,其中Spring和也提供了很多内置的事件,如下表所示:
事件 | 说明 | ContextRefreshedEvent | 在初始化或刷新ApplicationContext时发布(例如通过在ConfigurableApplicationContext接口上使用refresh()方法)。这里,“初始化”意味着加载所有bean,检测并激活post-processor bean,预先实例化单例,并且ApplicationContext对象准备好使用。只要上下文未关闭,只要所选的ApplicationContext实际上支持此类“热”刷新,就可以多次触发刷新。例如,XMLWebApplicationContext支持热刷新,但GenericApplicationContext不支持。 | ContextStartedEvent | 在可配置的ConfigurableApplicationContext 接口上使用start()方法启动ApplicationContext时发布。这里,“启动”意味着所有生命周期bean都会收到一个明确的启动信号。此信号用于在明确停止后重新启动be an,但也可以用于启动尚未配置为自动启动的组件(例如,初始化时尚未启动的组件)。 | ContextStoppedEvent | 在ConfigurableApplicationContext的接口上使用stop()方法停止ApplicationContext时发布。这里,“停止”意味着所有生命周期bean都会收到一个明确的停止信号。停止的上下文可以通过start()调用重新启动。 | ContextClosedEvent | 在ConfigurableApplicationContext接口上使用close()方法关闭ApplicationContext时发布。这里,“关闭”意味着所有的单例bean都被销毁了。封闭的环境达到了生命的尽头。无法刷新或重新启动。 | RequestHandledEvent | 一个特定的web事件告诉所有beans,HTTP请求已经被处理。这一事件在请求完成之后发布。该事件仅仅适用于适用Spring DispatcherServlet的web 应用 |
我们也可以通过 ApplicationEvent类和ApplicationListener接口创建自己的事件,下面的示例显示了一个扩展Spring的ApplicationEvent基类的简单类:
public class BlackListEvent extends ApplicationEvent { private final String address; private final String content; public BlackListEvent(Object source, String address, String content) {
若要发布自定义ApplicationEvent,需要在ApplicationEventPublisher上调用PublishEvent()方法。通常,通过创建一个实现ApplicationEventPublisheraware的类并将其注册为Spring Bean来完成的。如下示例所示:
public class EmailService implements ApplicationEventPublisherAware { private List<String> blackList; private ApplicationEventPublisher publisher; public void setBlackList(List<String> blackList) { this.blackList = blackList; public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; public void sendEmail(String address, String content) { if (blackList.contains(address)) { publisher.publishEvent(new BlackListEvent(this, address, content));
在配置时,Spring容器检测到EmailService实现了ApplicationEventPublisheraware,并自动调用setApplicationEventPublisher()注入ApplicationEventPublisher 实例。实际上,传入的参数是Spring容器本身。要接收自定义的ApplicationEvent,可以创建一个实现ApplicationListener 的类,并将其注册为Spring Bean。代码如下所示:
public class BlackListNotifier implements ApplicationListener<BlackListEvent> { private String notificationAddress; public void setNotificationAddress(String notificationAddress) { this.notificationAddress = notificationAddress; public void onApplicationEvent(BlackListEvent event) { // notify appropriate parties via notificationAddress...
注意,ApplicationListener通常是用自定义事件的类型(前面的示例中是BackListEvent)参数化。这意味着onApplicationEvent()方法可以保证类型安全,避免任何向下强制转换的需要。您可以注册任意多个事件侦听器,但请注意,默认情况下,事件侦听器同步接收事件。这意味着publishEvent()方法将一直阻塞,直到所有侦听器完成对事件的处理。这种同步和单线程方法的一个优点是,当侦听器接收到事件时,如果事务上下文可用,它将在发布服务器的事务上下文中操作。如果需要事件发布的另一个策略,请参阅ApplicationEventMulticaster接口。如下为上面示例中的Spring配置:
<bean id="emailService" class="example.EmailService"> <property name="blackList"> <value>known.spammer@example.org</value> <value>known.hacker@example.org</value> <value>john.doe@example.org</value> <bean id="blackListNotifier" class="example.BlackListNotifier"> <property name="notificationAddress" value="blacklist@example.org"/>
当调用emailServicebean的sendemail()方法时,如果有任何电子邮件应被列入黑名单,则会发布一个BlackListEvent类型的自定义事件。blackListNotifier bean注册为ApplicationListener并接收BlackListEvent,此时它可以通知相应的方。
除了使用ApplicationEventPublisher发布事件外,我们可以通过ApplicationContext实例发布事件,ApplicationContext提供了publishEvent()方法用于发布事件,代码如下所示:
public class MessageEvent extends ApplicationEvent { private static final long serialVersionUID = 1L; private String eventType; public String getEventType() { public void setEventType(String eventType) { this.eventType = eventType; public MessageEvent(Object source) { public class MessageEventListener implements ApplicationListener<MessageEvent> { public void onApplicationEvent(MessageEvent event) { String eventType = event.getEventType(); Object object = event.getSource(); public class SpringEventTest { public static void main(String[] args) { ApplicationContext applicationContext = new AnnotationConfigApplicationContext("cn.org.microservice.spring.ioc.event"); MessageEvent event = new MessageEvent(applicationContext); event.setEventType("发布"); applicationContext.publishEvent(event);
Spring的事件机制是为同一应用程序上下文中SpringBean之间的简单通信而设计的。然而,对于更复杂的企业集成需求,单独维护的Spring Integration项目为构建基于Spring编程模型的轻量级、面向模式、事件驱动的体系结构提供了完整的支持。从Spring4.2开始,您可以使用EventListener注解在被托管bean的任何公共方法上注册事件侦听器。BlackListNotifier程序可以改写如下:
public class BlackListNotifier { private String notificationAddress; public void setNotificationAddress(String notificationAddress) { this.notificationAddress = notificationAddress; public void processBlackListEvent(BlackListEvent event) { // notify appropriate parties via notificationAddress...
方法签名再次声明它要侦听的事件类型,但这次使用灵活的名称,而不实现特定的侦听器接口。只要实际事件类型在其实现层次结构中解析泛型参数,就可以通过泛型缩小事件类型。如果您的方法应该监听多个事件,或者您想要定义它而不使用任何参数,那么也可以在注解本身上指定事件类型。以下示例显示了如何执行此操作:
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class}) public void handleContextStart() {
如果希望特定的侦听器异步处理事件,可以重用常规的@Async支持。以下示例显示了如何执行此操作:
public void processBlackListEvent(BlackListEvent event) { // BlackListEvent is processed in a separate thread
如果需要先调用一个监听器,然后再调用另一个监听器,则可以将@Order注解添加到方法声明中,如下例所示:
public void processBlackListEvent(BlackListEvent event) { // notify appropriate parties via notificationAddress...
您还可以使用泛型进一步定义事件的结构。考虑使用EntityCreatedEvent,其中t是创建的实际实体的类型。例如,您可以创建以下侦听器定义以仅接收Person的EntityCreatedEvent:
public void onPersonCreated(EntityCreatedEvent<Person> event) {
由于类型消除,只有在激发的事件解析事件侦听器筛选的泛型参数(即类PersonCreatedEvent扩展EntityCreatedEvent<Person> { …})时,此操作才有效。在某些情况下,如果所有事件都遵循相同的结构(如前一个示例中的事件一样),则这可能会变得相当乏味。在这种情况下,您可以实现可ResolvableTypeProvider,以指导框架在运行时环境之上提供内容。以下事件显示如何执行此操作:
public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider { public EntityCreatedEvent(T entity) { public ResolvableType getResolvableType() { return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
|