Spring事件傳播機(jī)制

????Spring是基于事件驅(qū)動(dòng)模型的碎浇,事件驅(qū)動(dòng)模型也就是我們常說(shuō)的觀察者腾它,或者發(fā)布-訂閱模型。理解觀察者模式更有助于理解 Spring 事件機(jī)制碗誉,話不多說(shuō)召嘶,我們先來(lái)看一下 Spring 的事件角色的類(lèi)圖


Spring 的事件角色的類(lèi)圖.png

????從此類(lèi)圖中我們可以得到以下信息:
????1.事件源:如果我們需要實(shí)現(xiàn)事件傳播的話,我們首先需要實(shí)現(xiàn)自己的事件類(lèi)去實(shí)現(xiàn) ApplicationEvent 接口哮缺。
????2.監(jiān)聽(tīng)者:需要定義自己的事件監(jiān)聽(tīng)器類(lèi)去實(shí)現(xiàn) ApplicationListener<E extends ApplicationEvent> 接口苍蔬。
????3.事件發(fā)布:需要有一個(gè)對(duì)象去發(fā)布該事件,從類(lèi)圖中我們可以了解到蝴蜓,應(yīng)用上下文 ApplicationContext 就是一個(gè)天生的事件發(fā)布源。我們通常可以 事件ApplicationEventPublisherAware接口來(lái)注入上下文中的 ApplicationEventPublisher茎匠,既可以通過(guò)它發(fā)布事件

????需要知道的是格仲,在spring源碼中 AbstractApplicationContext 持有了 ApplicationEventMulticaster引用,且通過(guò)他進(jìn)行事件的發(fā)布诵冒,默認(rèn)實(shí)現(xiàn)為 SimpleApplicationEventMulticaster凯肋。那么了解了這些,我們可以來(lái)簡(jiǎn)單實(shí)現(xiàn)一下我們的spring自定義事件:

1.定義一個(gè)類(lèi)汽馋,及源事件:

public class Order {
    private String orderNo;
    private String orderStatus;
    private String goods;
    private Date createTime;
    //省略 get/set
}
//源事件
public class OrderCreateEvent extends ApplicationEvent {

    private final Order order;

    public OrderCreateEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }

    public Order getOrder() {
        return order;
    }
}

2.定義事件監(jiān)聽(tīng)器:

@Component
public class OrderCreateEventListener implements ApplicationListener<OrderCreateEvent> {

    @Override
    public void onApplicationEvent(OrderCreateEvent event) {
        System.out.printf(this.getClass().getName() + " -- ApplicationListener 接口實(shí)現(xiàn)侮东,訂單號(hào)[%s]:,商品[%s]\n",
                event.getOrder().getOrderNo(), event.getOrder().getGoods());
    }
}

3.定義事件發(fā)服務(wù):

@Service
public class OrderService implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;
    /**
     * 訂單保存
     */
    public void save(){
        Order order = new Order();
        order.setOrderNo("1");
        order.setGoods("手機(jī)");
        System.out.println("訂單保存成功:" + order.toString());
        //發(fā)布事件
        applicationEventPublisher.publishEvent(new OrderCreateEvent(this,order));
    }
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}

4.編寫(xiě)測(cè)試類(lèi):

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringStudyApp.class)
public class SpringStudyTest {

    @Autowired
    OrderService orderService;

    @Test
    public void contextLoads() {
        orderService.save();
    }
}

????簡(jiǎn)單的幾步就實(shí)現(xiàn)了自定義的spring事件。

基于注解的事件監(jiān)聽(tīng)@EventListener:

@Component
public class OrderCreateEventListenerAnnotation {
    @EventListener
    public void createOrderEvent(OrderCreateEvent event) {
        System.out.println(this.getClass().getName() + "--訂單創(chuàng)建事件豹芯,@EventListener注解實(shí)現(xiàn)悄雅,orderNo:" + event.getOrder().getOrderNo());
    }
}

