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) {
super(source);
this.address = address;
this.content = 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));
return;
}
// send email...
}
}
在配置时,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">
<list>
<value>known.spammer@example.org</value>
<value>known.hacker@example.org</value>
<value>john.doe@example.org</value>
</list>
</property></bean>
<bean id="blackListNotifier" class="example.BlackListNotifier">
<property name="notificationAddress" value="blacklist@example.org"/>
</bean>
当调用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() {
return eventType;
}
public void setEventType(String eventType) {
this.eventType = eventType;
}
public MessageEvent(Object source) {
super(source);
}
}
@Component
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;
}
@EventListener
public void processBlackListEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
方法签名再次声明它要侦听的事件类型,但这次使用灵活的名称,而不实现特定的侦听器接口。只要实际事件类型在其实现层次结构中解析泛型参数,就可以通过泛型缩小事件类型。如果您的方法应该监听多个事件,或者您想要定义它而不使用任何参数,那么也可以在注解本身上指定事件类型。以下示例显示了如何执行此操作:
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
...
}
如果希望特定的侦听器异步处理事件,可以重用常规的@Async支持。以下示例显示了如何执行此操作:
@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
// BlackListEvent is processed in a separate thread
}
如果需要先调用一个监听器,然后再调用另一个监听器,则可以将@Order注解添加到方法声明中,如下例所示:
@EventListener
@Order(42)
public void processBlackListEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress...
}
您还可以使用泛型进一步定义事件的结构。考虑使用EntityCreatedEvent,其中t是创建的实际实体的类型。例如,您可以创建以下侦听器定义以仅接收Person的EntityCreatedEvent:
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
...
}
由于类型消除,只有在激发的事件解析事件侦听器筛选的泛型参数(即类PersonCreatedEvent扩展EntityCreatedEvent<Person> { …})时,此操作才有效。在某些情况下,如果所有事件都遵循相同的结构(如前一个示例中的事件一样),则这可能会变得相当乏味。在这种情况下,您可以实现可ResolvableTypeProvider,以指导框架在运行时环境之上提供内容。以下事件显示如何执行此操作:
public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
public EntityCreatedEvent(T entity) {
super(entity);
}
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
}
}