Java 设计模式 随笔1 监听器/观察者

news2024/9/20 18:54:27

0. 不由自主,恍恍惚惚,又走回头路,再看一眼有过的幸福…

太棒了流沙!!!

0.1 引用

https://blog.csdn.net/majunzhu/article/details/100869562
ApplicationEvent事件机制源码分析
单机环境下优雅地使用事件驱动进行代码解耦

1. JDK

1.1 监听器模式

spring的ApplicationEvent机制也是基于jdk监听器规范实现的

1.1.1 EventListener

标记接口

package java.util;

/**
 * A tagging interface that all event listener interfaces must extend.
 * @since JDK1.1
 */
public interface EventListener {
}

1.1.2 EventObject

由此可见,jdk的事件规范: 事件本身持有事件源的引用

package java.util;

/**
 * <p>
 * The root class from which all event state objects shall be derived.
 * <p>
 * All Events are constructed with a reference to the object, the "source",
 * that is logically deemed to be the object upon which the Event in question
 * initially occurred upon.
 *
 * @since JDK1.1
 */

public class EventObject implements java.io.Serializable {

    /**
     * The object on which the Event initially occurred.
     */
    protected transient Object  source;

    /**
     * Constructs a prototypical Event.
     *
     * @param    source    The object on which the Event initially occurred.
     * @exception  IllegalArgumentException  if source is null.
     */
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");

        this.source = source;
    }
}

1.1.3 事件源

  • 产生事件的对象,需要自己实现
  • 比如说 spring.ApplicationEvent 的实现类就持有 其事件源 applicationContext

其实 事件由事件源产生,但事件本身并没有传播能力,甚至是路由到特定的监听器受处理。这意味着我们可以按照自己的想法大胆去实现!

再借用spring.applicationEvent 举例,applicationContext 可以作为事件源,调用 multicaster(路由的方式可以理解为传播)给所有的applicationListener投递事件

1.2 观察者模式

  • 用的很少(jdk的实现挺过时的),比较理论,某种程度上与监听器很接近,并且扩展性不如监听器模式
  • 目前没有找到其实现类

1.2.1 可观测对象

package java.util;

/**
 * This class represents an observable object, or "data"
 * in the model-view paradigm. It can be subclassed to represent an
 * object that the application wants to have observed.
 * <p>
 * An observable object can have one or more observers. An observer
 * may be any object that implements interface <tt>Observer</tt>. After an
 * observable instance changes, an application calling the
 * <code>Observable</code>'s <code>notifyObservers</code> method
 * causes all of its observers to be notified of the change by a call
 * to their <code>update</code> method.
 * <p>
 * The order in which notifications will be delivered is unspecified.
 * The default implementation provided in the Observable class will
 * notify Observers in the order in which they registered interest, but
 * subclasses may change this order, use no guaranteed order, deliver
 * notifications on separate threads, or may guarantee that their
 * subclass follows this order, as they choose.
 * <p>
 * Note that this notification mechanism has nothing to do with threads
 * and is completely separate from the <tt>wait</tt> and <tt>notify</tt>
 * mechanism of class <tt>Object</tt>.
 * <p>
 * When an observable object is newly created, its set of observers is
 * empty. Two observers are considered the same if and only if the
 * <tt>equals</tt> method returns true for them.
 *
 * @author  Chris Warth
 * @see     java.util.Observable#notifyObservers()
 * @see     java.util.Observable#notifyObservers(java.lang.Object)
 * @see     java.util.Observer
 * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
 * @since   JDK1.0
 */