異步事件:
  上面的監(jiān)聽(tīng)事件都是同步觸發(fā)的,如果想異步只需要兩步:

  1. 啟動(dòng)類(lèi)上添加 @EnableAsync注解铁蹈,開(kāi)啟異步支持宽闲。
  2. 監(jiān)聽(tīng)方法上添加 @Async注解
    事件傳播機(jī)制:
      當(dāng)我們監(jiān)聽(tīng)一個(gè)事件處理完成時(shí),還需要發(fā)布另一個(gè)事件握牧,一般我們想到的是調(diào)用ApplicationEventPublisher#publishEvent發(fā)布事件方法容诬,但Spring提供了另一種更加靈活的新的事件繼續(xù)傳播機(jī)制,監(jiān)聽(tīng)方法返回一個(gè)事件沿腰,也就是方法的返回值就是一個(gè)事件對(duì)象
@Component
public class OrderListener {

    @EventListener
    public void orderListener(Order order){
        System.out.println(this.getClass().getName() + " -- 監(jiān)聽(tīng)一個(gè)訂單");
    }
//事件傳播機(jī)制
//    當(dāng)我們監(jiān)聽(tīng)一個(gè)事件處理完成時(shí)览徒,還需要發(fā)布另一個(gè)事件,一般我們想到的是調(diào)用ApplicationEventPublisher#publishEvent發(fā)布事件方法颂龙,
//    但Spring提供了另一種更加靈活的新的事件繼續(xù)傳播機(jī)制习蓬,監(jiān)聽(tīng)方法返回一個(gè)事件,也就是方法的返回值就是一個(gè)事件對(duì)象厘托。
    @EventListener
    public OrderCreateEvent orderReturnEvent(Order order){
        System.out.println(this.getClass().getName() + " -- 監(jiān)聽(tīng)一個(gè)訂單,返回一個(gè)新的事件 OrderCreateEvent");
        return new OrderCreateEvent(this,order);
    }
}

????這樣子我們后續(xù)通過(guò) applicationEventPublisher.publishEvent(order); 發(fā)布一個(gè) Order 類(lèi)型的參數(shù)的事件友雳,他會(huì)被上面的 orderReturnEvent 方法監(jiān)聽(tīng)到,然后最后返回一個(gè) OrderCreateEvent 事件對(duì)象铅匹,繼而觸發(fā)該事件的監(jiān)聽(tīng)押赊。

????基于事件驅(qū)動(dòng)模型可以很方便的實(shí)現(xiàn)解耦,提高代碼的可讀性和可維護(hù)性包斑,那么 Spring中是怎么進(jìn)行初始化及使用的呢流礁?我們來(lái)一探究竟。

Spring 事件源碼分析:
????我們知道 Spring 容器的初始化都會(huì)走到 AbstractApplicationContext 的 refresh() 方法:

@Override
public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();
                // 關(guān)鍵來(lái)了罗丰,為此上下文初始化事件多播程序
                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();
                // 調(diào)用子類(lèi)特殊的某些特殊的方法刷新容器
                // Initialize other special beans in specific context subclasses.
                onRefresh();
                // 檢查偵聽(tīng)器bean并注冊(cè)它們神帅。
                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);
         
                // Last step: publish corresponding event.
                finishRefresh();
            }
        }
}

????直接來(lái)看 initApplicationEventMulticaster() 方法:

public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
protected void initApplicationEventMulticaster() {
     // 獲取上面初始化的 DefaultListableBeanFactory 容器
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        // 如果容器里找到了這個(gè)類(lèi),那么直接賦值給成員變量
            this.applicationEventMulticaster =
                    beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
            if (logger.isDebugEnabled()) {
                logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
            }
        }
        else {// 初始化一個(gè) SimpleApplicationEventMulticaster
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
            if (logger.isDebugEnabled()) {
                logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
                        APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
                        "': using default [" + this.applicationEventMulticaster + "]");
            }
        }
    }

????其實(shí)這個(gè)方法主要的作用是保證容器中又一個(gè)被實(shí)例化的ApplicationEventMulticaster 類(lèi)型的Bean萌抵。緊接著等待子類(lèi)特殊的刷新方法結(jié)束基本上整個(gè)容器也就初始化完成了找御。然后調(diào)用 registerListeners():

