觀察者模式在實際開發過程中是非常常見的一種設計模式。 Spring Event的原理就是觀察者模式,只不過有Spring的加持,讓我們更加方便的使用這一設計模式。 一、什麼是觀察者模式 概念: 觀察者模式又叫發佈-訂閱模式。 發佈指的是當目標對象的狀態改變時,它就向它所有的觀察者對象發佈狀態更改的消 ...
觀察者模式在實際開發過程中是非常常見的一種設計模式。
Spring Event的原理就是觀察者模式,只不過有Spring的加持,讓我們更加方便的使用這一設計模式。
一、什麼是觀察者模式
概念
: 觀察者模式又叫發佈-訂閱模式。
發佈指的是當目標對象的狀態改變時,它就向它所有的觀察者對象發佈狀態更改
的消息,以讓這些觀察者對象知曉。
舉例
:
網上有一個非常符合觀察者模式的例子
當溫度有變化,對應的儀錶盤也會跟著變化。
一個儀錶盤可以當作一個觀察者,去掉一個儀錶盤或者新增一個儀錶盤跟目標對象(溫度)是解耦的,不是強綁定關係。
一句話:感知變化,相應變化
二、觀察者模式 VS 責任鏈模式
這兩種設計模式是有相似的地方,但其實有很大的區別。
我們先來看相似的點,就好比上面的這個例子,我們是不是也可以用責任鏈模式來實現?
當然可以了。
當溫度變化了,一條一條鏈路的執行下去就是了。
當然如果是我,這個功能在選擇設計模式的時候,我還是會選擇使用觀察者模式。
1、區別
我個人認為主要有四點區別:
「第一點」:我們也會稱觀察者模式為發佈訂閱模式,作為訂閱者來講,每個訂閱者是平級的,也就是每個觀察者對象是平級的,但責任鏈可以有先後次序。
比如我們在電商場景中,有個電商活動,這個商品需要先走 包郵活動->滿減送->會員折扣活動->積分抵扣活動。
這個責任鏈的順序不同會導致最終優惠的價格不同。
「第二點」:所有觀察者一般接收統一參數,但責任鏈獲取的參數可能是上一個鏈路已經處理完成的
就好比上面的電商活動,會員折扣活動計算後的價格,還會傳入到積分抵扣活動中。
「第三點」:觀察者的對象都會執行,但責任鏈這我們可以在得到滿意結果直接返回。
比如我想查一個份數據,這個數據可以先從A -> B -> C,三個介面獲得。只要返回數據,這個鏈路就不用往下走了。
「第四點」:觀察者模式可以做非同步操作,我們說的MQ發佈訂閱模式,就是完全非同步,但是責任鏈不太適合走非同步。
三、代碼示例
1、觀察者模式有哪些角色
抽象被觀察者
: 定義了一個介面,包含了註冊觀察者、刪除觀察者、通知觀察者等方法。
具體被觀察者
: 實現了抽象被觀察者介面,維護了一個觀察者列表,併在狀態發生改變時通知所有註冊的觀察者。
抽象觀察者
: 定義了一個介面,包含了更新狀態的方法。
具體觀察者
: 實現了抽象觀察者介面,在被觀察者狀態發生改變時進行相應的處理。
-
抽象被觀察者
/**
* 抽象被觀察者
*/
public interface ISubject {
/**
* 新增觀察者
*/
boolean attach(IObserver observer);
/**
* 刪除觀察者
*/
boolean detach(IObserver observer);
/**
* 通知觀察者
*/
void notify(String event);
}
-
抽象觀察者
/**
* 抽象觀察者
*/
public interface IObserver {
/**
* 觀察者所執行方法
*/
void update(String event);
}
-
具體被觀察者
/**
* 具體被觀察者
*/
public class ConcreteSubject implements ISubject {
private List<IObserver> observers = new ArrayList<>();
@Override
public boolean attach(IObserver observer) {
return this.observers.add(observer);
}
@Override
public boolean detach(IObserver observer) {
return this.observers.remove(observer);
}
@Override
public void notify(String event) {
System.out.println("被觀察者: 數據變更 = " + event);
for (IObserver observer : this.observers) {
observer.update(event);
}
}
}
-
具體觀察者
/**
* 具體觀察者
*/
public class ConcreteObserver implements IObserver {
@Override
public void update(String event) {
System.out.println("觀察者: 收到被觀察者的溫度變動: " + event);
}
}
-
測試
/**
* 測試
*/
public class ClientTest {
public static void main(String[] args) {
// 被觀察者
ISubject subject = new ConcreteSubject();
// 觀察者
IObserver observer = new ConcreteObserver();
// 將觀察者註冊
subject.attach(observer);
// 被觀察者通知觀察者
subject.notify("溫度從6變到7");
}
}
運行結果
被觀察者: 數據變更 = 溫度從6變到7
觀察者: 收到被觀察者的溫度變動: 溫度從6變到7
當然上面這種模式也太傻了吧,下麵就通過Spring Event實現觀察者模式,非常方便。
四、Spring Event 實現觀察者模式
Spring 基於觀察者模式實現了自身的事件機制,由三部分組成:
事件 ApplicationEvent
: 通過繼承它,實現自定義事件。
事件發佈者 ApplicationEventPublisher
: 通過它,可以進行事件的發佈。
事件監聽器 ApplicationListener
: 通過實現它,進行指定類型的事件的監聽。
這裡以下麵案例實現,當一個用戶出現欠費,那麼通過觀察者模式通過 簡訊通知
,郵箱通知
,微信通知
,到具體用戶
1、事件 UserArrearsEvent
繼承 ApplicationEvent
類,用戶欠費事件。
/**
* 用戶欠費事件 繼承ApplicationEvent
*/
public class UserArrearsEvent extends ApplicationEvent {
/**
* 用戶名
*/
private String username;
public UserArrearsEvent(Object source, String username) {
super(source);
this.username = username;
}
public String getUsername() {
return username;
}
}
2、被觀察者 UserArrearsService
/**
* 被觀察者 實現ApplicationEventPublisherAware 介面
*/
@Service
public class UserArrearsService implements ApplicationEventPublisherAware {
private Logger logger = LoggerFactory.getLogger(getClass());
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
public void arrears(String username) {
// 執行欠費邏輯
logger.info("被觀察者 用戶欠費,用戶名稱", username);
// 發佈
applicationEventPublisher.publishEvent(new UserArrearsEvent(this, username));
}
}
-
實現
ApplicationEventPublisherAware 介面
,從而將 ApplicationEventPublisher 註入到其中。 -
在執行完註冊邏輯後,調用 ApplicationEventPublisher的
publishEvent
方法,發佈 UserArrearsEvent 事件。
3、觀察者 EmailService
/**
* 觀察者 郵箱欠費通知
*/
@Service
public class EmailService implements ApplicationListener<UserArrearsEvent> {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
@Async
public void onApplicationEvent(UserArrearsEvent event) {
logger.info("郵箱欠費通知,你好 {} ,請儘快繳費啊啊啊啊!", event.getUsername());
}
}
-
實現
ApplicationListener
介面,通過 E 泛型設置感興趣的事件。 -
實現
onApplicationEvent
方法,針對監聽的 UserRegisterEvent 事件,進行自定義處理。 -
設置
@Async
註解,那就代表走非同步操作。同時需要在啟動類上添加@EnableAsync
,這樣非同步才生效。
4、觀察者 SmsService
/**
* 簡訊欠費通知
*/
@Service
public class SmsService {
private Logger logger = LoggerFactory.getLogger(getClass());
@EventListener
public void smsArrears(UserArrearsEvent event) {
logger.info("簡訊欠費通知,你好 {} ,請儘快繳費啊啊啊啊!", event.getUsername());
}
}
這裡提供另一種方式,就是在方法上,添加 @EventListener
註解,並設置監聽的事件為 UserRegisterEvent。
5、介面測試
/**
* 測試 Sping Event觀察者模式
*/
@RestController
@RequestMapping("/test")
public class DemoController {
@Autowired
private UserArrearsService userArrearsService;
@GetMapping("/arrears")
public String arrears(String username) {
userArrearsService.arrears(username);
return "成功";
}
}
日誌輸出
被觀察者 用戶欠費,用戶名稱 = 張老三
簡訊欠費通知,你好 張老三 ,請儘快繳費啊啊啊啊!
郵箱欠費通知,你好 張老三 ,請儘快繳費啊啊啊啊!
成功!
GitHub地址
:https://github.com/yudiandemingzi/spring-boot-study
作者|binron
本文來自博客園,作者:古道輕風,轉載請註明原文鏈接:https://www.cnblogs.com/88223100/p/Spring-Event-Observer-Mode-Business-Decoupling-Artifact.html