public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    /** Construct an Observable with zero Observers. */

    public Observable() {
        obs = new Vector<>();
    }

    /**
     * Adds an observer to the set of observers for this object, provided
     * that it is not the same as some observer already in the set.
     * The order in which notifications will be delivered to multiple
     * observers is not specified. See the class comment.
     *
     * @param   o   an observer to be added.
     * @throws NullPointerException   if the parameter o is null.
     */
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    /**
     * Deletes an observer from the set of observers of this object.
     * Passing <CODE>null</CODE> to this method will have no effect.
     * @param   o   the observer to be deleted.
     */
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    /**
     * If this object has changed, as indicated by the
     * <code>hasChanged</code> method, then notify all of its observers
     * and then call the <code>clearChanged</code> method to
     * indicate that this object has no longer changed.
     * <p>
     * Each observer has its <code>update</code> method called with two
     * arguments: this observable object and <code>null</code>. In other
     * words, this method is equivalent to:
     * <blockquote><tt>
     * notifyObservers(null)</tt></blockquote>
     *
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#hasChanged()
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void notifyObservers() {
        notifyObservers(null);
    }

    /**
     * If this object has changed, as indicated by the
     * <code>hasChanged</code> method, then notify all of its observers
     * and then call the <code>clearChanged</code> method to indicate
     * that this object has no longer changed.
     * <p>
     * Each observer has its <code>update</code> method called with two
     * arguments: this observable object and the <code>arg</code> argument.
     *
     * @param   arg   any object.
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#hasChanged()
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;

        synchronized (this) {
            /* We don't want the Observer doing callbacks into
             * arbitrary code while holding its own Monitor.
             * The code where we extract each Observable from
             * the Vector and store the state of the Observer
             * needs synchronization, but notifying observers
             * does not (should not).  The worst result of any
             * potential race-condition here is that:
             * 1) a newly-added Observer will miss a
             *   notification in progress
             * 2) a recently unregistered Observer will be
             *   wrongly notified when it doesn't care
             */
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    /**
     * Clears the observer list so that this object no longer has any observers.
     */
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    /**
     * Marks this <tt>Observable</tt> object as having been changed; the
     * <tt>hasChanged</tt> method will now return <tt>true</tt>.
     */
    protected synchronized void setChanged() {
        changed = true;
    }

    /**
     * Indicates that this object has no longer changed, or that it has
     * already notified all of its observers of its most recent change,
     * so that the <tt>hasChanged</tt> method will now return <tt>false</tt>.
     * This method is called automatically by the
     * <code>notifyObservers</code> methods.
     *
     * @see     java.util.Observable#notifyObservers()
     * @see     java.util.Observable#notifyObservers(java.lang.Object)
     */
    protected synchronized void clearChanged() {
        changed = false;
    }

    /**
     * Tests if this object has changed.
     *
     * @return  <code>true</code> if and only if the <code>setChanged</code>
     *          method has been called more recently than the
     *          <code>clearChanged</code> method on this object;
     *          <code>false</code> otherwise.
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#setChanged()
     */
    public synchronized boolean hasChanged() {
        return changed;
    }

    /**
     * Returns the number of observers of this <tt>Observable</tt> object.
     *
     * @return  the number of observers of this object.
     */
    public synchronized int countObservers() {
        return obs.size();
    }
}

1.2.2 观察者

package java.util;

/**
 * A class can implement the <code>Observer</code> interface when it
 * wants to be informed of changes in observable objects.
 *
 * @author  Chris Warth
 * @see     java.util.Observable
 * @since   JDK1.0
 */
public interface Observer {
    /**
     * This method is called whenever the observed object is changed. An
     * application calls an <tt>Observable</tt> object's
     * <code>notifyObservers</code> method to have all the object's
     * observers notified of the change.
     *
     * @param   o     the observable object.
     * @param   arg   an argument passed to the <code>notifyObservers</code>
     *                 method.
     */
    void update(Observable o, Object arg);
}

2. guava

  • 单机事件的一个很好的实践
  • 并不走jdk的事件规范
  • 考虑的比较完善

2.1 EventBus

请添加图片描述

  • Executor: 异步分发事件 使用的线程池
  • SubscriberExceptionHandler: 订阅者的异常处理类
  • Dispatcher:根据规则分发
  • Subscribe: 订阅者(相当于监听器)
  • SubscriberRegsitry: 维护订阅关系
  • EventBus: 怎么说呢,事件总线
package com.google.common.eventbus;

// 标记:尚在测试阶段
@Beta
public class EventBus {
  private final String identifier;
  private final Executor executor;
  private final SubscriberExceptionHandler exceptionHandler;

  // 注册器持有当前总线的引用
  private final SubscriberRegistry subscribers = new SubscriberRegistry(this);
  private final Dispatcher dispatcher;

  /** Creates a new EventBus named "default". */
  public EventBus() {
    this("default");
  }

  public EventBus(String identifier) {
    this(
        identifier,
        // 默认的线程池
        // 从命名可知:直接调用的线程池,即单线程的池
        MoreExecutors.directExecutor(),
        Dispatcher.perThreadDispatchQueue(),
        // 异常处理类:日志输出
        LoggingHandler.INSTANCE);
  }

