观察者模式在实际开发过程中是非常常见的一种设计模式。

Spring Event的原理就是观察者模式,只不过有Spring的加持,让我们更加方便的使用这一设计模式。

一、什么是观察者模式

概念: 观察者模式又叫发布-订阅模式。

发布指的是当目标对象的状态改变时,它就向它所有的观察者对象发布状态更改的消息,以让这些观察者对象知晓。

举例:

网上有一个非常符合观察者模式的例子

当温度有变化,对应的仪表盘也会跟着变化。

一个仪表盘可以当作一个观察者,去掉一个仪表盘或者新增一个仪表盘跟目标对象(温度)是解耦的,不是强绑定关系。

一句话:感知变化,相应变化

二、观察者模式 VS 责任链模式

这两种设计模式是有相似的地方,但其实有很大的区别。

我们先来看相似的点,就好比上面的这个例子,我们是不是也可以用责任链模式来实现?

当然可以了。

当温度变化了,一条一条链路的执行下去就是了。

当然如果是我,这个功能在选择设计模式的时候,我还是会选择使用观察者模式。

1、区别

我个人认为主要有四点区别:

第一点:我们也会称观察者模式为发布订阅模式,作为订阅者来讲,每个订阅者是平级的,也就是每个观察者对象是平级的,但责任链可以有先后次序。

比如我们在电商场景中,有个电商活动,这个商品需要先走 包邮活动->满减送->会员折扣活动->积分抵扣活动。

这个责任链的顺序不同会导致最终优惠的价格不同。

第二点:所有观察者一般接收统一参数,但责任链获取的参数可能是上一个链路已经处理完成的

就好比上面的电商活动,会员折扣活动计算后的价格,还会传入到积分抵扣活动中。

第三点:观察者的对象都会执行,但责任链这我们可以在得到满意结果直接返回。

比如我想查一个份数据,这个数据可以先从A -> B -> C,三个接口获得。只要返回数据,这个链路就不用往下走了。

第四点:观察者模式可以做异步操作,我们说的MQ发布订阅模式,就是完全异步,但是责任链不太适合走异步。

三、代码示例

1、观察者模式有哪些角色

抽象被观察者: 定义了一个接口,包含了注册观察者、删除观察者、通知观察者等方法。

具体被观察者: 实现了抽象被观察者接口,维护了一个观察者列表,并在状态发生改变时通知所有注册的观察者。

抽象观察者: 定义了一个接口,包含了更新状态的方法。

具体观察者: 实现了抽象观察者接口,在被观察者状态发生改变时进行相应的处理。

  1. 抽象被观察者
/**
* 抽象被观察者
*/
public interface ISubject { /**
* 新增观察者
*/
boolean attach(IObserver observer); /**
* 删除观察者
*/
boolean detach(IObserver observer); /**
* 通知观察者
*/
void notify(String event);
}
  1. 抽象观察者
/**
* 抽象观察者
*/
public interface IObserver { /**
* 观察者所执行方法
*/
void update(String event);
}
  1. 具体被观察者
/**
* 具体被观察者
*/
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);
}
}
}
  1. 具体观察者
/**
* 具体观察者
*/
public class ConcreteObserver implements IObserver { @Override
public void update(String event) {
System.out.println("观察者: 收到被观察者的温度变动: " + event);
}
}
  1. 测试
/**
* 测试
*/
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));
} }
  1. 实现 ApplicationEventPublisherAware 接口,从而将 ApplicationEventPublisher 注入到其中。

  2. 在执行完注册逻辑后,调用 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());
}
}
  1. 实现 ApplicationListener 接口,通过 E 泛型设置感兴趣的事件。

  2. 实现 onApplicationEvent方法,针对监听的 UserRegisterEvent 事件,进行自定义处理。

  3. 设置 @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

声明: 公众号如需转载该篇文章,发表文章的头部一定要 告知是转至公众号: 后端元宇宙。同时也可以问本人要markdown原稿和原图片。其它情况一律禁止转载!

