java和Spring中观察者模式的应用详解

目录
  • 一、观察者模式基本概况
    • 1.概念
    • 2.作用
    • 3.实现方式
  • 二、java实现两种观察者模式
    • 1.Observer接口和Observable类
    • 2.EventObject和EventListener
  • 三、Spring事件监听实战及原理
    • 1.Spring如何使用EventObject和EventListener实现观察者?
    • 2.先实战—要先会用
    • 3.会原理—搞清楚为什么会这样
  • 四、最后一张图总结

一、观察者模式基本概况

1.概念

观察者模式(Observer Design Pattern)也被称为发布订阅模式(Publish-Subcribe Design Pattern)。定义如下

Define a one-to-many dependency between objects so that when one object changes state,all its dependents are notified and update automatically。

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有观察者对象,使它们能够自动更新自己。

2.作用

参考设计模式之美的一段总结

回到本质,设计模式要干的事情就是解耦。创建型模式是将创建对象和使用对象解耦,结构型模式是将不同功能代码解耦,行为型模式是将不同的行为代码解耦,具体到观察者模式,是将观察者和被观察者代码解耦。

3.实现方式

观察者模式在不同的场景有不同的实现方式,自然也有不同的名字。如Publisher-Subscriber、Producer-Consumer、Dispatcher-Listener,等等。

有哪些不同实现方式呢?同步阻塞方式异步非阻塞方式进程内实现方式跨进程实现方式

同步阻塞方式是经典的实现方式,是说在主题通知观察者和观察者执行自己的逻辑是由同一个线程完成的,比如下面要说的java中Observer接口和Observable类,后有UML图

public void notifyObservers(Object arg) {
    for (int i = arrLocal.length-1; i>=0; i--){
    	//使用了for循环同步通知观察者
    	((Observer)arrLocal[i]).update(this, arg);
    }
}

异步非阻塞方式是在通知观察者和观察者执行自己逻辑不是同一个线程,如下代码

public void notifyObservers(Object arg) {
    for (int i = arrLocal.length-1; i>=0; i--){
    	//使用了开启新线程方式异步通知观察者
    	new Thread(()->{
    		((Observer)arrLocal[i]).update(this, arg);
		}).start();
    }
}

上面都是在同一个进程中,还有跨进程的实现方式就是常用的MQ,消息队列

二、java实现两种观察者模式

1.Observer接口和Observable类

下面我们使用JDK提供的接口和类实现项目中使用观察者模式

先说下Observer接口和Observable类

Observable类,被观察者。有三个组成部分,用Vector管理注册的观察者(Observer),并提供了增删、统计方法。一旦状态(changed)改变就通知(notifyObservers)观察者。notifyObservers方法会调用观察者(Observer)的update方法。

Observer接口就是一个观察者的标准,实现该接口并重写update方法就可以实现一个观察者。

具体的源码大家可以自己看下,比较简单,在java.util包下。

实现一个需求

当自定义被观察者data值=1时通知各个观察者执行update方法,应该如何做?

UML图已经画出来了,剩下就是写代码了。

代码如下

//自定义被观察者
public class MyObservable extends Observable {
    private int data;
    public void setData(int data) {
        this.data = data;
    }
    public void notifyObserver(){
    	//当data等于1时通知观察者
        if (data==1){
            this.setChanged();
            super.notifyObservers();
        }
    }
}
//自定义观察者
public class MyObserver implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("自定义观察者执行了.....");
    }
}
//测试类
public class PubSubTest {
    public static void main(String[] args) {
        MyObservable observable = new MyObservable();
        observable.addObserver(new MyObserver());
        observable.setData(1);
        observable.notifyObserver();
    }
}

2.EventObject和EventListener

JDK提供了EventObject类和EventListener接口定义了实现观察者模式的第二个标准,只是定义了标准没有提供默认实现,但是其他框架如Spring实现该方式,这个下面再说。

先看EventObject类

该类实现的功能就是定义一个事件,其中有一个最重要的属性source,叫事件源。含义是哪个类发出的事件。自定义事件时需要继承该类

public class EventObject implements java.io.Serializable {
    protected transient Object  source;
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");
        this.source = source;
    }
    public Object getSource() {
        return source;
    }
}

EventListener接口是一个空接口,自定义监听器需要继承该接口。

注意此时事件就是一个被观察者,监视器就是观察者。

三、Spring事件监听实战及原理