private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
//注冊(cè)監(jiān)聽(tīng)器
protected void registerListeners() {
        // Register statically specified listeners first.
        for (ApplicationListener<?> listener : getApplicationListeners()) {
       // 遍歷上面這個(gè)集合元镀,將提前儲(chǔ)備好的監(jiān)聽(tīng)器添加到監(jiān)聽(tīng)器容器中
            getApplicationEventMulticaster().addApplicationListener(listener);
        }
     // 獲得容器中類(lèi)型是 ApplicationListener 的 beanName集合
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let post-processors apply to them!
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {
            getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }
        // 發(fā)布這個(gè) earlyApplicationEvents 容器中的事件
        // Publish early application events now that we finally have a multicaster...
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (earlyEventsToProcess != null) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                // 委派上面初始化的 SimpleApplicationEventMulticaster 進(jìn)行廣播發(fā)布
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }

????緊接著來(lái)看 multicastEvent(ApplicationEvent event):

@Override
public void multicastEvent(ApplicationEvent event) {
  multicastEvent(event, resolveDefaultEventType(event));
}

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
     // 再去通過(guò)event去獲得一個(gè)ResolvableType
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        // 通過(guò)事件及類(lèi)型進(jìn)行獲取事件監(jiān)聽(tīng)器進(jìn)行遍歷
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            // 當(dāng) executor 不為空,這里走的是異步事件
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            // 同步事件
            else {
                invokeListener(listener, event);
            }
        }
}

????從這里去調(diào)用 listener 的 onApplicationEvent 方法:

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    try {
        listener.onApplicationEvent(event);
    }
    //.....省略部分代碼
}

????像容器初始化完成霎桅,AbstractApplicationContext的 finishRefresh() 方法中

protected void finishRefresh() {
        // Clear context-level resource caches (such as ASM metadata from scanning).
        clearResourceCaches();

        // Initialize lifecycle processor for this context.
        initLifecycleProcessor();

        // Propagate refresh to lifecycle processor first.
        getLifecycleProcessor().onRefresh();
                // 發(fā)布容器刷新完成事件
        // Publish the final event.
        publishEvent(new ContextRefreshedEvent(this));

        // Participate in LiveBeansView MBean, if active.
        LiveBeansView.registerApplicationContext(this);
}

????這里所調(diào)用的 publishEvent(new ContextRefreshedEvent(this)) 最終還是走 multicastEvent(ApplicationEvent event) 方法的栖疑。這就是Spring事件的基本處理流程。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末滔驶,一起剝皮案震驚了整個(gè)濱河市遇革,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌揭糕,老刑警劉巖萝快,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異著角,居然都是意外死亡揪漩,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)雇寇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)氢拥,“玉大人,你說(shuō)我怎么就攤上這事锨侯∧酆#” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵囚痴,是天一觀的道長(zhǎng)叁怪。 經(jīng)常有香客問(wèn)我,道長(zhǎng)深滚,這世上最難降的妖魔是什么奕谭? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮痴荐,結(jié)果婚禮上血柳,老公的妹妹穿的比我還像新娘。我一直安慰自己生兆,他們只是感情好难捌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著鸦难,像睡著了一般根吁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上合蔽,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天击敌,我揣著相機(jī)與錄音,去河邊找鬼拴事。 笑死沃斤,一個(gè)胖子當(dāng)著我的面吹牛圣蝎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播轰枝,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼捅彻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了鞍陨?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤从隆,失蹤者是張志新(化名)和其女友劉穎诚撵,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體键闺,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡寿烟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了辛燥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筛武。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖挎塌,靈堂內(nèi)的尸體忽然破棺而出徘六,到底是詐尸還是另有隱情,我是刑警寧澤榴都,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布待锈,位于F島的核電站,受9級(jí)特大地震影響嘴高,放射性物質(zhì)發(fā)生泄漏竿音。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一拴驮、第九天 我趴在偏房一處隱蔽的房頂上張望春瞬。 院中可真熱鬧,春花似錦套啤、人聲如沸宽气。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)抹竹。三九已至,卻和暖如春止潮,著一層夾襖步出監(jiān)牢的瞬間窃判,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工喇闸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留袄琳,地道東北人询件。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像唆樊,于是被迫代替她去往敵國(guó)和親宛琅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355