偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Spring Event 業(yè)務(wù)解耦神器,大大提高可擴展性,刷爆了!

系統(tǒng)
Spring 使用反射機制,獲取了所有繼承 ApplicationListener 接口的監(jiān)聽器,在 Spring 初始化時,會把監(jiān)聽器都自動注冊到注冊表中。

一、前言

ApplicationContext 中的事件處理是通過 ApplicationEvent 類和 ApplicationListener 接口提供的。如果將實現(xiàn)了 ApplicationListener 接口的 bean 部署到容器中,則每次將 ApplicationEvent 發(fā)布到ApplicationContext 時,都會通知到該 bean,這簡直是典型的觀察者模式。設(shè)計的初衷就是為了系統(tǒng)業(yè)務(wù)邏輯之間的解耦,提高可擴展性以及可維護性。

Spring 中提供了以下的事件:

二、ApplicationEvent 與 ApplicationListener 應(yīng)用

1.實現(xiàn)

自定義事件類,基于 ApplicationEvent 實現(xiàn)擴展:

public class DemoEvent extends ApplicationEvent {
    private static final long serialVersionUID = -2753705718295396328L;
    private String msg;

    public DemoEvent(Object source, String msg) {
        super(source);
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

定義 Listener 類,實現(xiàn) ApplicationListener接口,并且注入到 IOC 中。等發(fā)布者發(fā)布事件時,都會通知到這個bean,從而達到監(jiān)聽的效果。

@Component
public class DemoListener implements ApplicationListener<DemoEvent> {
    @Override
    public void onApplicationEvent(DemoEvent demoEvent) {
        String msg = demoEvent.getMsg();
        System.out.println("bean-listener 收到了 publisher 發(fā)布的消息: " + msg);
    }
}

要發(fā)布上述自定義的 event,需要調(diào)用 ApplicationEventPublisher 的 publishEvent 方法,我們可以定義一個實現(xiàn) ApplicationEventPublisherAware 的類,并注入 IOC來進行調(diào)用:

@Component
public class DemoPublisher implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void sendMsg(String msg) {
        applicationEventPublisher.publishEvent(new DemoEvent(this, msg));
    }
}

客戶端調(diào)用 publisher:

@RestController
@RequestMapping("/event")
public class DemoClient implements ApplicationContextAware {
    private ApplicationContext applicationContext;

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

    @GetMapping("/publish")
    public void publish(){
        DemoPublisher bean = applicationContext.getBean(DemoPublisher.class);
        bean.sendMsg("發(fā)布者發(fā)送消息......");
    }
}

輸出結(jié)果:

bean-listener 收到了 publisher 發(fā)布的消息: 發(fā)布者發(fā)送消息......

2.基于注解

我們可以不用實現(xiàn) AppplicationListener 接口 ,在方法上使用 @EventListener 注冊事件。如果你的方法應(yīng)該偵聽多個事件,并不使用任何參數(shù)來定義,可以在 @EventListener 注解上指定多個事件。

重寫 DemoListener 類如下:

public class DemoListener {
    @EventListener(value = {DemoEvent.class, TestEvent.class})
    public void processApplicationEvent(DemoEvent event) {
        String msg = event.getMsg();
        System.out.println("bean-listener 收到了 publisher 發(fā)布的消息: " + msg);
    }
}

3.事件過濾

如果希望通過一定的條件對事件進行過濾,可以使用 @EventListener 的 condition 屬性。以下實例中只有 event 的 msg 屬性是 my-event 時才會進行調(diào)用。

@EventListener(value = {DemoEvent.class, TestEvent.class}, condition = "#event.msg == 'my-event'")
public void processApplicationEvent(DemoEvent event) {
     String msg = event.getMsg();
     System.out.println("bean-listener 收到了 publisher 發(fā)布的消息: " + msg);
 }

此時,發(fā)送符合條件的消息,listener 才會偵聽到 publisher 發(fā)布的消息。

bean-listener 收到了 publisher 發(fā)布的消息: my-event

4.異步事件監(jiān)聽

前面提到的都是同步處理事件,那如果我們希望某個特定的偵聽器異步去處理事件,如何做?

使用 @Async 注解可以實現(xiàn)類內(nèi)方法的異步調(diào)用,這樣方法在執(zhí)行的時候,將會在獨立的線程中被執(zhí)行,調(diào)用者無需等待它的完成,即可繼續(xù)其他的操作。

@EventListener
@Async
public void processApplicationEvent(DemoEvent event) {
    String msg = event.getMsg();
    System.out.println("bean-listener 收到了 publisher 發(fā)布的消息: " + msg);
}

使用異步監(jiān)聽時,有兩點需要注意:

  • 如果異步事件拋出異常,則不會將其傳播到調(diào)用方。
  • 異步事件監(jiān)聽方法無法通過返回值來發(fā)布后續(xù)事件,如果需要作為處理結(jié)果發(fā)布另一個事件,請插入 ApplicationEventPublisher 以手動發(fā)布事件

三、好處及應(yīng)用場景

ApplicationContext 在運行期會自動檢測到所有實現(xiàn)了 ApplicationListener 的 bean,并將其作為事件接收對象。當我們與 spring 上下文交互觸發(fā) publishEvent 方法時,每個實現(xiàn)了 ApplicationListener 的 bean 都會收到 ApplicationEvent 對象,每個 ApplicationListener 可以根據(jù)需要只接收自己感興趣的事件。

這樣做有什么好處呢?

在傳統(tǒng)的項目中,各個業(yè)務(wù)邏輯之間耦合性比較強,controller 和 service 間都是關(guān)聯(lián)關(guān)系,然而,使用 ApplicationEvent 監(jiān)聽 publisher 這種方式,類間關(guān)系是什么樣的?我們不如畫張圖來看看。

DemoPublisher 和 DemoListener 兩個類間并沒有直接關(guān)聯(lián),解除了傳統(tǒng)業(yè)務(wù)邏輯兩個類間的關(guān)聯(lián)關(guān)系,將耦合降到最小。這樣在后期更新、維護時難度大大降低了。

ApplicationEvent 使用觀察者模式實現(xiàn),那什么時候適合使用觀察者模式呢?觀察者模式也叫 發(fā)布-訂閱模式,例如,微博的訂閱,我們訂閱了某些微博賬號,當這些賬號發(fā)布消息時,我們都會收到通知。

總結(jié)來說,定義對象間的一種一對多的依賴關(guān)系,當一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新,從而實現(xiàn)廣播的效果。

四、源碼閱讀

Spring中的事件機制流程:

  • ApplicationEventPublisher是Spring的事件發(fā)布接口,事件源通過該接口的pulishEvent方法發(fā)布事件
  • ApplicationEventMulticaster就是Spring事件機制中的事件廣播器,它默認提供一個SimpleApplicationEventMulticaster實現(xiàn),如果用戶沒有自定義廣播器,則使用默認的。它通過父類AbstractApplicationEventMulticaster的getApplicationListeners方法從事件注冊表(事件-監(jiān)聽器關(guān)系保存)中獲取事件監(jiān)聽器,并且通過invokeListener方法執(zhí)行監(jiān)聽器的具體邏輯
  • ApplicationListener就是Spring的事件監(jiān)聽器接口,所有的監(jiān)聽器都實現(xiàn)該接口,本圖中列出了典型的幾個子類。其中RestartApplicationListnener在SpringBoot的啟動框架中就有使用
  • 在Spring中通常是ApplicationContext本身擔任監(jiān)聽器注冊表的角色,在其子類AbstractApplicationContext中就聚合了事件廣播器ApplicationEventMulticaster和事件監(jiān)聽器ApplicationListnener,并且提供注冊監(jiān)聽器的addApplicationListnener方法

通過上圖就能較清晰的知道當一個事件源產(chǎn)生事件時,它通過事件發(fā)布器ApplicationEventPublisher發(fā)布事件,然后事件廣播器ApplicationEventMulticaster會去事件注冊表ApplicationContext中找到事件監(jiān)聽器ApplicationListnener,并且逐個執(zhí)行監(jiān)聽器的onApplicationEvent方法,從而完成事件監(jiān)聽器的邏輯。

來到ApplicationEventPublisher 的 publishEvent 方法內(nèi)部:

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
 if (this.earlyApplicationEvents != null) {
 this.earlyApplicationEvents.add(applicationEvent);
 }
 else {
  // 
  getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
 }
}

