文章目录
- 1、观察者模式
- 2、示例
- 3、spring中的观察者模式
- 3.1 spring观察者模式的使用
- 3.2 spring观察者模式原理解析
1、观察者模式
观察者模式(Observer Design Pattern),也叫做发布订阅模式(Publish-Subscribe Design Pattern)、模型-视图(Model-View)模式、源-监听器(Source-Listener)模式、从属者(Dependents)模式
观察者允许一个对象将其状态的改变通知其他对象。实际上主要的部分就是观察者和被观察者,比如前言提到的消息发布,就属于被观察者,而各种不同的平台消息提醒,则是一系列的观察者。
模型:
//观察者接口
public interface Observer {
/**声明响应方法,被观察者调用以达到通知的作用*/
void response();
}
//观察者实现类
public class ConcreteObserver1 implements Observer{
@Override
public void response() {
System.out.println("我是具体观察者ConcreteObserver1");
}
}
public class ConcreteObserver2 implements Observer{
@Override
public void response() {
System.out.println("我是具体观察者ConcreteObserver2");
}
}
//观察者抽象类
public abstract class Subject {
//存储注册的观察者
protected List<Observer> observerList = new ArrayList<Observer>();
/**
* 增加观察者
* @param observer 观察者
*/
public void add(Observer observer) {
observerList.add(observer);
}
/**
* 注销观察者,从观察者集合中删除一个观察者
* @param observer 观察者
*/
public void remove(Observer observer) {
observerList.remove(observer);
}
/**通知观察者*/
public abstract void notifyObserver();
}
//被观察者实现类
public class ConcreteSubject extends Subject {
@Override
public void notifyObserver() {
System.out.println("遍历观察者:");
for (Observer observer : observerList) {
observer.response();
}
}
}
//客户端测试类
public class Client {
public static void main(String[] args) {
Subject subject = new ConcreteSubject();
//项目中,这些内容一般在new Subject对象启动时自动加载
subject.add(new ConcreteObserver1());
subject.add(new ConcreteObserver2());
subject.notifyObserver();
}
}
观察者注册到被观察者中,然后通过被观察者调用观察者的方法达到通知的效果
实际中,会有多个观察者,以及多个事件,每个观察者关注不同的事件,待相对应事件发生时,"通知"关注改时间的观察者
2、示例
宝马公司偶尔推出打折或立减活动(8折、9折、立减5000),不同的用户关注不同的活动,如用户1关注8折和9折活动,用户2关注8折和立减5000活动,用户3这些活动都关注,当宝马公司推出一种活动的时候,立刻通知到关注该活动的用户
//活动(事件)
@Getter
public enum EventEnum {
eightDisc(1, "8折"),
nineDisc(2, "9折"),
subFiveThous(3, "立减5000元");
private Integer code;
private String value;
public static EventEnum getEventEnum(Integer code){
for(EventEnum event : values()){
if(event.code.equals(code)){
return event;
}
}
return null;
}
public static EventEnum getEventEnum(String value){
for(EventEnum event : values()){
if(event.getValue().equals(value)){
return event;
}
}
return null;
}
public String getValue(Integer code){
for(EventEnum event : values()){
if(event.code.equals(code)){
return event.getValue();
}
}
return null;
}
public Integer getCode(String value){
for(EventEnum event : values()){
if(event.getValue().equals(value)){
return event.getCode();
}
}
return null;
}
EventEnum(Integer code, String value) {
this.code = code;
this.value = value;
}
public void setCode(Integer code) {this.code = code;}
public void setValue(String value) {this.value = value;}
}
//客户(观察者)
public interface Consumer {
void response(EventEnum event);
}
public class ConcreteConsumer1 implements Consumer{
@Override
public void response(EventEnum event) {
System.out.println("ConcreteConsumer1 已知悉 宝马公司" + event.getValue() +" 活动");
}
}
public class ConcreteConsumer2 implements Consumer{
@Override
public void response(EventEnum event) {
System.out.println("ConcreteConsumer2 已知悉 宝马公司" + event.getValue() +" 活动");
}
}
public class ConcreteConsumer3 implements Consumer {
@Override
public void response(EventEnum event) {
System.out.println("ConcreteConsumer3 已知悉 宝马公司" + event.getValue() +" 活动");
}
}
//宝马公司(被观察者)
public abstract class BMWCompany {
Map<EventEnum, List<Consumer>> eventMap = new HashMap<>();
//客户注册关注事件
public BMWCompany(){
Consumer consumer1 = new ConcreteConsumer1();
Consumer consumer2 = new ConcreteConsumer2();
Consumer consumer3 = new ConcreteConsumer3();
registerConsumerEvent(consumer1, EventEnum.getEventEnum(1));
registerConsumerEvent(consumer1, EventEnum.getEventEnum(2));
registerConsumerEvent(consumer2, EventEnum.getEventEnum(2));
registerConsumerEvent(consumer2, EventEnum.getEventEnum(3));
registerConsumerEvent(consumer3, EventEnum.getEventEnum(1));
registerConsumerEvent(consumer3, EventEnum.getEventEnum(2));
registerConsumerEvent(consumer3, EventEnum.getEventEnum(3));
}
public void registerConsumerEvent(Consumer consumer,EventEnum event){
List<Consumer> consumers = eventMap.getOrDefault(event, new ArrayList<>());
consumers.add(consumer);
eventMap.put(event, consumers);
}
public void removeConsumer(Consumer consumer){
for(List<Consumer> consumerList : eventMap.values()){
consumerList.remove(consumer);
}
}
public abstract void notifyConsumer(EventEnum event);
}
public class DiscountManage extends BMWCompany{
@Override
public void notifyConsumer(EventEnum event) {
List<Consumer> consumers = eventMap.get(event);
if(!CollectionUtils.isEmpty(consumers)){
consumers.forEach(consumer -> consumer.response(event));
}else{
System.out.println("无人关注 " + event.getValue() + " 活动");
}
}
}
//客户端测试类
public class Client {
public static void main(String[] args) {
BMWCompany discountManage = new DiscountManage();
discountManage.notifyConsumer(EventEnum.getEventEnum(3));
}
}
该示例中,生成被观察者对象时,构造函数将观察者与其关注的事件放入到map中,key为事件,value为关注该事件的用户;
也就是说由被观察者管理事件和观察者之间的关系:观察者面向被观察者,由被观察者管理;被观察者亲自通知观察者
以上就是观察者模式与发布订阅模式,发布订阅模式有专门的组件管理事件和观察者之间的关系:被观察者发布事件到事件组件,无需关心谁订阅了哪些事件;观察者面向事件组件订阅事件,不关心谁发布的事件
3、spring中的观察者模式
以上示例中我们需要在创建被观察者实例时,自己写代码创建观察者和事件之间的关系,当新增事件和观察者的时候,要在BMWCompany()
构造函数中再新增语句;那么可不可以只提供事件、观察者以及观察者所关注的事件,组装的事情由spring自动完成?
3.1 spring观察者模式的使用
示例:平台用户注册成功时,为用户发送邮件并发放优惠券;平台用户销毁账户时,发送邮件和消息
一般模型:
//邮件业务接口和实现类
public interface EmailService {
void onRegister(String name);
void onDestory(String name);
}
public class EmailServiceImpl implements EmailService {
@Override
public void onRegister(String name) {
log.info("邮件: 尊敬的 " + name + " 先生/女士,恭喜注册成功");
}
@Override
public void onDestory(String name) {
log.info("邮件: 尊敬的 " + name + " 先生/女士,很遗憾,您销毁账号");
}
}
//优惠券业务接口和实现类
public interface CouponService {
void onRegister(String name);
}
@Slf4j
@Service
public class CouponServiceImpl implements CouponService {
@Override
public void onRegister(String name) {
log.info("优惠券: 尊敬的 " + name + " 先生/女士,恭喜注册成功,赠送您100元代金券");
}
}
//消息业务接口和实现类
public interface MessageService {
void onDestory(String name);
}
@Slf4j
@Service
public class MessageServiceImpl implements MessageService {
@Override
public void onDestory(String name) {
log.info("信息: 尊敬的 " + name + " 先生/女士,很遗憾,您销毁账号");
}
}
//用户业务接口和实现类
public interface UserService {
void register(String name);
void destroy(String name);
}
@Slf4j
@Service
public class UserServiceImpl implements UserService {
@Autowired
EmailService emailService;
@Autowired
MessageService messageService;
@Autowired
CouponService couponService;
@Override
public void register(String name) {
//执行各种校验动作,入库操作
//doRegister(name);
log.info(name + " 的注册逻辑......");
//发送邮件
emailService.onRegister(name);
//发送优惠券
couponService.onRegister(name);
}
@Override
public void destroy(String name) {
//执行销毁账号操作
//doDestory(name);
log.info(name + " 的销毁逻辑......");
//发送邮件
emailService.onDestory(name);
//发送短信
messageService.onDestory(name);
}
}
如上所示,如果订阅者很多,那么在用户业务实现类中要添加所有的相关订阅者引用,并且在方法中通知所有对应的订阅者
spring中发布订阅模式:
//注册事件
public class UserRegisterEvent extends ApplicationEvent {
private String username;
public UserRegisterEvent(Object source) {
super(source);
}
public UserRegisterEvent(Object source, String username) {
super(source);
this.username = username;
}
public String getUsername() {
return username;
}
}
//销毁事件
public class UserDestoryEvent extends ApplicationEvent {
private String username;
public UserDestoryEvent(Object source) {
super(source);
}
public UserDestoryEvent(Object source, String username) {
super(source);
this.username = username;
}
public String getUsername() {
return username;
}
}
//邮件业务实现类
@Slf4j
@Service
public class EmailServiceImpl implements EmailService {
//实现监听者(订阅者)的一种方式---方法上添加@EventListener注解,可实现一个类中订阅多个事件
@EventListener
public void onRegister(UserRegisterEvent userRegisterEvent) {
log.info("邮件: 尊敬的 " + userRegisterEvent.getUsername() + " 先生/女士,恭喜注册成功");
}
@EventListener
public void onDestory(UserRegisterEvent userRegisterEvent) {
log.info("邮件: 尊敬的 " + userRegisterEvent.getUsername() + " 先生/女士,很遗憾,您销毁账号");
}
}
//优惠券业务实现类
@Slf4j
@Service
public class CouponServiceImpl implements CouponService, ApplicationListener<UserRegisterEvent> {
//第二种实现监听者(订阅者)的方式,实现ApplicationListener接口的onApplicationEvent方法
@Override
public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
log.info("优惠券: 尊敬的 " + userRegisterEvent.getUsername() + " 先生/女士,恭喜注册成功,赠送您100元代金券");
}
}
//消息业务实现类
@Slf4j
@Service
public class MessageServiceImpl implements MessageService, ApplicationListener<UserDestoryEvent> {
@Override
public void onApplicationEvent(UserDestoryEvent userDestoryEvent) {
log.info("信息: 尊敬的 " + userDestoryEvent.getUsername() + " 先生/女士,很遗憾,您销毁账号");
}
}
//用户业务实现类
@Slf4j
@Service
public class UserServiceImpl implements UserService, ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(@NotNull ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
@Override
public void register(String name) {
//执行各种校验动作,入库操作
//doRegister(name);
log.info(name + " 的注册逻辑......");
//发布注册事件
applicationEventPublisher.publishEvent(new UserRegisterEvent(this, name));
}
@Override
public void destroy(String name) {
//执行销毁账号操作
//doDestory(name);
log.info(name + " 的销毁逻辑......");
//发布销毁事件
applicationEventPublisher.publishEvent(new UserDestoryEvent(this, name));
}
}
由上所示,我们只需要编写事件、订阅者逻辑即可,具体的订阅者和事件之间的关系有spring来建立关联
每当事件发生时,spring获取订阅该事件的类去执行相对应的处理方法
3.2 spring观察者模式原理解析
当发布者执行applicationEventPublisher.publishEvent(new UserRegisterEvent(this, name));
时,spring会一直执行到SimpleApplicationEventMulticaster.multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType)
方法
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Executor executor = this.getTaskExecutor();
Iterator var5 = this.getApplicationListeners(event, type).iterator();
while(var5.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var5.next();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
this.invokeListener(listener, event);
}
}
}
由上可知,
spring
也是通过事件获取所有关注该事件的监听器,依次执行订阅处理逻辑
具体spring
执行逻辑参考 Spring观察监听器-ApplicationEventPublisher的publishEvent实现异步事件解耦业务