目录
前言
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
一、什么是观察者模式?
观察者模式又称为 发布-订阅模式,定义了对象之间一对多依赖关系,当目标对象(被观察者)的状态发生改变时,它的所有依赖者(观察者)都会收到通知。一个观察目标可以对应多个观察者,而这些观察者之间没有相互联系,所以能够根据需要增加和删除观察者,使得系统更易于扩展,符合开闭原则;并且观察者模式让目标对象和观察者松耦合,虽然彼此不清楚对方的细节,但依然可以交互,目标对象只知道一个具体的观察者列表,但并不认识任何一个具体的观察者,它只知道他们都有一个共同的接口。
电商领域的一个典型应用场景是,当用户注册成功以后,需要给用户发送短信,邮件 和优惠券信息。
二、Spring的事件机制
- ApplicationEvent:通过继承它,实现自定义事件。另外,通过它的 source 属性可以获取事件源,timestamp 属性可以获得发生时间。
- ApplicationEventPublisher:通过实现它,来发布变更事件。
- ApplicationEventListener:通过实现它,来监听指定类型事件并响应动作。
ApplicationEvent 源代码
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
private final long timestamp = System.currentTimeMillis();
public ApplicationEvent(Object source) {
super(source);
}
public final long getTimestamp() {
return this.timestamp;
}
}
ApplicationEventPublisherAware 源代码
package org.springframework.context;
import org.springframework.beans.factory.Aware;
public interface ApplicationEventPublisherAware extends Aware {
void setApplicationEventPublisher(ApplicationEventPublisher var1);
}
ApplicationListener源代码
package org.springframework.context;
import java.util.EventListener;
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E var1);
}
三、Spring的事件应用扩展
定义自己的 事件 类:Event
public interface Event extends DTO {
}
定义事件发布接口类:Publisher
/**
* 消息事件发布接口
*
* @author yangyanping
* @date 2022-12-22
*/
public interface Publisher<E extends Event> {
/**
* 发布同步事件
*/
void publishSync(E event);
/**
* 发布异步事件
*/
void publishAsync(E event);
}
这里就以上面的用户注册为例,来看看代码示例。首先定义用户注册事件 UserRegisterEvent。UserService 在完成自身的用户注册逻辑之后,仅仅只需要发布一个 UserRegisterEvent 事件,而无需关注其它拓展逻辑。其它 Service 可以自己订阅 UserRegisterEvent 事件,实现自定义的拓展逻辑。
public class UserRegisterEvent extends ApplicationEvent implements Event {
/**
* 用户名
*/
@Getter
private String username;
public UserRegisterEvent(Object source, String username) {
super(source);
this.username = username;
}
}
然后定义用户注册事件发布UserRegisterPublisher类,实现 ApplicationEventPublisherAware 接口,从而将 ApplicationEventPublisher 注入进来。从下面代码可以看到,在执行完注册逻辑后,调用了 ApplicationEventPublisher的 publishEvent(ApplicationEvent event) 方法,发布了 UserRegisterEvent 事件。
/**
* 用户注册事件发布
* @author yangyanping
* @date 2023-06-27
*/
@Component
public class UserRegisterPublisher implements Publisher<UserRegisterEvent>, ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
/**
* 同步发送事件
*/
@Override
public void publishSync(UserRegisterEvent event) {
applicationEventPublisher.publishEvent(event);
}
/**
* 异步发送事件
*/
@Async
@Override
public void publishAsync(UserRegisterEvent event) {
applicationEventPublisher.publishEvent(event);
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}
创建UserMailListener 监听器,实现 ApplicationListener 接口,通过 E 泛型设置感兴趣的事件,实现 onApplicationEvent(E event) 方法,针对监听的 UserRegisterEvent 事件,进行自定义处理。
/**
* 用户短信发送监听器
*
* @author yangyanping
* @date 2023-06-27
*/
@Slf4j
@Component
public class UserMailListener implements ApplicationListener<UserRegisterEvent> {
@Override
public void onApplicationEvent(UserRegisterEvent event) {
}
}
创建用户邮件UserEmailListener 监听器,不同于上面的实现 ApplicationListener 接口方式,在方法上,添加 @EventListener 注解,并设置监听的事件为 UserRegisterEvent。这是另一种使用方式。
/**
* 用户邮件发送监听器
*
* @author yangyanping
* @date 2023-06-27
*/
@Slf4j
@Component
public class UserEmailListener {
@EventListener
public void sendMail(UserRegisterEvent event) {
}
}
创建用户服务 UserService ,完成用户注册 和 用户注册事件发送 。
@Service
public class UserService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Resource
private UserRegisterPublisher publisher;
public void register(String username) {
// ... 执行注册逻辑
logger.info("[register][执行用户({}) 的注册逻辑]", username);
// ... 发布用户注册事件
publisher.publishSync(new UserRegisterEvent(this, username));
}
}
看到这里,细心的同学可能想到了发布订阅模式,其实观察者模式于发布订阅还是有区别的,简单来说,发布订阅模式属于广义上的观察者模式,在观察者模式的 Subject 和 Observer 的基础上,引入 Event Channel 这个中介,进一步解耦。观察者模式更加轻量,通常用于单机,而发布订阅模式相对而言更重一些,通常用于分布式环境下的消息通知场景。