多播事件方法:

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
 ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
 Executor executor = getTaskExecutor();
 // 遍歷所有的監(jiān)聽者
 for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
  if (executor != null) {
   // 異步調(diào)用監(jiān)聽器
   executor.execute(() -> invokeListener(listener, event));
  }
  else {
   // 同步調(diào)用監(jiān)聽器
   invokeListener(listener, event);
  }
 }
}

invokeListener:

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
 ErrorHandler errorHandler = getErrorHandler();
 if (errorHandler != null) {
  try {
   doInvokeListener(listener, event);
  }
  catch (Throwable err) {
   errorHandler.handleError(err);
  }
 }
 else {
  doInvokeListener(listener, event);
 }
}

doInvokeListener:

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
 try {
  // 這里是事件發(fā)生的地方
  listener.onApplicationEvent(event);
 }
 catch (ClassCastException ex) {
  ......
 }
}

點擊 ApplicationListener 接口 onApplicationEvent 方法的實現(xiàn),可以看到我們重寫的方法。

五、總結(jié)

Spring 使用反射機制,獲取了所有繼承 ApplicationListener 接口的監(jiān)聽器,在 Spring 初始化時,會把監(jiān)聽器都自動注冊到注冊表中。

Spring 的事件發(fā)布非常簡單,我們來總結(jié)一下:

  • 定義一個繼承 ApplicationEvent 的事件
  • 定義一個實現(xiàn) ApplicationListener 的監(jiān)聽器或者使用 @EventListener 監(jiān)聽事件
  • 定義一個發(fā)送者,調(diào)用 ApplicationContext 直接發(fā)布或者使用 ApplicationEventPublisher 來發(fā)布自定義事件

最后,發(fā)布-訂閱模式可以很好的將業(yè)務(wù)邏輯進行解耦(上圖驗證過),大大提高了可維護性、可擴展性。

責任編輯:趙寧寧 來源: 技術(shù)老男孩
相關(guān)推薦

2023-05-17 15:53:21

2020-08-18 08:04:16

DubboSPI框架

2021-05-17 07:28:23

Spring可擴展性項目

2022-02-28 10:02:54

Linux技巧命令

2022-08-02 16:38:53

惡意軟件密碼

2024-03-17 20:01:51

2021-09-02 09:42:11

測試軟件可擴展性開發(fā)

2013-03-29 10:23:02

數(shù)據(jù)庫癌癥治療

2021-12-21 09:50:02

Java請求合并代碼

2025-05-20 07:13:22

Spring異步解耦Event

2012-06-04 11:04:46

虛擬化

2022-09-05 15:17:34

區(qū)塊鏈比特幣可擴展性

2022-07-13 08:36:57

MQ架構(gòu)設(shè)計模式

2009-11-30 17:47:24

2010-02-26 15:07:20

WCF單例服務(wù)

2021-12-03 14:41:00

云存儲可擴展性存儲

2024-10-10 14:01:34

2021-09-09 08:23:11

Vue 技巧 開發(fā)工具

2016-10-13 14:38:51

OpenStack可擴展性IT人員

2021-12-09 05:36:16

云存儲可擴展性數(shù)據(jù)存儲云存儲
點贊
收藏

51CTO技術(shù)棧公眾號