  /**
   * Creates a new EventBus with the given {@link SubscriberExceptionHandler}.
   *
   * @param exceptionHandler Handler for subscriber exceptions.
   * @since 16.0
   */
  public EventBus(SubscriberExceptionHandler exceptionHandler) {
    this(
        "default",
        MoreExecutors.directExecutor(),
        Dispatcher.perThreadDispatchQueue(),
        exceptionHandler);
  }

  EventBus(
      String identifier,
      Executor executor,
      Dispatcher dispatcher,
      SubscriberExceptionHandler exceptionHandler) {
    this.identifier = checkNotNull(identifier);
    this.executor = checkNotNull(executor);
    this.dispatcher = checkNotNull(dispatcher);
    this.exceptionHandler = checkNotNull(exceptionHandler);
  }

  // 异常处理,即 捕获异常,调用异常处理类
  void handleSubscriberException(Throwable e, SubscriberExceptionContext context) {
    checkNotNull(e);
    checkNotNull(context);
    try {
      exceptionHandler.handleException(e, context);
    } catch (Throwable e2) {
      // if the handler threw an exception... well, just log it
      logger.log(
          Level.SEVERE,
          String.format(Locale.ROOT, "Exception %s thrown while handling exception: %s", e2, e),
          e2);
    }
  }

  // 将订阅者注册到register
  public void register(Object object) {
  	// SubscriberRegistry.registry(listener)
    subscribers.register(object);
  }
  public void unregister(Object object) {
    subscribers.unregister(object);
  }

	// 投递事件的地方!
  public void post(Object event) {
  	// 从register中获取匹配事件的、已注册的订阅者
    Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);
    if (eventSubscribers.hasNext()) {
      // step into ...
      // 准备分发到对应的订阅者
      dispatcher.dispatch(event, eventSubscribers);
    } else if (!(event instanceof DeadEvent)) {
      // the event had no subscribers and was not itself a DeadEvent
      post(new DeadEvent(this, event));
    }
  }

	
  // 默认的输出日志的异常处理类
  static final class LoggingHandler implements SubscriberExceptionHandler {
    static final LoggingHandler INSTANCE = new LoggingHandler();

    @Override
    public void handleException(Throwable exception, SubscriberExceptionContext context) {
      Logger logger = logger(context);
      if (logger.isLoggable(Level.SEVERE)) {
        logger.log(Level.SEVERE, message(context), exception);
      }
    }

    private static Logger logger(SubscriberExceptionContext context) {
      return Logger.getLogger(EventBus.class.getName() + "." + context.getEventBus().identifier());
    }

    private static String message(SubscriberExceptionContext context) {
      Method method = context.getSubscriberMethod();
      return "Exception thrown by subscriber method "
          + method.getName()
          + '('
          + method.getParameterTypes()[0].getName()
          + ')'
          + " on subscriber "
          + context.getSubscriber()
          + " when dispatching event: "
          + context.getEvent();
    }
  }
}

2.1.1 MoreExecutors.directExecutor() 默认的线程池

// com.google.common.util.concurrent.MoreExecutors#directExecutor
public static Executor directExecutor() {
	// step into ...
    return DirectExecutor.INSTANCE;
}

// 枚举单例
// com.google.common.util.concurrent.DirectExecutor
@GwtCompatible
enum DirectExecutor implements Executor {
  INSTANCE;

  @Override
  public void execute(Runnable command) {
  	// 直接执行,不绕圈子
    command.run();
  }
}

2.1.2 Dispatcher.perThreadDispatchQueue() 默认的事件分发器

