1. 实现观察者模式的使用案例
- 观察者模式核心是让事件的发布者去观察消息
就是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
1.1 气象站的天气信息 : 将天气信息的更新消息同步到订阅了天气信息的人上
java
package club.shengsheng.desigin.oberver;
import club.shengsheng.desigin.oberver.TVStation.TVStation;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
// 气象站
public class WeatherStation {
private List<User> userList = new ArrayList<User>();
private final TVStation station;
// 把电视台交给气象站
public WeatherStation(TVStation station) {
this.station = station;
}
public String getInfo(){
if(new Random().nextBoolean()){
return "晴天";
}
return "雨天";
}
// 订阅
public void subscribe(User user){
if(userList != null && !userList.contains(user) ){
userList.add(user);
}
}
public void start() throws Exception{
while(true){
String info = getInfo();
// 一个天气更新的事件
WeatherUpdateEvent weatherUpdateEvent = new WeatherUpdateEvent(info);
// 电视台发布事件
station.publish(weatherUpdateEvent);
// 更新信息
// station.onInfoUpdate(info);
Thread.sleep(300);
}
}
}
1.2 电视台的总线系统 ,事件发布器 , 监听消息同步消息到User中。
接收到新的天气信息的函数
java
package club.shengsheng.desigin.oberver.TVStation;
import club.shengsheng.desigin.oberver.Event;
import club.shengsheng.desigin.oberver.EventListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/// 电视台
/// 消息总线
public class TVStation {
// 发布订阅模型
List<EventListener> enventList = new ArrayList<EventListener>();
// 监听器依次遍历
private final Map<Class<? extends Event>,List<EventListener>> eventListenerMap = new HashMap<>();
// 电视台的订阅的逻辑 消息的监听
public void subscribe(EventListener listener,Class<? extends Event> eventClazz) {
eventListenerMap.computeIfAbsent(eventClazz, k -> new ArrayList<>()).add(listener);
}
// 发布逻辑实现
public void publish(Event event){
Class<? extends Event> aClass = event.getClass();
// 拿到监听器
List<EventListener> eventListeners = eventListenerMap.get(aClass);
if(eventListeners != null){
//发布给对应的监听器
enventList.forEach(listener -> listener.onEvent(event) );
}
for (EventListener eventListener : enventList) {
eventListener.onEvent(event);
}
}
}
1.3 天气更新事件,使用event 实现 事件的抽象,有具体的事件继承Event方法就行 ,进行事件的
更新
java
// 事件接口
public interface Event {
// 时间戳获取函数
long timestamp();
// 携带的内容
Object source();
}
1.4 实现消息的监听器 ,消息的消费者要实现这个监听器,监听消息的逻辑,并实现具体的拿到消息后的方法
java
// 监听消息的人
public interface EventListener {
// 消息监听
void onEvent(Event event);
}
Main函数中使用具体的方法
java
package club.shengsheng.desigin.oberver;
import club.shengsheng.desigin.oberver.TVStation.TVStation;
// 设计模式的本质是让程序更好理解,更好维护,更好扩展 而不去关注 具体用的是那个设计模式
public class Main {
// 观察者模式 -》 事件的生产者 直接去监听器
// 发布订阅 -> 事件的生产者 -》 总线 -》 监听器
//
public static void main(String[] args) {
// Tv是 时间的发布者
TVStation tvStation = new TVStation();
WeatherStation station = new WeatherStation(tvStation);
// 事件驱动的发布订阅模型
//
User tom = new User("tom",info->{
if( info.equals("晴天") ){
System.out.println("看日出");
}else{
System.out.println("看不了日出 , 呆在家里");
}
});
// 点击实现
User jerry = new User("jerry", info -> {
if (info.equals("阴天")) {
System.out.println("阴天 在家玩游戏");
}
});
/// 定义监听器 以及 关心的事件 只关心天气的更新事件
tvStation.subscribe(tom, WeatherUpdateEvent.class );
tvStation.subscribe(jerry,WeatherUpdateEvent.class);
try {
station.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
2. Spring中的观察者模式的使用
2.1 Spring中的Event 实现类继承 JDK中的实现类
java
package org.springframework.context;
import java.time.Clock;
import java.util.EventObject;
/**
* Class to be extended by all application events. Abstract as it
* doesn't make sense for generic events to be published directly.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see org.springframework.context.ApplicationListener
* @see org.springframework.context.event.EventListener
*/
public abstract class ApplicationEvent extends EventObject {
/** use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = 7099057708183571937L;
/** System time when the event happened. */
private final long timestamp;
/**
* Create a new {@code ApplicationEvent} with its {@link #getTimestamp() timestamp}
* set to {@link System#currentTimeMillis()}.
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
* @see #ApplicationEvent(Object, Clock)
*/
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
/**
* Create a new {@code ApplicationEvent} with its {@link #getTimestamp() timestamp}
* set to the value returned by {@link Clock#millis()} in the provided {@link Clock}.
* <p>This constructor is typically used in testing scenarios.
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
* @param clock a clock which will provide the timestamp
* @since 5.3.8
* @see #ApplicationEvent(Object)
*/
public ApplicationEvent(Object source, Clock clock) {
super(source);
this.timestamp = clock.millis();
}
/**
* Return the time in milliseconds when the event occurred.
* @see #ApplicationEvent(Object)
* @see #ApplicationEvent(Object, Clock)
*/
public final long getTimestamp() {
return this.timestamp;
}
}
2.2 Spring中的消息总线,事件发布器,ApplicationEventPublisher 只有两个功能: 1是发布 2是注册。
java
package org.springframework.context;
/**
* Interface that encapsulates event publication functionality.
*
* <p>Serves as a super-interface for {@link ApplicationContext}.
*
* @author Juergen Hoeller
* @author Stephane Nicoll
* @since 1.1.1
* @see ApplicationContext
* @see ApplicationEventPublisherAware
* @see org.springframework.context.ApplicationEvent
* @see org.springframework.context.event.ApplicationEventMulticaster
* @see org.springframework.context.event.EventPublicationInterceptor
* @see org.springframework.transaction.event.TransactionalApplicationListener
*/
@FunctionalInterface
public interface ApplicationEventPublisher {
/**
* Notify all <strong>matching</strong> listeners registered with this
* application of an application event. Events may be framework events
* (such as ContextRefreshedEvent) or application-specific events.
* <p>Such an event publication step is effectively a hand-off to the
* multicaster and does not imply synchronous/asynchronous execution
* or even immediate execution at all. Event listeners are encouraged
* to be as efficient as possible, individually using asynchronous
* execution for longer-running and potentially blocking operations.
* <p>For usage in a reactive call stack, include event publication
* as a simple hand-off:
* {@code Mono.fromRunnable(() -> eventPublisher.publishEvent(...))}.
* As with any asynchronous execution, thread-local data is not going
* to be available for reactive listener methods. All state which is
* necessary to process the event needs to be included in the event
* instance itself.
* <p>For the convenient inclusion of the current transaction context
* in a reactive hand-off, consider using
* {@link org.springframework.transaction.reactive.TransactionalEventPublisher#publishEvent(Function)}.
* For thread-bound transactions, this is not necessary since the
* state will be implicitly available through thread-local storage.
* @param event the event to publish
* @see #publishEvent(Object)
* @see ApplicationListener#supportsAsyncExecution()
* @see org.springframework.context.event.ContextRefreshedEvent
* @see org.springframework.context.event.ContextClosedEvent
*/
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
/**
* Notify all <strong>matching</strong> listeners registered with this
* application of an event.
* <p>If the specified {@code event} is not an {@link ApplicationEvent},
* it is wrapped in a {@link PayloadApplicationEvent}.
* <p>Such an event publication step is effectively a hand-off to the
* multicaster and does not imply synchronous/asynchronous execution
* or even immediate execution at all. Event listeners are encouraged
* to be as efficient as possible, individually using asynchronous
* execution for longer-running and potentially blocking operations.
* <p>For the convenient inclusion of the current transaction context
* in a reactive hand-off, consider using
* {@link org.springframework.transaction.reactive.TransactionalEventPublisher#publishEvent(Object)}.
* For thread-bound transactions, this is not necessary since the
* state will be implicitly available through thread-local storage.
* @param event the event to publish
* @since 4.2
* @see #publishEvent(ApplicationEvent)
* @see PayloadApplicationEvent
*/
void publishEvent(Object event);
}
2.3 ApplicationEventPublisher 发布的事件会发送给事件广器 ApplicationEventMulticaster,ApplicationEventMulticaster接口中的MulticastEventer方法实现真正的广播机制。广播我们的事件, ApplicationEventMulticaster实现的事件的广播机制实际上是为了让Spring的事件发布更具有托展性,Spring中的事件发布器默认是串行的,当ApplicationEventMulticaster实现事件的广播的功能后开发者可自定义事件的发布,也就支持了事件的异步发布。
java
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.event;
import java.util.function.Predicate;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
/**
* Interface to be implemented by objects that can manage a number of
* {@link ApplicationListener} objects and publish events to them.
*
* <p>An {@link org.springframework.context.ApplicationEventPublisher}, typically
* a Spring {@link org.springframework.context.ApplicationContext}, can use an
* {@code ApplicationEventMulticaster} as a delegate for actually publishing events.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Stephane Nicoll
* @see ApplicationListener
*/
public interface ApplicationEventMulticaster {
/**
* Add a listener to be notified of all events.
* @param listener the listener to add
* @see #removeApplicationListener(ApplicationListener)
* @see #removeApplicationListeners(Predicate)
*/
void addApplicationListener(ApplicationListener<?> listener);
/**
* Add a listener bean to be notified of all events.
* @param listenerBeanName the name of the listener bean to add
* @see #removeApplicationListenerBean(String)
* @see #removeApplicationListenerBeans(Predicate)
*/
void addApplicationListenerBean(String listenerBeanName);
/**
* Remove a listener from the notification list.
* @param listener the listener to remove
* @see #addApplicationListener(ApplicationListener)
* @see #removeApplicationListeners(Predicate)
*/
void removeApplicationListener(ApplicationListener<?> listener);
/**
* Remove a listener bean from the notification list.
* @param listenerBeanName the name of the listener bean to remove
* @see #addApplicationListenerBean(String)
* @see #removeApplicationListenerBeans(Predicate)
*/
void removeApplicationListenerBean(String listenerBeanName);
/**
* Remove all matching listeners from the set of registered
* {@code ApplicationListener} instances (which includes adapter classes
* such as {@link ApplicationListenerMethodAdapter}, for example, for annotated
* {@link EventListener} methods).
* <p>Note: This just applies to instance registrations, not to listeners
* registered by bean name.
* @param predicate the predicate to identify listener instances to remove,
* for example, checking {@link SmartApplicationListener#getListenerId()}
* @since 5.3.5
* @see #addApplicationListener(ApplicationListener)
* @see #removeApplicationListener(ApplicationListener)
*/
void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate);
/**
* Remove all matching listener beans from the set of registered
* listener bean names (referring to bean classes which in turn
* implement the {@link ApplicationListener} interface directly).
* <p>Note: This just applies to bean name registrations, not to
* programmatically registered {@code ApplicationListener} instances.
* @param predicate the predicate to identify listener bean names to remove
* @since 5.3.5
* @see #addApplicationListenerBean(String)
* @see #removeApplicationListenerBean(String)
*/
void removeApplicationListenerBeans(Predicate<String> predicate);
/**
* Remove all listeners registered with this multicaster.
* <p>After a remove call, the multicaster will perform no action
* on event notification until new listeners are registered.
* @see #removeApplicationListeners(Predicate)
*/
void removeAllListeners();
/**
* Multicast the given application event to appropriate listeners.
* <p>Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)}
* if possible as it provides better support for generics-based events.
* <p>If a matching {@code ApplicationListener} does not support asynchronous
* execution, it must be run within the calling thread of this multicast call.
* @param event the event to multicast
* @see ApplicationListener#supportsAsyncExecution()
*/
void multicastEvent(ApplicationEvent event);
/**
* Multicast the given application event to appropriate listeners.
* <p>If the {@code eventType} is {@code null}, a default type is built
* based on the {@code event} instance.
* <p>If a matching {@code ApplicationListener} does not support asynchronous
* execution, it must be run within the calling thread of this multicast call.
* @param event the event to multicast
* @param eventType the type of event (can be {@code null})
* @since 4.2
* @see ApplicationListener#supportsAsyncExecution()
*/
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}
2. 4 SpringBoot中实现事件的发布
java
// 定义一个事件的类 继承 spring的ApplicationEvent
public class RandomEvent extends ApplicationEvent {
public RandomEvent(Object source) {
super(source);
}
}
// 定义一个监听器
import org.example.controller.event.RandomEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
// 事件监听器
@Component
public class RandomEventListener {
// 处理一个事件
@EventListener
public void onRandomEvent(RandomEvent randomEvent) {
// 处理随机事件的内容
System.out.println(randomEvent.getSource());
System.out.println("Random event triggered");
}
}
// 初始化成功后实现事件的发布
@SpringBootApplication
public class Main {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Main.class, args);
context.publishEvent( new RandomEvent("login 初始化了"));
}
}
// 结果是SpringBoot启动后会打印"Random event triggered"
2.5 发布订阅模式的具体运用
2.5.1 实现一个注册的事件
java
package org.example.controller.listener;
import org.springframework.context.ApplicationEvent;
// 注册事件
public class RegisterEvent extends ApplicationEvent {
public RegisterEvent(Object user) {
super(user);
}
public String getUser() {
return getSource().toString();
}
}
2.5.2 在接口逻辑中当注册成功后发布这个注册事件
java
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.example.controller.event.RandomEvent;
import org.example.controller.listener.RegisterEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Random;
@RestController
@RequestMapping("/user")
public class LoginController {
@Resource
private ApplicationContext applicationContext;
@Resource // Spring 事件发布
private ApplicationEventPublisher publisher;
@Resource // Spring 事件广播
private ApplicationEventPublisher multicaster;
// 设计模式解耦
@GetMapping("/register")
public String register( @RequestParam("user") String user){
System.out.println(user +" is register");
//发布事件
publisher.publishEvent(new RegisterEvent(user));
System.out.println("打印日志");
return "success";
}
}
2.5.2 实现事件的监听,并执行具体的业务逻辑
java
import org.example.controller.listener.RegisterEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
@Service
public class MailService {
@EventListener
public void onRegister(RegisterEvent event) {
String user = event.getUser();
System.out.println("给 "+user+" 发了邮件 mail" );
}
}
java
import org.example.controller.listener.RegisterEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
// 礼包服务
@Service
public class GiftService {
@EventListener // 注册事件
public void onRegisterEvent(RegisterEvent event) {
String source = event.getUser();
System.out.println( "给 " + source + "发了新手礼包");
}
}
调用接口后实现的打印结果