Spring Event 观察者模式, 业务解耦神器的更多相关文章

  1. EventBus VS Spring Event

    EventBus VS Spring Event 本地异步处理,采用事件机制 可以使 代码解耦,更易读.事件机制实现模式是 观察者模式(或发布订阅模式),主要分为三部分:发布者.监听者.事件. Gua ...

  2. spring event

    昨天看到了一遍关于spring event的帖子,觉得很好,就照着敲了一份代码,感觉对spring event有了进一步的认识.帖子链接:https://segmentfault.com/a/1190 ...

  3. 微服务架构-利用Redis特性进行业务解耦

    背景:     接着上篇文章来,上篇文章讲的是如何利用ApplicationContext的事件机制来达到业务解耦,而且这只能作用在单体应用中.在当下这么盛行的微服务架构中,想要再利用此方案做业务解耦 ...

  4. Spring Event事件驱动

    Spring事件驱动模型,简单来说类似于Message-Queue消息队列中的Pub/Sub发布/订阅模式,也类似于Java设计模式中的观察者模式. 自定义事件 Spring的事件接口位于org.sp ...

  5. Spring.Net封闭业务类为WebService注意点和问题

    最近遇到的一个项目中用到了标题所说的方法,用Spring.Net将业务类封闭成WebService供其它地方调用使用,感觉还是蛮新鲜的,于是在园子中搜了一篇园友写的文章(这里)自己也尝试着搭了一个环境 ...

  6. 使用Spring AOP实现业务依赖解耦

    Spring IOC用于解决对象依赖之间的解耦,而Spring AOP则用于解决业务依赖之间的解耦: 统一在一个地方定义[通用功能],通过声明的方式定义这些通用的功能以何种[方式][织入]到某些[特定 ...

  7. 基于spring的观察者模式

    简单的说,观察者模式,就类似于 广播站发送广播,和收音机的关系.多个收音机去收听同一个广播频道. 在实际的业务场景中,可以是这样的.创建订单成功后,发布事件.然后减库存.发送短信.调用微信.调用物流服 ...

  8. 自定义Spring event

    通过Spring自定义event 首先我们定义我们的event类 package com.hyenas.spring.custom.event; import org.springframework. ...

  9. Spring 定时操作业务需求

    1.定时分析 在业务需求中有的需要检测用户的状态,通过对用户状态的检测做出对此状态相应的操作,如果这种检测由运营人工检测,不仅工作量大,而且准确性不高,人工无法很好的完成工作: 问题根源:在检测用户状 ...

  10. spring注解实现业务层事务管理,当业务层自调用时,事务失效问题解决

    前些日子一朋友在需要在目标对象中进行自我调用,且需要实施相应的事务定义,且网上的一种通过BeanPostProcessor的解决方案是存在问题的.因此专门写此篇帖子分析why. 1.预备知识 aop概 ...

随机推荐

  1. WPF入门教程系列二十七 ——DataGrid使用示例MVVM模式(4)

    WPF入门教程系列目录 WPF入门教程系列二--Application介绍 WPF入门教程系列三--Application介绍(续) WPF入门教程系列四--Dispatcher介绍 WPF入门教程系 ...

  2. Linux 调用约定

      函数调用约定是对函数调用时如何传递参数的一种约定.关于它的约定有许多种,下面我们分 别从内核接口和用户接口两方面介绍32位和64位Linux的调用约定. 一.内核接口   x86-32系统调用约定 ...

  3. 使用numpy计算分子内坐标

    技术背景 当我们打开一个用于表示分子构象的xyz文件或者pdb文件,很容易可以理解这种基于笛卡尔坐标的空间表征方法.但是除了笛卡尔坐标表示方法之外,其实也有很多其他的方法用于粗粒化或者其他目的的表征方 ...

  4. Centos7安装配置Hive

    Centos7安装配置 一 . 安装 安装就不多做详述,选择好自己的镜像设置好路径即可 二 .配置 2.1 网络配置 桌面右键进入 cmd 命令编辑窗口,在 Linux 中设置网络的相关配置都需要管理 ...

  5. spingmvc配置AOP 之 非注解方式

    spingmvc配置AOP有两种方式,一种是利用注解的方式配置,另一种是XML配置实现. 应用注解的方式配置: 先在maven中引入AOP用到的依赖 <dependency> <gr ...

  6. 行行AI人才直播第8期:新加坡国立大学在读博士生张傲《多模态大语言模型(MLLM)的简介及高效训练》

    随着 ChatGPT 在各领域展现出非凡能力,多模态大型语言模型(MLLM)近来也成为了研究的热点,它利用强大的大型语言模型(LLM)作为"大脑",可以执行各种多模态任务.更让人感 ...

  7. 从钢铁行业数字化管控与超自动化融合,看华为WeAutomate能力进化

    文/王吉伟 钢铁行业的数字化转型,历来都是值得探讨的热点话题. 2022年,我国粗钢产量10.13亿吨,占据了全球钢铁供给市场的半壁江山. 这组数据可谓非常抢眼,但仍旧难掩诸多企业的各种经营问题. 钢 ...

  8. Djiango 创建迁移项报错query = query.decode(errors='replace') AttributeError: 'str' object has no attribute 'decode'

    在终端命令行创建迁移项 输入(python manage.py makemigrations) 或(python manage.py migrate)时出现报错信息:Traceback (most r ...

  9. sql中当关联查询主表很大影响查询速度时怎么办?

    sql中当关联查询主表很大时,直接关联,查询速度会较慢,这时可以先利用子查询经筛选条件筛除一部数据,这样主连接表体量减少,这样能一定程度加快速度. (1)常规join -- 最慢7.558s sele ...

  10. 基于C#的无边框窗体动画效果的完美解决方案 - 开源研究系列文章

    最近在整理和编写基于C#的WinForm应用程序,然后碰到一个其他读者也可能碰到的问题,就是C#的Borderless无边框窗体的动画效果问题. 在Visual Studio 2022里,C#的Win ...