在这里插入图片描述

  // com.google.common.eventbus.Dispatcher#perThreadDispatchQueue
  static Dispatcher perThreadDispatchQueue() {
    return new PerThreadQueuedDispatcher();
  }

  // com.google.common.eventbus.Dispatcher.PerThreadQueuedDispatcher
  private static final class PerThreadQueuedDispatcher extends Dispatcher {

    // This dispatcher matches the original dispatch behavior of EventBus.

    /** Per-thread queue of events to dispatch. */
    private final ThreadLocal<Queue<Event>> queue =
        new ThreadLocal<Queue<Event>>() {
          @Override
          protected Queue<Event> initialValue() {
            return Queues.newArrayDeque();
          }
        };

    /** Per-thread dispatch state, used to avoid reentrant event dispatching. */
    private final ThreadLocal<Boolean> dispatching =
        new ThreadLocal<Boolean>() {
          @Override
          protected Boolean initialValue() {
            return false;
          }
        };

	// 分发逻辑在此
    @Override
    void dispatch(Object event, Iterator<Subscriber> subscribers) {
      checkNotNull(event);
      checkNotNull(subscribers);
      Queue<Event> queueForThread = queue.get();
      queueForThread.offer(new Event(event, subscribers));

      if (!dispatching.get()) {
        dispatching.set(true);
        try {
          Event nextEvent;
          while ((nextEvent = queueForThread.poll()) != null) {
            while (nextEvent.subscribers.hasNext()) {
              nextEvent.subscribers.next().dispatchEvent(nextEvent.event);
            }
          }
        } finally {
          dispatching.remove();
          queue.remove();
        }
      }
    }

    private static final class Event {
      private final Object event;
      private final Iterator<Subscriber> subscribers;

      private Event(Object event, Iterator<Subscriber> subscribers) {
        this.event = event;
        this.subscribers = subscribers;
      }
    }
  }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/725902.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LAXCUS分布式操作系统总体概述

本文回答用户问的一些情况&#xff0c;因为问题主要集中LAXCUS分布式操作系统的产品、市场、发展层面&#xff0c;技术问题倒是不多&#xff0c;我在这里做个总体概述的回答吧。 LAXCUS分布式操作系统是基于分布式运行环境构建的【数存算管】超融合一体化平台&#xff0c;处理…

Excel 合并单元格筛选时只出现首行

一、问题描述 如果对合并单元格直接筛选&#xff0c;只能筛选出第一个单元格的值 二、原因分析&#xff1a; Excel筛选单元格时&#xff0c;遇到不连续区域&#xff08;即中间有空白单元格&#xff09;会识别不到后续内容&#xff1b; 合并单元格后&#xff0c; 除首行外&…

测试中那些悲桑的。。。

今天不讲“锅”&#xff0c;也不讲知识&#xff0c;只聊聊我们在测试中那些悲桑的故事。鉴于过于真实&#xff0c;请新人慎看。 - 1 - 实验室环境中测试顺利进行&#xff0c; 一到发版时刻&#xff0c; 哦豁&#xff0c;出现了一个惊天大bug&#xff01; 通宵走起~ - 2 - 辛…

【机器学习核心总结】什么是RNN(循环神经网络)

什么是RNN(循环神经网络) 循环神经网络(Recurrent Neural Network)&#xff0c;在识别图像时&#xff0c;输入的每张图片都是孤立的&#xff0c;认出这张图片是苹果&#xff0c;并不会对认出下一张图片是梨造成影响。 但对语言来说&#xff0c;顺序是十分重要的&#xff0c;“…

六、计算机视觉相关内容