1.Spring如何使用EventObject和EventListener实现观察者?

在Spring中为自定义事件和自定义监听者,分别提供一个类和一个接口。

ApplicationEvent类继承了EventObject,用于在Spring环境下自定义事件

public abstract class ApplicationEvent extends EventObject {
	/**
	 * Create a new {@code ApplicationEvent}.
	 * @param source the object on which the event initially occurred or with
	 * which the event is associated (never {@code null})
	 */
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}
}

ApplicationListener接口继承JDK的EventListener,用于在Spring环境下自定义监听者

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	/**
	 * Handle an application event.
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);
}

onApplicationEvent方法就是根据传过来的事件参数,监听是否是自己感兴趣的事件,然后执行方法体。

介绍完类和接口剩下的就是如何在Spring中如何自定义事件、如何发布事件、如何使用自定义监听器监听事件,下面举个例子

2.先实战—要先会用

实现一个需求:当调用一个类的方法完成时,该类发布事件,事件监听器监听该类的事件并执行的自己的方法逻辑

假设这个类是Request、发布的事件是ReuqestEvent、事件监听者是ReuqestListener。当调用Request的doRequest方法时,发布事件。模拟的代码如下

public class SpringEventTest {
    public static void main(String[] args) {
        ApplicationContext context=new AnnotationConfigApplicationContext("com.thinkcoder.parttern.behavioral.pubsub.spring");
        Request request = (Request) context.getBean("request");
        //调用方法,发布事件
        request.doRequest();
    }
}
//定义事件
class RequestEvent extends ApplicationEvent {
    public RequestEvent(Request source) {
        super(source);
    }
}
//发布事件
@Component
class Request{
    @Autowired
    private ApplicationContext applicationContext;
    public void doRequest(){
        System.out.println("调用Request类的doRequest方法发送一个请求");
        applicationContext.publishEvent(new RequestEvent(this));
    }
}
//监听事件
@Component
class RequestListener implements ApplicationListener<RequestEvent> {
    @Override
    public void onApplicationEvent(RequestEvent event) {
        System.out.println("监听到RequestEvent事件,执行方法");
    }
}
//打印的日志
调用Request类的doRequest方法发送一个请求
监听到RequestEvent事件,执行方法

上面我们依靠spring实现了事件—监听机制,使用的步骤有如下几步

  • 定义事件:继承ApplicationEvent类,实现方法传入事件源。由事件源产生事件
  • 发布事件:使用Spring的IOC容器ApplicationContext的publishEvent方法发布事件
  • 监听事件:实现ApplictionListener接口重写方法即可实现自定义监听器

3.会原理—搞清楚为什么会这样

先将上述过程画成一幅图,然后展开来解释各个步骤,相信我你能看懂

通过上面的流程图,回答下面几个问题

1.监听器什么时候注册到IOC容器中?

注册的开始逻辑是在AbstractApplicationContext类的refresh方法,该方法包含了整个IOC容器初始化所有方法。其中有一个registerListeners()方法就是注册系统监听者(spring自带的)和自定义监听器的。

看registerListeners的关键方法体,其中的两个方法addApplicationListeneraddApplicationListenerBean,从方法可以看出是添加监听者。

for (ApplicationListener<?> listener : getApplicationListeners()) {
	getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
	getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}

那么最后将监听者放到哪里了呢?就是ApplicationEventMulticaster接口的子类

该接口主要两个职责,维护ApplicationListener相关类和发布事件。

实现是在默认实现类AbstractApplicationEventMulticaster,最后将Listener放到了内部类ListenerRetriever两个set集合中

private class ListenerRetriever {
	public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
	public final Set<String> applicationListenerBeans = new LinkedHashSet<>();

ListenerRetriever又被称为监听器注册表。

2.Spring如何发布的事件并通知的监听者?

该问题开始是在ApplicationContext.publishEvent的方法,该方法调用路线

AbstractApplicationContext.publishEventSimpleApplicaitonEventMulticaster.multicastEventSimpleApplicaitonEventMulticaster.invokeListener→SimpleApplicaitonEventMulticaster.doInvokeListener→调用系统及自定义listener的onApplicationEvent方法,这个就是发布事件并通知的调用路线。

这个注意的有两个方法

multicastEvent方法,该方法有两种方式调用invokeListener,通过线程池和直接调用,进一步说就是通过异步和同步两种方式调用

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
	Executor executor = getTaskExecutor();
	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
		if (executor != null) {
			executor.execute(() -> invokeListener(listener, event));
		}
		else {
			invokeListener(listener, event);
		}
	}
}

最后看doInvokeListener方法

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
	try {
		//直接调用了listener接口的onApplicationEvent方法
		listener.onApplicationEvent(event);
	}
}

四、最后一张图总结

上图包含了在Spring如何自定义事件、监听器以及发布事件和通知监听者的原理,大家可以自己梳理下。

时间: 2021-10-12

java设计模式之观察者模式学习

1.什么是观察者模式 简单情形:有A.B.C.D等四个独立的对象,其中B.C.D这三个对象想在A对象发生改变的第一时间知道这种改变,以便做出相应的响应或者对策. 上面的这种情形,就是观察者模式. 当然可以有多个观察者,多个被观察者. 观察者与被观察者也不是对立的,一个对象可以观察其他对象,也可以被其他对象观察. 2.观察者模式的应用 为了更好的理解什么是观察者模式,下面我举一些可能用到该模式的情形或例子: (1)周期性任务.比如linux中的周期性任务命令crontab命令,win7下的定时关机

java设计模式之观察者模式

观察者模式又称发布-订阅(Publish/Subscribe)模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己.将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性.我们不希望为了维持一致性而使各类紧密耦合,这样会给维护.扩展和复用都带来不便.观察者模式所做的工作其实就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体. 观察者模式是实际中应用比较广泛的模

Java设计模式之观察者模式(Observer模式)

一.观察者模式是什么? 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern).当一个对象被修改时,则会自动通知依赖它的对象.观察者模式属于行为型模式. 人话: 就像微信公众号推送消息,订阅的人能收到,没订阅的收不到,还可以取消/添加订阅 二.模式分析 2.1 四个角色 抽象主题(抽象被观察者角色):也就是一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者.抽象主题提供一个接口,可以增加和删除观察者角色.一般用一个抽象类和接口来实现

Java设计模式之java观察者模式详解

目录 引言 介绍 角色 原理类图 微信订阅号的案例 总结 优点 缺点 适用场景 观察者模式的典型应用 JDK 提供的观察者接口 Guava EventBus 中的观察者模式 Spring ApplicationContext 事件机制中的观察者模式 参考文章 总结 引言 观察者模式是设计模式中的 "超级模式",其应用随处可见,我们以微信公众号为例. 微信公众号有服务号.订阅号和企业号之分.当我们在公众号上发布一篇博文推送时,订阅的用户都能够在我发布推送之后及时接收到推送,即可方便地在手

java 在观察者模式中使用泛型T的实例

被观察者 public class Observable<T> { List<Observer> observers = new ArrayList<Observer>(); boolean changed = false; /** * Adds the specified observer to the list of observers. If it is already * registered, it is not added a second time. *

Java通俗易懂系列设计模式之观察者模式

介绍 观察者模式是行为设计模式之一.当您对对象的状态感兴趣并希望在有任何更改时收到通知时,观察者设计模式非常有用.在观察者模式中,监视另一个对象状态的对象称为Observer,正在被监视的对象称为Subject. 根据GoF,观察者设计模式的意图是; 定义对象之间的一对多依赖关系,以便当一个对象更改状态时,将自动通知和更新其所有依赖项. Subject包含一个观察者列表,用于通知其状态的任何变化,因此它应该提供观察者可以注册和注销自己的方法.Subject还包含一种方法,用于通知所有观察者任何更

Java经典设计模式之观察者模式原理与用法详解

本文实例讲述了Java经典设计模式之观察者模式.分享给大家供大家参考,具体如下: 观察者模式:对象间的一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象(被观察). 以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并发生相应的变化. 观察者模式有很多实现方式:该模式必须包含观察者和被观察对象两种角色.观察者和被观察者之间存在"观察"的逻辑关系,当被观察者发生改变的时候,观察者就会观察到这样的变化,发出相应的改变. /** * 观察者接口:观察者,需要用到观察者模式的

Java设计模式之观察者模式原理与用法详解

本文实例讲述了Java设计模式之观察者模式原理与用法.分享给大家供大家参考,具体如下: 什么是观察者模式 可以这么理解: 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象. 这个主题对象在状态上发生变化时,会通知所有观察者对象,让它们能够自动更新自己. 也可以这样理解: 观察者模式是关于多个对象想知道一个对象中数据变化情况的一种成熟模式.观察者模式中有一个称作"主题"的对象和若干个称作"观察者"的对象,"主题"和&qu

Java中的设计模式与7大原则归纳整理

Java中的设计模式与7大原则: 一.创建型模式 1.抽象工厂模式(Abstract factory pattern): 提供一个接口, 用于创建相关或依赖对象的家族, 而不需要指定具体类. 2.生成器模式(Builder pattern): 使用生成器模式封装一个产品的构造过程, 并允许按步骤构造. 将一个复杂对象的构建与它的表示分离, 使得同样的构建过程可以创建不同的表示. 3.工厂模式(factory method pattern): 定义了一个创建对象的接口, 但由子类决定要实例化的类是

Android编程设计模式之观察者模式实例详解

本文实例讲述了Android编程设计模式之观察者模式.分享给大家供大家参考,具体如下: 一.介绍 观察者模式是一个使用率非常高的模式,它最常用的地方是GUI系统.订阅--发布系统.因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖.以GUI系统来说,应用的UI具有易变性,尤其是前期随着业务的改变或者产品的需求修改,应用界面也会经常性变化,但是业务逻辑基本变化不大,此时,GUI系统需要一套机制来应对这种情况,使得UI层与具体的业务逻辑解耦,观察者

JS设计模式之观察者模式实现实时改变页面中金额数的方法

本文实例讲述了JS设计模式之观察者模式实现实时改变页面中金额数的方法.分享给大家供大家参考,具体如下: 观察者设计模式概念: 有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者(每个处的主账号金额函数)对象同时监听某一个主题对象(修改子账号金额后调用的deliver的对象Publisher).这个主题对象在状态(调用deliver方法)发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 在一个会员管理系统中,主账号给子账号充值金额的功能. 场景:主账号有1000

Python设计模式之观察者模式原理与用法详解

本文实例讲述了Python设计模式之观察者模式原理与用法.分享给大家供大家参考,具体如下: 观察者模式(发布-订阅模式 Publish Subscribe Pattern):定义了一种一对多的关系,让多个观察对象同时监听一个主题对象,当主题对象状态发生变化时会通知所有观察者,是它们能够自动更新自己 下面是观察者模式的一个demo: #!/usr/bin/env python # -*- coding:utf-8 -*- __author__ = 'Andy' """ 大话设计

JavaScript编程设计模式之观察者模式(Observer Pattern)实例详解

本文实例讲述了JavaScript编程设计模式之观察者模式.分享给大家供大家参考,具体如下: 简介 简单的解释观察者模式,就是一个对象(subject)维护一个依赖他的对象(observers)列表,当自身状态发生变化时,自动通知所有观察者对象.当某个对象不需要获得通知时,可以从对象列表中删除掉. 从上面的解释中我们可以提炼出三个componet: Subject, ObserverList和Observer,用JS实现很简单: function ObserverList(){ this.obs

Java装饰器设计模式_动力节点Java学院整理

定义: 动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活.设计初衷:通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的. 要点: 装饰者与被装饰者拥有共同的超类,继承的目的是继承类型,而不是行为 实际上Java 的I/O API就是使用Decorator实现的. //定义被装

Javascript设计模式之观察者模式(推荐)

推荐阅读: JavaScript观察者模式(经典) 1.什么是观察者模式 观察者模式有时也称为发布--订阅模式,在观察者模式中,有一个观察者可以管理所有的目标,等到有状态发生改变的时候发出通知.(其实sql server中的发布订阅也是这个道理) 2.通俗解释 假如以前村里的广播是一个观察者,那么每个村民就是被观察对象,如果村子里有通知,政策发生改变的时候,就需要通过广播把这个消息发布出去,而不用直接一家家的跑去发通知. 3.代码 <!DOCTYPE html> <html lang=&

PHP设计模式之观察者模式实例

首先了解观察者模式的概念:一个对象通过添加一个方法(该方法允许另一个对象,即观察者 注册自己)使本身变得可观察.当可观察的对象更改时,它会将消息发送到已注册的观察者.这些观察者使用该信息执行的操作与可观察的对象无关.结果是对象可以相互对话,而不必了解原因.观察者模式是一种事件系统,意味着这一模式允许某个类观察另一个类的状态,当被观察的类状态发生改变的时候,观察类可以收到通知并且做出相应的动作;观察者模式为您提供了避免组件之间紧密耦. UML结构图: 观察者模式解决的问题 在我们的开发过程中,应该