天天看点

Spring事件摸摸摸

Spring事件

概述

Spring事件是

Spring Framework

中观察者模式的体现,其主要应用于

ApplicationContext

生命周期中各关键点的通知。

文档:

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/htmlsingle/#context-functionality-events

文档中有一段特别说明,大概的意思就是从4.2版本开始事件得到了巨大的升级,加入了基于注解的使用方式和用任意对象作为事件,下面都会进行讲解。

As of Spring 4.2, the event infrastructure has been significantly improved and offers an annotation-based model as well as the ability to publish any arbitrary event (that is, an object that does not necessarily extend from ApplicationEvent). When such an object is published, we wrap it in an event for you.      

分类

从文档章节标题来看,事件分为标准事件和自定义事件。标准事件就是Spring内置的事件,下图就是

Spring Framework

中的标准事件;自定义事件就是用户利用框架事件机制创建的事件。

Spring事件摸摸摸

原始用法

最早的实现方式是继承和实现对应的接口,主要包括

ApplicationEvent

ApplicationEventPublisherAware

ApplicationListener

1. 定义事件

事件需要继承

ApplicationEvent

,事件中包含了你需要传递的数据

public class BlockedListEvent extends ApplicationEvent {

    private final String address;
    private final String content;

    public BlockedListEvent(Object source, String address, String content) {
        super(source);
        this.address = address;
        this.content = content;
    }

    // accessor and other methods...
}      

2. 创建事件监听

监听要实现

ApplicationListener

接口,实现接收事件方法

onApplicationEvent(evetn)

public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    public void onApplicationEvent(BlockedListEvent event) {
        // 处理收到的事件
    }
}      

3. 发布事件

发布事件使用框架内的

ApplicationEventPublisher

实现类的实例。由于

ApplicationContext

也实现了该接口,所以用

ApplicationContext

也可以发布事件,代码

applicationContext.publishEvent(event)

public class EmailService implements ApplicationEventPublisherAware {

    private List<String> blockedList;
    private ApplicationEventPublisher publisher;

    public void setBlockedList(List<String> blockedList) {
        this.blockedList = blockedList;
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void sendEmail(String address, String content) {
        if (blockedList.contains(address)) {
            publisher.publishEvent(new BlockedListEvent(this, address, content));
            return;
        }
        // send email...
    }
}      

4. 总结

这是最原始的使用方式,由于Java是单继承,这样会产生很大的限制,而且继承和实现也会导致框架的侵入性

用注解优化Listener

作为上一种使用方式的升级,注解的加入不但减少了框架的侵入还支持了监听多个事件甚至做到了按条件监听事件

// 基本用法
public class BlockedListNotifier {

    @EventListener
    public void processBlockedListEvent(BlockedListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}
// 监听多个事件,但注意方法参数
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
    // ...
}
// 按特定条件接收事件,具体用法见官方文档
@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blEvent) {
    // notify appropriate parties via notificationAddress...
}
// 接收事件后发布另一个事件,注意返回值,甚至你可以返回一批事件。另外,文档中特别注明异步事件不支持
@EventListener
public ListUpdateEvent handleBlockedListEvent(BlockedListEvent event) {
    // notify appropriate parties via notificationAddress and
    // then publish a ListUpdateEvent...
}
//      

异步事件

默认情况下,

Listener

是同步处理事件的,你可以理解为发布事件的方法同步调用处理事件的方法。但通常情况是希望是异步调用。那么实现方式是使用

@Async

注解。

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

最后说明一下,异步事件中,

Listener

抛出的异常不会传递到发布方。也无法通过返回值来发布新的事件。

循序监听

当有多个监听器监听同一个事件且需要指定监听的顺序时,需要使用

@Order

指定。

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

通过泛型限定监听范围

通过对事件增加泛型,可以控制监听器监听的事件范围

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

如上,当需要定义一个非固定类型的泛型事件时,可以用下面的方式

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框架也在4.2进行了升级,任意的对象都可以作为一个事件发布出去,也就是说事件不必继承

ApplicationEvent

,普通的Java Bean就可以作为事件。

public class BlockedListEvent {

    private final String address;
    private final String content;

    public BlockedListEvent(String address, String content) {
        this.address = address;
        this.content = content;
    }

    // accessor and other methods...
}      

ApplicationEventPublisher

也有对应的发布方法

Spring事件摸摸摸