分享

Spring 中时事件处理机制

 昵称10087950 2022-09-21 发布于江苏

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基类的简单类:

  1. public class BlackListEvent extends ApplicationEvent {
  2. private final String address;
  3. private final String content;
  4. public BlackListEvent(Object source, String address, String content) {
  5. super(source);
  6. this.address = address;
  7. this.content = content;
  8. }
  9. // 其他方法
  10. }

若要发布自定义ApplicationEvent,需要在ApplicationEventPublisher上调用PublishEvent()方法。通常,通过创建一个实现ApplicationEventPublisheraware的类并将其注册为Spring Bean来完成的。如下示例所示:

  1. public class EmailService implements ApplicationEventPublisherAware {
  2. private List<String> blackList;
  3. private ApplicationEventPublisher publisher;
  4. public void setBlackList(List<String> blackList) {
  5. this.blackList = blackList;
  6. }
  7. public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
  8. this.publisher = publisher;
  9. }
  10. public void sendEmail(String address, String content) {
  11. if (blackList.contains(address)) {
  12. publisher.publishEvent(new BlackListEvent(this, address, content));
  13. return;
  14. }
  15. // send email...
  16. }
  17. }

在配置时,Spring容器检测到EmailService实现了ApplicationEventPublisheraware,并自动调用setApplicationEventPublisher()注入ApplicationEventPublisher 实例。实际上,传入的参数是Spring容器本身。要接收自定义的ApplicationEvent,可以创建一个实现ApplicationListener 的类,并将其注册为Spring Bean。代码如下所示:

  1. public class BlackListNotifier implements ApplicationListener<BlackListEvent> {
  2. private String notificationAddress;
  3. public void setNotificationAddress(String notificationAddress) {
  4. this.notificationAddress = notificationAddress;
  5. }
  6. public void onApplicationEvent(BlackListEvent event) {
  7. // notify appropriate parties via notificationAddress...
  8. }
  9. }

注意,ApplicationListener通常是用自定义事件的类型(前面的示例中是BackListEvent)参数化。这意味着onApplicationEvent()方法可以保证类型安全,避免任何向下强制转换的需要。您可以注册任意多个事件侦听器,但请注意,默认情况下,事件侦听器同步接收事件。这意味着publishEvent()方法将一直阻塞,直到所有侦听器完成对事件的处理。这种同步和单线程方法的一个优点是,当侦听器接收到事件时,如果事务上下文可用,它将在发布服务器的事务上下文中操作。如果需要事件发布的另一个策略,请参阅ApplicationEventMulticaster接口。如下为上面示例中的Spring配置:

  1. <bean id="emailService" class="example.EmailService">
  2. <property name="blackList">
  3. <list>
  4. <value>known.spammer@example.org</value>
  5. <value>known.hacker@example.org</value>
  6. <value>john.doe@example.org</value>
  7. </list>
  8. </property></bean>
  9. <bean id="blackListNotifier" class="example.BlackListNotifier">
  10. <property name="notificationAddress" value="blacklist@example.org"/>
  11. </bean>

当调用emailServicebean的sendemail()方法时,如果有任何电子邮件应被列入黑名单,则会发布一个BlackListEvent类型的自定义事件。blackListNotifier bean注册为ApplicationListener并接收BlackListEvent,此时它可以通知相应的方。

除了使用ApplicationEventPublisher发布事件外,我们可以通过ApplicationContext实例发布事件,ApplicationContext提供了publishEvent()方法用于发布事件,代码如下所示:

  1. public class MessageEvent extends ApplicationEvent {
  2. private static final long serialVersionUID = 1L;
  3. private String eventType;
  4. public String getEventType() {
  5. return eventType;
  6. }
  7. public void setEventType(String eventType) {
  8. this.eventType = eventType;
  9. }
  10. public MessageEvent(Object source) {
  11. super(source);
  12. }
  13. }
  14. @Component
  15. public class MessageEventListener implements ApplicationListener<MessageEvent> {
  16. public void onApplicationEvent(MessageEvent event) {
  17. String eventType = event.getEventType();
  18. //发布事件的对象
  19. Object object = event.getSource();
  20. }
  21. }
  22. public class SpringEventTest {
  23. public static void main(String[] args) {
  24. ApplicationContext applicationContext = new AnnotationConfigApplicationContext("cn.org.microservice.spring.ioc.event");
  25. MessageEvent event = new MessageEvent(applicationContext);
  26. event.setEventType("发布");
  27. applicationContext.publishEvent(event);
  28. }
  29. }

Spring的事件机制是为同一应用程序上下文中SpringBean之间的简单通信而设计的。然而,对于更复杂的企业集成需求,单独维护的Spring Integration项目为构建基于Spring编程模型的轻量级、面向模式、事件驱动的体系结构提供了完整的支持。从Spring4.2开始,您可以使用EventListener注解在被托管bean的任何公共方法上注册事件侦听器。BlackListNotifier程序可以改写如下:

  1. public class BlackListNotifier {
  2. private String notificationAddress;
  3. public void setNotificationAddress(String notificationAddress) {
  4. this.notificationAddress = notificationAddress;
  5. }
  6. @EventListener
  7. public void processBlackListEvent(BlackListEvent event) {
  8. // notify appropriate parties via notificationAddress...
  9. }
  10. }

方法签名再次声明它要侦听的事件类型,但这次使用灵活的名称,而不实现特定的侦听器接口。只要实际事件类型在其实现层次结构中解析泛型参数,就可以通过泛型缩小事件类型。如果您的方法应该监听多个事件,或者您想要定义它而不使用任何参数,那么也可以在注解本身上指定事件类型。以下示例显示了如何执行此操作:

  1. @EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
  2. public void handleContextStart() {
  3. ...
  4. }

如果希望特定的侦听器异步处理事件,可以重用常规的@Async支持。以下示例显示了如何执行此操作:

  1. @EventListener
  2. @Async
  3. public void processBlackListEvent(BlackListEvent event) {
  4. // BlackListEvent is processed in a separate thread
  5. }

如果需要先调用一个监听器,然后再调用另一个监听器,则可以将@Order注解添加到方法声明中,如下例所示:

  1. @EventListener
  2. @Order(42)
  3. public void processBlackListEvent(BlackListEvent event) {
  4. // notify appropriate parties via notificationAddress...
  5. }

您还可以使用泛型进一步定义事件的结构。考虑使用EntityCreatedEvent,其中t是创建的实际实体的类型。例如,您可以创建以下侦听器定义以仅接收Person的EntityCreatedEvent:

  1. @EventListener
  2. public void onPersonCreated(EntityCreatedEvent<Person> event) {
  3. ...
  4. }

由于类型消除,只有在激发的事件解析事件侦听器筛选的泛型参数(即类PersonCreatedEvent扩展EntityCreatedEvent<Person> { …})时,此操作才有效。在某些情况下,如果所有事件都遵循相同的结构(如前一个示例中的事件一样),则这可能会变得相当乏味。在这种情况下,您可以实现可ResolvableTypeProvider,以指导框架在运行时环境之上提供内容。以下事件显示如何执行此操作:

  1. public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
  2. public EntityCreatedEvent(T entity) {
  3. super(entity);
  4. }
  5. @Override
  6. public ResolvableType getResolvableType() {
  7. return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
  8. }
  9. }

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多