Spring 事件发布

Posted 蟹烟客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 事件发布相关的知识,希望对你有一定的参考价值。

1、设计模式

基于观察者模式,主要方法为1 监听者注册 2 监听者注销 3 执行监听方法

 

2、使用篇

类结构图 

MsgEvent:事件对象

MsgListener:事件监听

MsgListener2:事件监听(使用注解方式实现)

MsgPublisher:事件发布器

SpringEventTest:单元测试类

测试代码

MsgEvent

@Data
public class MsgEvent extends ApplicationEvent{

    //消息ID
    private String msgId;
    //消息体
    private String payload;

    public MsgEvent(Object source) {
        super(source);
    }

}

MsgListener

@Component
public class MsgListener implements ApplicationListener<MsgEvent> {
    @Override
    public void onApplicationEvent(MsgEvent event) {
        System.out.println("listenter1 got message, ID:"+event.getMsgId()+", payload:"+event.getPayload());
    }
}

MsgListener2

@Component
public class MsgListener2 {

    @EventListener
    public void processMsg(MsgEvent event){
        System.out.println("listenter2 got message, ID:"+event.getMsgId()+", payload:"+event.getPayload());
    }
}

MsgPublisher

@Component
public class MsgPublisher implements ApplicationContextAware {
    //持有当前容器
    private ApplicationContext applicationContext;

    //模拟业务触发事件
    public void publish(){
        MsgEvent msgEvent = new MsgEvent("这是一条消息事件");
        msgEvent.setMsgId(UUID.randomUUID().toString());
        msgEvent.setPayload("事件消息体xxx");
        applicationContext.publishEvent(msgEvent);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

SpringEventTest

public class SpringEventTest extends BaseTest{

    @Autowired
    private MsgPublisher msgPublisher;


    @Test
    public void publish(){
        msgPublisher.publish();
    }
}

打印结果:

listenter2 got message, ID:5938c2c4-0cda-4abc-b187-9d347102e867, payload:事件消息体xxx
listenter1 got message, ID:5938c2c4-0cda-4abc-b187-9d347102e867, payload:事件消息体xxx

 

 

3、源码分析篇

流程简述:

1 注册Listener到容器中,集合存储 (本文忽略注册过程源码,着重发布事件和处理事件代码)

2 获取发布器SimpleApplicationEventMulticaster, 发布器在spring启动时会初始化 initApplicationEventMulticaster()方法 此处不细究

3 发布器根据事件source和事件类class从容器中获取监听器集合

4 遍历监听器集合, 并调用监听器EventListener的onApplication方法

 

类图展示:

 

 

MsgPublisher为入口  跟踪方法 applicationContext.publishEvent(msgEvent)

@Component
public class MsgPublisher implements ApplicationContextAware {
    //持有当前容器
    private ApplicationContext applicationContext;

    //模拟业务触发事件
    public void publish(){
        MsgEvent msgEvent = new MsgEvent("这是source");
        msgEvent.setMsgId(UUID.randomUUID().toString());
        msgEvent.setPayload("事件消息体xxx");
        applicationContext.publishEvent(msgEvent);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

 

AbstractApplicationContext 持续追踪代码

protected void publishEvent(Object event, ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    if (logger.isTraceEnabled()) {
        logger.trace("Publishing event in " + getDisplayName() + ": " + event);
    }

    // Decorate event as an ApplicationEvent if necessary
    ApplicationEvent applicationEvent;
    //若继承自ApplicationEvent 则直接转Application类型
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent) event;
    }
    //非继承自ApplicationEvent 则标准化成 PayloadApplicationEvent
    //继承关系为 PayloadApplicationEvent<T> extends ApplicationEvent 所以最终还是ApplicationEvent方法
    else {
        applicationEvent = new PayloadApplicationEvent<Object>(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
        }
    }

    // 有可能事件广播器正在初始化 则存入事件列表延后处理
    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    }
    //获取事件广播器 并广播事件该时间
    //重点在于时间广播代码
    else {
        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    }

    // Publish event via parent context as well...
    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
        }
        else {
            this.parent.publishEvent(event);
        }
    }
}


ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
    if (this.applicationEventMulticaster == null) {
        throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
                "call \'refresh\' before multicasting events via the context: " + this);
    }
    //返回当前容器广播器 ApplicationEventMulticaster applicationEventMulticaster
    return this.applicationEventMulticaster;
}

SimpleApplicationEventMulticaster 发布器发布事件

//广播事件
public
void multicastEvent(final ApplicationEvent event, ResolvableType eventType) { //事件类型 ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); //事件类型筛选容器中已注册的监听器 并循环调用 for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { Executor executor = getTaskExecutor(); if (executor != null) { executor.execute(new Runnable() { @Override public void run() { //调用监听器 invokeListener(listener, event); } }); } else { //调用监听器 invokeListener(listener, event); } } }
//筛选获取监听器
protected Collection<ApplicationListener<?>> getApplicationListeners( ApplicationEvent event, ResolvableType eventType) { //获取事件内容 Object source = event.getSource(); //获取事件source 可以理解为topic Class<?> sourceType = (source != null ? source.getClass() : null); //构建缓存KEY ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); //Map<ListenerCacheKey , ListenerRetriever> retrieverCache //ListenerRetriever持有事件监听器集合 按ListenerCacheKey分组 ListenerRetriever retriever = this.retrieverCache.get(cacheKey); //若从缓存中获取到retriever 则直接返回持有的监听器集合 if (retriever != null) { return retriever.getApplicationListeners(); } if (this.beanClassLoader == null || (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) { // Fully synchronized building and caching of a ListenerRetriever synchronized (this.retrievalMutex) { retriever = this.retrieverCache.get(cacheKey); if (retriever != null) { return retriever.getApplicationListeners(); } retriever = new ListenerRetriever(true); //根据eventType sourceType筛选监听器 并存入retriever中 Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(eventType, sourceType, retriever); //放入MAP缓存 this.retrieverCache.put(cacheKey, retriever); return listeners; } } else { // No ListenerRetriever caching -> no synchronization necessary return retrieveApplicationListeners(eventType, sourceType, null); } } //调用监听器 事件处理 protected void invokeListener(ApplicationListener listener, ApplicationEvent event) { ErrorHandler errorHandler = getErrorHandler(); if (errorHandler != null) { try { //执行监听器的onApplicationEvent方法 listener.onApplicationEvent(event); } catch (Throwable err) { errorHandler.handleError(err); } } else { try { //执行监听器的onApplicationEvent方法 listener.onApplicationEvent(event); } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || msg.startsWith(event.getClass().getName())) { // Possibly a lambda-defined listener which we could not resolve the generic event type for Log logger = LogFactory.getLog(getClass()); if (logger.isDebugEnabled()) { logger.debug("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } } }

 

以上是关于Spring 事件发布的主要内容,如果未能解决你的问题,请参考以下文章

Spring Rest 文档。片段生成时 UTF-8 中间字节无效 [重复]

初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段

初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段

从活动中调用片段事件

如何使用事件侦听器来加载动画片段的循环

选择选项卡片段时触发啥事件