觀察者模式 一、Java觀察者模式 Java觀察者模式是一種設計模式,用於實現對象之間的一對多依賴關係。在觀察者模式中,當一個對象的狀態發生變化時,它的所有依賴對象(觀察者)都會自動收到通知併進行相應的更新。 觀察者模式由以下幾個核心組件組成: 主題(Subject):也稱為被觀察者或可觀察對象,它 ...
觀察者模式
一、Java觀察者模式
Java觀察者模式是一種設計模式,用於實現對象之間的一對多依賴關係。在觀察者模式中,當一個對象的狀態發生變化時,它的所有依賴對象(觀察者)都會自動收到通知併進行相應的更新。
觀察者模式由以下幾個核心組件組成:
- 主題(Subject):也稱為被觀察者或可觀察對象,它維護一組觀察者對象,並提供方法用於添加、刪除和通知觀察者。
- 觀察者(Observer):也稱為訂閱者或監聽器,它定義了接收和處理主題通知的方法。
- 具體主題(ConcreteSubject):實現主題介面,維護觀察者列表,併在狀態發生變化時通知觀察者。
- 具體觀察者(ConcreteObserver):實現觀察者介面,定義了接收和處理主題通知的具體邏輯。
觀察者模式的工作流程如下:
- 觀察者通過訂閱主題來註冊自己,使得主題知道它們的存在。
- 當主題的狀態發生變化時,它會通知所有註冊的觀察者。
- 觀察者收到通知後,根據需要進行相應的更新操作。
觀察者模式的優點包括解耦主題和觀察者,使得它們可以獨立變化;支持動態添加和刪除觀察者;實現了對象之間的松耦合,提高了系統的靈活性和可擴展性。
- 場景1:用圖來理解:
- 場景2:多模塊開發,解耦情況下,可以模塊之間傳遞參數。
二、Spring實現的觀察者模式
1、設計理念
在Spring Boot中,實現觀察者模式的設計理念是基於事件驅動的編程模型。Spring Boot提供了一種簡單而強大的事件機制,可以方便地實現觀察者模式。
以下是Spring Boot實現觀察者模式的設計理念:
-
事件(Event):事件是觸發的動作或狀態變化,可以是任何Java對象。在Spring Boot中,事件通常是繼承自ApplicationEvent類的對象。
-
事件發佈者(Event Publisher):事件發佈者負責發佈事件。在Spring Boot中,可以使用ApplicationEventPublisher介面來發佈事件。通常,事件發佈者是一個Spring Bean,通過依賴註入ApplicationEventPublisher來發佈事件。
-
事件監聽者(Event Listener):事件監聽者是觀察者,負責接收和處理事件。在Spring Boot中,可以使用@EventListener註解標記方法,使其成為事件監聽者。當事件發佈者發佈事件時,被標記的方法會自動被調用。
-
事件處理邏輯:事件監聽者方法中定義了事件的處理邏輯。可以根據具體需求,在事件監聽者方法中編寫相應的業務邏輯。
通過使用Spring Boot的事件機制,可以實現松耦合的觀察者模式。事件發佈者和事件監聽者之間沒有直接的依賴關係,它們通過事件進行通信。這樣,可以方便地添加、刪除和修改事件監聽者,而不需要修改事件發佈者的代碼。
觀察者模式的設計理念在Spring Boot中體現了面向對象編程的原則,如單一職責、開閉原則和依賴倒置原則。它提供了一種靈活、可擴展和可維護的方式來實現事件驅動的編程模型。
2、繼承ApplicationEvent和不繼承的區別
- 發送的參數可以是普通JavaBean(String、integer、自定義類)、也可以是繼承了ApplicationEvent的JavaBean
如果MyEvent繼承ApplicationEvent,則可以使用Spring框架提供的事件傳遞機制。這意味著可以通過ApplicationEventPublisher發佈事件,並且可以使用@EventListener註解標記觀察者方法來接收事件。這種方式更符合Spring框架的設計理念,可以方便地實現觀察者模式。
如果MyEvent不繼承ApplicationEvent,則無法使用Spring框架提供的事件傳遞機制。需要自己實現事件的傳遞和觀察者模式的邏輯。這種方式更適用於非Spring環境下的觀察者模式實現。
繼承ApplicationEvent的好處是可以利用Spring框架提供的事件傳遞機制,簡化了觀察者模式的實現。同時,還可以利用Spring框架提供的其他特性,如事務管理、AOP等。
不繼承ApplicationEvent的好處是更加靈活,可以根據具體需求自由定義事件的結構和傳遞方式。但需要自己實現事件的傳遞和觀察者模式的邏輯。
綜上所述,如果在Spring框架中使用觀察者模式,建議繼承ApplicationEvent,以便利用Spring框架提供的事件傳遞機制和其他特性。
3、預設是廣播模式
Spring框架中的觀察者模式預設是廣播模式。
在Spring框架中,當一個事件被髮布時,所有註冊的觀察者都會接收到該事件。這意味著,一個事件可以被多個觀察者同時接收和處理,實現了廣播的效果。
這種廣播模式的實現是通過ApplicationEventMulticaster介面和其預設實現類SimpleApplicationEventMulticaster來完成的。SimpleApplicationEventMulticaster會將事件廣播給所有註冊的觀察者。
當然,如果需要更加細粒度地控制事件的傳遞方式,也可以自定義ApplicationEventMulticaster的實現類,實現自己的事件傳遞邏輯。
總結起來,Spring框架中的觀察者模式預設是廣播模式,一個事件會被所有註冊的觀察者接收和處理。這種廣播模式的實現是通過ApplicationEventMulticaster介面和SimpleApplicationEventMulticaster類來完成的。
三、實現
1、廣播模式(預設)——不推薦
- 事件 —— 這裡先不用,也可以用
- 這裡發送普通的JavaBean的參數,不發送“事件”
1.1、事件(Event)(無)
1.2、事件發佈者(Event Publisher)
- 註入ApplicationEventPublisher來實現
package com.cc.jsl.service.impl;
import com.cc.jsl.service.ILoginService;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* <p>事件發佈者(Event Publisher)</p>
*
* @author CC
* @since 2023/10/10
*/
@Service
public class LoginServiceImpl implements ILoginService {
@Resource
private ApplicationEventPublisher eventPublisher;
@Override
public void login(){
//登陸邏輯...
//發送
// 1、可以直接發送JavaBean的參數:如String、自定義類
eventPublisher.publishEvent("字元串參數!");
// eventPublisher.publishEvent(new UserCs("cs" , 18));
// eventPublisher.publishEvent(34);
// 2、發送事件參數(繼承了ApplicationEvent的類)
}
}
1.3、事件監聽者(Event Listener)
- 使用@Async、@EventListener實現
- 可以是很多個,也可以在不同類中
package com.cc.jsl.listener;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
* <p>事件監聽者(Event Listener)</p>
*
* @author CC
* @since 2023/10/10
*/
@Component
public class ReceiveListener {
/**
* 打電話 - 接收普通JavaBean參數
*/
@Async
@EventListener
public void sendCall(String msg) {
//發送郵件邏輯
System.out.println("打電話!-普通-> " + msg);
}
/**
* 發送郵件 - 接收字元串參數
*/
@Async
@EventListener
public void sendEmail(String msg) {
//發送郵件邏輯
System.out.println("發送郵件!-普通-> " + msg);
}
}
2、單個發送(推薦)
- 每個發送都自定義一個唯一的類,並且繼承ApplicationEvent
2.1、事件(Event)
- 繼承ApplicationEvent
- 繼承ApplicationEvent的好處是可以利用Spring框架提供的事件傳遞機制,簡化了觀察者模式的實現。同時,還可以利用Spring框架提供的其他特性,如事務管理、AOP等。
package com.cc.jsl.event;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.ApplicationEvent;
/**
* <p>發郵件專屬的唯一事件</p>
* <p>需要實現set方法</p>
*
* @author CC
* @since 2023/10/10
*/
@Getter
@Setter
public class EmailEvent extends ApplicationEvent {
/**
* 參數1
*/
private String name;
/**
* 參數2
*/
private Integer age;
public EmailEvent(Object source, String name, Integer age) {
super(source);
this.name = name;
this.age = age;
}
}
2.2、事件發佈者(Event Publisher)
package com.cc.jsl.service.impl;
import com.cc.jsl.event.EmailEvent;
import com.cc.jsl.service.ILoginService;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* <p>事件發佈者(Event Publisher)</p>
*
* @author CC
* @since 2023/10/10
*/
@Service
public class LoginServiceImpl implements ILoginService {
@Resource
private ApplicationEventPublisher eventPublisher;
@Override
public void login(){
//登陸邏輯...
//事件發佈
// 1、可以直接發送JavaBean的參數:如String、自定義類
// eventPublisher.publishEvent("字元串參數!");
// eventPublisher.publishEvent(new UserCs("cs" , 18));
// eventPublisher.publishEvent(34);
// 2、發送事件參數(繼承了ApplicationEvent的類)
EmailEvent emailEvent = new EmailEvent(this, "cc", 18);
eventPublisher.publishEvent(emailEvent);
}
}
2.3、事件監聽者(Event Listener)
package com.cc.jsl.listener;
import com.cc.jsl.event.EmailEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
* <p>事件監聽者(Event Listener)</p>
*
* @author CC
* @since 2023/10/10
*/
@Component
public class ReceiveListener {
/**
* 打電話 - 接收普通JavaBean參數
*/
@Async
@EventListener
public void sendCall(String msg) {
//發送郵件邏輯
System.out.println("打電話!-普通-> " + msg);
}
/**
* 發送郵件 - 接收字元串參數
*/
@Async
@EventListener
public void sendEmail(String msg) {
//發送郵件邏輯
System.out.println("發送郵件!-普通-> " + msg);
}
/**
* 發送郵件 - 接收唯一事件
*/
@Async
@EventListener(EmailEvent.class)
public void sendEmail(EmailEvent emailEvent) {
//發送郵件邏輯
System.out.println("發送郵件!-事件-> " + emailEvent);
}
}
- 發送郵件!-事件-> 除了這個以外的所有監聽者都不會監聽到消息
- 經過測試,不使用事件,直接使用自定義類,只有監聽了這個類的接受者才能接收到,所以相當於實現了一對一。
- 自定義事件的好處:可以利用Spring框架提供的事件傳遞機制,簡化了觀察者模式的實現。同時,還可以利用Spring框架提供的其他特性,如事務管理、AOP等。
- 接收到的消息:
四、總結
參考:https://blog.csdn.net/weixin_43745998/article/details/127301003