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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM