Spring事件流

6 篇文章 0 订阅

ApplicationContext中的事件处理是通过ApplicationEvent类和ApplicationListener接口提供的。如果将实现ApplicationListener接口的bean部署到上下文中,则每次将ApplicationEvent发布到ApplicationContext时,都会通知该bean。本质上,这是标准的观察者模式实现。

从 Spring 4.2 开始,事件基础结构得到了显着改进,并提供了annotation-based model以及发布任何任意事件的功能,该对象不一定是从ApplicationEvent扩展的。发布此类对象后,我们会为您包装一个事件。

Spring 自定义事件

您还可以创建和发布自己的自定义事件。这个例子演示了一个简单的类,它扩展了 Spring 的ApplicationEventBase类:

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;
    }

    // accessor and other methods...
}

要发布自定义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()。实际上,传入的参数将是 Spring 容器本身。您只需通过其ApplicationEventPublisher接口与应用程序上下文进行交互。

要接收自定义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通常使用自定义事件BlackListEvent的类型进行参数化。这意味着onApplicationEvent()方法可以保持类型安全,从而避免了向下转换的任何需要。您可以根据需要注册任意数量的事件监听器,但是请注意,默认情况下,事件监听器会同步接收事件。这意味着publishEvent()方法将阻塞,直到所有侦听器都已完成对事件的处理为止。

这种同步和单线程方法的一个优点是,当侦听器收到事件时,如果有可用的事务上下文,它将在发布者的事务上下文内部进行操作。如果需要其他用于事件发布的策略,请参阅 Spring 的ApplicationEventMulticaster接口的 javadoc。

以下示例显示了用于注册和配置上述每个类的 Bean 定义:

<bean id="emailService" class="example.EmailService">
    <property name="blackList">
        <list>
            <value>[emailprotected]</value>
            <value>[emailprotected]</value>
            <value>[emailprotected]</value>
        </list>
    </property>
</bean>

<bean id="blackListNotifier" class="example.BlackListNotifier">
    <property name="notificationAddress" value="[emailprotected]"/>
</bean>

总而言之,当调用emailServicesendEmail()方法时,如果有任何电子邮件应被列入黑名单,则会发布BlackListEvent类型的自定义事件。 blackListNotifier被注册为ApplicationListener并因此接收BlackListEvent,此时它可以通知适当的参与者。

Spring 的事件机制旨在在同一应用程序上下文内在 Spring bean 之间进行简单的通信。但是,对于更复杂的企业集成需求,单独维护的Spring Integration项目为构建基于众所周知的 Spring 编程模型的轻量级事件pattern-oriented,事件驱动的体系结构提供了完整的支持。

基于注解的事件监听器

从 Spring 4.2 开始,可以通过EventListenerComments在托管 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() {
    ...
}

也可以通过定义SpEL expression的注解的condition属性添加其他运行时筛选,该SpEL expression应该匹配以针对特定事件实际调用该方法。

例如,如果事件的content属性等于foo,则可以将我们的通知程序重写为仅被调用:

@EventListener(condition = "#blEvent.content == 'foo'")
public void processBlackListEvent(BlackListEvent blEvent) {
    // notify appropriate parties via notificationAddress...
}

异步监听器(asynchronous listeners)不支持此功能。

监听器拓展

异步监听器

如果要特定的侦听器异步处理事件,只需重用常规@Async支持:

@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
    // BlackListEvent is processed in a separate thread
}

使用异步事件时,请注意以下限制:

  • 如果事件监听器抛出Exception,它将不会传播给调用者,详见AsyncUncaughtExceptionHandler
  • 此类事件监听器无法发送答复事件。如果您需要发送另一个事件作为处理结果,请注入ApplicationEventPublisher以手动发送事件。

监听器执行顺序

如果需要在另一个监听器之前调用该监听器,只需将@Order注解添加到方法声明中:

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

泛型事件

您也可以使用泛型来进一步定义事件的结构。考虑一个EntityCreatedEvent<T>,其中T是创建的实际实体的类型。您可以创建以下侦听器定义,以仅针对Person接收EntityCreatedEvent

@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
    ...
}

由于类型擦除,这仅在触发的事件解析了事件监听器所依据的通用参数(如class PersonCreatedEvent extends 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()));
    }
}

这不仅适用于ApplicationEvent,还适用于您作为事件发送的任何任意对象。

Spring 内置事件

EventExplanation
ContextRefreshedEvent初始化或刷新ApplicationContext时发布,例如,使用ConfigurableApplicationContext接口上的refresh()方法。这里的“已初始化”是指所有Bean都已加载,检测到并激活了后处理器Bean,已预先实例化单例,并且已准备好使用ApplicationContext对象。只要尚未关闭上下文,只要选定的ApplicationContext实际上支持这种“热”刷新,就可以多次触发刷新。例如,XmlWebApplicationContext支持热刷新,但GenericApplicationContext不支持。
ContextStartedEventApplicationContext启动时使用ConfigurableApplicationContext界面上的start()方法发布。这里的“已启动”表示所有Lifecycle bean都收到一个明确的启动signal。通常,此signal用于在显式停止后重新启动 Bean,但也可以用于启动尚未配置为自动启动的组件,例如,尚未在初始化时启动的组件。
ContextStoppedEventApplicationContext停止时使用ConfigurableApplicationContext界面上的stop()方法发布。这里的“停止”表示所有Lifecycle bean都收到一个明确的停止signal。停止的上下文可以通过start()调用重新启动。
ContextClosedEventApplicationContext关闭时使用ConfigurableApplicationContext界面上的close()方法发布。这里的“关闭”表示所有单例bean都被销毁。封闭的环境已经到了生命的尽头。它不能刷新或重新启动。
RequestHandledEvent一个特定于 Web 的事件,告诉所有 Bean HTTP 请求已得到服务。该事件在请求完成后发布。此事件仅适用于使用Spring 的DispatcherServlet的 Web 应用程序。
ServletRequestHandledEventRequestHandledEvent的子类,用以添加Servlet特定的上下文信息

参考资料:

  1. Spring docs
  2. Spring Integration
  3. Spring 表达语言
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值