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套裝也提供了觀察者模式的實現,重要的類:
- ApplicationEvent:抽象事件,具體事件可以繼承這個類
- ApplicationListener:觀察者,需要實現onApplicationEvent方法,處理具體的事件發生邏輯
- 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自帶的觀察者模式實現。