文章目录 前言一、图像增广1.1 常用的图像增广1.1.1 翻转和裁剪1.1.2 变换颜色1.1.3 结合多种图像增广方法 二、微调2.1 微调的步骤2.2 具体案例 三、 目标检测和边界框3.1 边界框 四、锚框五、多尺度目标检测六、目标检测数据集七、单发多框检测(SSD)八、区域卷积神经网络(R-C…

【NLP】分步图解transformer 数学示例

一、说明 我知道transformer 架构可能看起来很可怕&#xff0c;你可能在网上或博客上遇到了各种解释。但是&#xff0c;在我的博客中&#xff0c;我将通过提供一个全面的数值示例来努力澄清它。通过这样做&#xff0c;我希望简化对变压器架构的理解。 二、输入和位置编码 让我…

网络编程-day3

UDP服务器&#xff1a; UDP客户端&#xff1a;

go-redis的基本使用

Golang操作Redis 安装go-redis //redis 6 go get github.com/go-redis/redis/v8 //redis 7 go get github.com/go-redis/redis/v9golang连接redis import "github.com/go-redis/redis/v8" var rdb *redis.Clientfunc init() {rdb : redis.NewClient(&redis.Opt…

云原生之深入解析Prometheus AlertManager的实战操作

一、概述 Prometheus 包含一个报警模块&#xff0c;就是 AlertManager&#xff0c;Alertmanager 主要用于接收 Prometheus 发送的告警信息&#xff0c;它支持丰富的告警通知渠道&#xff0c;而且很容易做到告警信息进行去重、降噪、分组等&#xff0c;是一款前卫的告警通知系统…

Android Studio实现内容丰富的安卓校园二手交易平台

如需源码可以添加q-------3290510686&#xff0c;也有演示视频演示具体功能&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动。 项目编号038 1.开发环境 android stuido jdk1.8 eclipse mysql tomcat 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.查看二手商品…

[学习笔记] 扩散模型 Diffusion

前置知识-从深度生成模型、隐变量、VAE开始 机器学习是人工智能的一种&#xff0c;它是一种通过利用数据&#xff0c;训练出模型&#xff0c;然后使用模型预测的一种方法。 机器学习分为监督学习、无监督学习和强化学习&#xff0c;这是根据数据训练方式分类的&#xff0c;通俗…

leetcode 100. 相同的树

2023.7.6 这题类似于树的对称性这道题&#xff0c;下面给出递归和迭代两种解法&#xff1a; 递归法&#xff1a; class Solution { public:bool isSameTree(TreeNode* p, TreeNode* q) {if(pnullptr && qnullptr) return true;if(pnullptr && q!nullptr || p…

python实现文本转语音音频

文章目录 文本转语音音频第一步&#xff1a;讯飞平台的注册第二步&#xff1a;导入程序所需要的依赖库第二步&#xff1a;websocket对象类的初始化第三步&#xff1a;websocket建立连接后的函数第四步&#xff1a;websocket数据返回结果的处理函数第五步&#xff1a;pcm音频转换…

VALSE 20200415 | 机器学习 vs 压缩感知:核磁共振成像与重建

【Talk】VALSE 20200415 | 机器学习 vs 压缩感知&#xff1a;核磁共振成像与重建 文章目录 【Talk】VALSE 20200415 | 机器学习 vs 压缩感知&#xff1a;核磁共振成像与重建Deep learning for MR imaging and analysis - Shanshan WangMachine Learning for CS MRI: From Model…

Spring Boot 中的视图解析器是什么,如何使用

Spring Boot 中的视图解析器是什么&#xff0c;如何使用 在 Spring Boot 中&#xff0c;视图解析器是将视图名称解析为具体视图对象的组件。视图对象可以是 JSP、FreeMarker、Thymeleaf 等模板引擎生成的 HTML 页面&#xff0c;也可以是 JSON、XML 等格式的数据响应。Spring B…

基于Javaweb实现ATM机系统开发实战(三)用户查询功能实现

首先通过我们查看前端界面发现&#xff0c;先要实现前端用户查询功能&#xff0c;主要就是要把list1和list2所需的数据传递给前端&#xff0c;由前端进行展示。 首先我们需要写一个servlet处理收到的请求&#xff1a; ps&#xff1a;Servlet是什么&#xff1f; Java Servlet 是…

FreeRTOS ~(五)队列的常规使用 ~ (1/5)队列解决同步缺陷

前情提要 FreeRTOS ~&#xff08;四&#xff09;同步互斥与通信 ~ &#xff08;1/3&#xff09;同步的缺陷 举例子说明&#xff1a;利用队列解决前述的"同步的缺陷"问题 static int sum 0; /* sum存放计算的结果 */ static volatile int flagCalcEnd 0; /* 标…

哪款3D虚拟人物建模软件好用?

3D虚拟人物建模软件一直以来受到许多人的关注和追捧。现在&#xff0c;随着智能手机的普及&#xff0c;3D虚拟人物手机建模软件也开始走进大家的视野。那么&#xff0c;市面上3D虚拟人物建模软件这么多&#xff0c;究竟哪款3D虚拟人物建模软件是好用的呢&#xff1f; 首先&…

【聚类算法】OPTICS基于密度聚类

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 对DBSCAN的补充&#xff0c;OPTICS聚类 1. 正文 1.0 DBSCAN的存在问题 前面我们介绍了DBSCAN&#xff0c;其能根据密度进行聚类。 但其存在这样一个问…

cv2 安装问题, opencv

解决安装了opencv-python&#xff0c;但 import cv2 报错。 需要安装&#xff1a; pip install opencv-python-headless