觀察者模式與spring guava事件

1. 觀察者模式

觀察者模式,也叫發佈/訂閱(Publish/Subscribe)模式,觀察者模式比較簡單,但是非常常用,當然也非常實用。

觀察者模式最大的好處是,當業務邏輯變得複雜之後,通過觀察者模式可以減輕耦合。

舉個簡單的例子,一個簡單的用戶充值事件,當業務複雜之後,除了處理訂單之外,可能還需要送券、獎勵積分金幣、提示VIP等級、送抽獎等激勵活動、處理邀請碼、發送重要事件記錄日誌、處理重要事件異常操作等等。

如果這些邏輯都放在“用戶充值”這個流程之中,那肯定邏輯耦合爆炸。所以,一般不這麼幹。

如果用戶規模大,做了服務拆分,那就走消息系統,這本質上還是觀察者模式。

如果沒有做服務拆分,像guava的EventBus這樣的觀察者模式組件就派上用場了。

組件 說明
Subject 抽象主題、被觀察者,一般是事件,例如用戶充值、配置變化、VIP等級提升等事件
ConcreteSubject 主要提供觀察者註冊、取消,以及當事件發送時,回調已經註冊的觀察者的接口
Observer 抽象觀察者
ConcrereObserver 具體觀察者,在具體事件、如用戶充值這個事情發送之後要執行的具體邏輯

2. Guava EventBus

首先,可以先定義一些自定義事件,例如用戶充值:

import vip.mycollege.tool.guava.bean.ChargeBean;
import vip.mycollege.tool.guava.bean.UserBean;

import java.io.Serializable;

public class ChargeEvent implements Serializable{

    private static final long serialVersionUID = -8775590840527884827L;

    private UserBean user;

    private ChargeBean charge;

    public UserBean getUser() {
        return user;
    }

    public void setUser(UserBean user) {
        this.user = user;
    }

    public ChargeBean getCharge() {
        return charge;
    }

    public void setCharge(ChargeBean charge) {
        this.charge = charge;
    }
}

然後,添加觀察者,也就是當事件發生時候,需要執行的邏輯:

import com.google.common.eventbus.Subscribe;
import vip.mycollege.tool.guava.event.ChargeEvent;

import java.util.concurrent.TimeUnit;

public class CommonListener {

    @Subscribe
    public void dealChargeGold(ChargeEvent event){
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("處理gold");
    }

    @Subscribe
    public void dealChargeLevel(ChargeEvent event){
        System.out.println("處理level");
    }
}

guava中的觀察者非常簡單,不需要實現什麼接口,只需要添加@Subscribe方法註解就可以了,方法的參數就是要觀察的具體事件。

接下來需要Subject,在guava中也非常簡單:

import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import vip.mycollege.tool.guava.event.ChargeEvent;
import vip.mycollege.tool.guava.listener.CommonListener;

import java.util.concurrent.Executors;

public class EventBusStart {
    public static void main(String[] args) {
        EventBus eventBus = new EventBus();
        eventBus.register(new CommonListener());
        eventBus.post(new ChargeEvent());

        AsyncEventBus asyncEventBus = new AsyncEventBus(Executors.newCachedThreadPool());
        asyncEventBus.register(new CommonListener());
        asyncEventBus.post(new ChargeEvent());
        System.out.println("main");
    }
}

EventBus是同步事件,它使用一個main線程順序的分發Event。

AsyncEventBus可以指定一個線程池來異步分發Event。使用AsyncEventBus需要注意業務邏輯中應該沒有先後順序約束。

**register方法:**註冊時間,具體的就是去掃描參數類中的@Subscribe註解方法。

**post方法:**具體事件發生時候,觸發,然後EventBus就去回調register中觀察了該事件(參數和該事件類型一致)的@Subscribe方法。

3. Spring Event

除了guava之外,Spring套裝也提供了觀察者模式的實現,重要的類:

  1. ApplicationEvent:抽象事件,具體事件可以繼承這個類
  2. ApplicationListener:觀察者,需要實現onApplicationEvent方法,處理具體的事件發生邏輯
  3. ApplicationEventPublisher:發佈事件,具體事件發生時,通過它觸發

還是一個簡單的實例:

import org.springframework.context.ApplicationEvent;

public class LoginEvent extends ApplicationEvent {

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

觀察者:

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class LoginEventListener implements ApplicationListener<LoginEvent> {

    @Override
    public void onApplicationEvent(LoginEvent event) {
       Map<String,String> map = (Map<String, String>) event.getSource();
        System.out.println(map.keySet());
        System.out.println(map.values());
    }
}

除了要繼承ApplicationListener,還需要添加@Component之類的註解,這樣通過註解掃描才能創建這個類。

事件發佈:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import java.util.HashMap;

@Component
public class LoginLogic {

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void login(){
        HashMap<String, String> params = new HashMap<>();
        params.put("name","tim");
        params.put("age","25");
        LoginEvent loginEvent = new LoginEvent(params);
        applicationEventPublisher.publishEvent(loginEvent);
    }

}

Spring在啓動的時候,會自動創建一個ApplicationEventPublisher,所以我們可以直接注入就可以了。

當具體的事件發生時候,直接通過ApplicationEventPublisher觸發響應的事件就好了,只需要簡單的封裝一下事件參數。

Spring的觀察者模式比guava的稍微複雜一些,如果僅僅是需要觀察者模式,建議使用guava,如果是已經使用了Spring,需要觀察者模式,可以考慮使用Spring自帶的觀察者模式實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章