• 未来:spring响应式编程 Hands-On Reactive Programming in Spring 5(二)------Basic Concepts


    看完这篇文章你会有很大收获!
    好学近乎知,力行近乎仁,知耻而后勇.

    The previous chapter explained why it is important to build reactive systems and how
    reactive programming helps to do this. In this section, we will look at some toolsets that have
    already been present in Spring Framework for some time. We will also learn the important basic
    concepts of reactive programming by exploring the RxJava library, which is the first and most
    well-known reactive library in the Java world
    这一章节我们要来谈谈.spring响应式编程了

    Early reactive solutions in Spring

    We have previously mentioned that there are a lot of patterns and programming techniques
    that are capable of becoming building blocks for the reactive system. For example, callbacks
    and CompletableFuture are commonly used to implement the message-driven
    architecture. We also mentioned reactive programming as a prominent candidate for such a
    role. Before we explore this in more detail, we need to look around and find other solutions
    that we have already been using for years.

    1.早期使用CompletableFuture (java多线程种的开辟异步线程的一个类)和 callbacks(异步回调)来解决阻塞问题
    2.异步编程也是一个很好的备选方案
    3.我们也需要找到其他的解决方案

    newer Java 8 CompletableFuture,
    which introduces some neat methods for asynchronous execution composition.
    Nevertheless, Spring Framework provides other bits of infrastructure that will be very
    useful for building our reactive application. Let’s look through some of these features now.

    早期的java8提供的CompletableFuture是一种解异步编程的解决方案,然而,spring框架有自己的提供的解决响应式编程的方案

    Observer pattern(观察者模式)

    To move things along(随着事情的发展), we need to remind(回忆起) ourselves about a particular pretty old and wellknown
    design pattern—the Observer pattern. That is one of the twenty-three famous GoF
    (Gang of Four) design patterns. At first glance, it may appear that the Observer pattern is
    not related to reactive programming. However, as we will see later, with some small
    modifications, it defines the foundations of reactive programming.
    To read more about GoF design patterns, please refer to Design
    Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma,
    Richard Helm, Ralph Johnson, and John Vlissides (https:/ / en.
    wikipedia. org/ wiki/ Design_ Patterns).
    The Observer pattern involves a subject that holds a list of its dependants, called
    Observers. The subject notifies its observers of any state changes, usually by calling one of
    their methods. This pattern is essential when implementing (贯彻)systems based on event
    handling. The Observer pattern is a vital part of the MVC (Model-View-Controller)
    pattern. Consequently, almost all UI libraries apply it internally.

    1.提出 观察者模式 :一种古老且优雅的模式,23种设计模式之一,
    被观察者 做出状态变更 通过 调用观察者的方法 提醒观察者自己的变更
    这种模式使用在 事件驱动的 系统中,在MVC 架构种是一个非常重要的组件,几乎所有的UI库都在内部应用它。

    • 下面是我和hdx 对观察者模式的探讨
      在这里插入图片描述

    To simplify this, let’s use an analogy (类比)from an everyday situation. We can apply this pattern
    to the newsletter subscription from one of the technical portals(门户网站). We have to register our
    email address somewhere on the site of our interest, and then it will send us notifications in
    the form of newsletters, as shown in the following diagram:
    为了解释上面的这种情况,我举个例子,我们能够订阅门户网站获得实时通讯,我们必须注册我们的
    电子邮件地址在我们感兴趣的网站上,然后该网站会以实时通信的形式 会发送给我们一个通知 。如下图
    在这里插入图片描述
    The Observer pattern makes it possible to register one-to-many dependencies between
    objects at runtime. At the same time, it does this without knowing anything about the
    component implementation details (to be type safe, an observer may be aware of a type of
    incoming event). That gives us the ability to decouple application pieces even though these
    parts actively interact. Such communication is usually one directional and helps efficiently
    distribute events through the system, as shown in the following diagram:
    观察者能够依赖一到多个对象,与此同时,观察者不需要了解组件的任何细节,观察者也许只需要知道 传入事件。下图这种设计具有很强的解耦合能力,这种 交流是单项的,通过这种系统具有很强的分发事件。
    1.只能被观察者通知观察者 (单项交流)
    2.一个观察者接收多个被观察者的发过来的事件 (强的分发能力)
    在这里插入图片描述

    观察者模式

    As the preceding diagram shows, a typical Observer pattern consists of two
    interfaces, Subject(被观察对象) and Observer. Here, an Observer is registered in Subject and
    listens for notifications from it. A Subject may generate events on its own or may be called
    by other components. Let’s define a Subject interface in Javazh

    上面有两个接口,一个是观察者,一个是被观察者, 观察者被注册进观察者对象,
    所谓的监听就是被观察对象提醒 观察者~~

    实现被观察者接口和观察者接口

    public interface Subject<T> {
     void registerObserver(Observer<T> observer);
     void unregisterObserver(Observer<T> observer);
     void notifyObservers(T event);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    This generic interface is parametrized with the event type T, which improves the type
    safety of our programme. It also contains methods for managing subscriptions
    (the registerObserver, unregisterObserver, and notifyObservers methods) that
    trigger an event’s broadcasting. In turn, the Observer interface may look like the
    following:

    通用接口将 事件类进行泛化,那可以保护程序的类型安全.有三个管理订阅者的方法,分别是增删订阅者,和提醒订阅者.
    被观察者会触发事件的广播,相反而言,观察者接口
    public interface Subject {
    void registerObserver(Observer observer);
    void unregisterObserver(Observer observer);
    void notifyObservers(T event);
    }

    public interface Observer<T> {
    void observe(T event);
    }
    
    • 1
    • 2
    • 3

    The Observer is a generic interface, which parametrized with the T type . In turn, it has
    only one observe method in place to handle events. Both the Observer and Subject do
    not know about each other more than described in these interfaces.
    The Observer implementation may be responsible for the subscription procedure, or
    the Observer instance may not be aware of the existence of the Subject at all. In the latter
    case, a third component may be responsible for finding all of the instances of
    the Subject and all registration procedures. For example, such a role may come into
    play with the Dependency Injection container. This scans the classpath for each
    Observer with the @EventListener annotation and the correct signature. After that, it
    registers the found components to the Subject.

    观察者是一个通用的接口,观察者具有一个泛型参数. 相反而言,他仅仅拥有一个 observe 方法这个方法可以处理事件,
    观察者和被观察者对彼此并不了解在这些接口之中
    观察者 对 订阅程序 进行响应 或者 观察者根本不关系订阅者的全部。
    同时一个三方组件越需要找到所有的被观察者,和所有的注册手续(被观察者需要注册进观察者),
    例如,有一个角色 ,他将在容器的依赖注入中发挥作用,使用 @EventListener 注解 扫描观察者的类路径和签名,过后 观察者将被注册成一个组件 ,这个组件 观察者可以发现。

    下面是一个简单的案例

    观察者 A
    public class ConcreteObserverA implements Observer<String> {
    @Override
    public void observe(String event) {
    System.out.println("Observer A: " + event);
    
    观察者 B
    public class ConcreteObserverB implements Observer<String> {
    @Override
    public void observe(String event) {
    System.out.println("Observer B: " + event);
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    We also need to write an implementation of the Subject, which
    produces String events, as shown in the following code:

    具体的被观察者对象(也是事件发布的 对象)
    public class ConcreteSubject implements Subject<String> {
    
    // 这个set 里面包含了所有的 被观察者  
    private final Set<Observer<String>> observers = new CopyOnWriteArraySet<>();
    
    // 这个方法 用来注册观察者
    public void registerObserver(Observer<String> observer) {
    observers.add(observer);
     }
    // 这个方法 删除保存的观察者
    public void unregisterObserver(Observer<String> observer) {
    observers.remove(observer);
     }
    // 提醒观察者 ,遍历所有观察者 让观察者接收 自己发出的事件
    public void notifyObservers(String event) {
    observers.forEach(observer -> observer.observe(event)); 
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    As we can see from the preceding example, the implementation of the Subject holds the
    Set of observers (1) that are interested in receiving notifications. In turn, a modification
    (subscription or cancellation of the subscription) of the mentioned Set is
    possible with the support of the registerObserver and unregisterObserver methods.
    To broadcast events, the Subject has a notifyObservers method (2) that iterates over
    the list of observers and invokes the observe() method with the actual event (2.1) for
    each Observer. To be secure in the multithreaded scenario, we
    use CopyOnWriteArraySet, a thread-safe Set implementation that creates a new copy of
    its elements each time the update operation happens. It is relatively expensive to update
    the contents of the CopyOnWriteArraySet, especially when the container holds a lot of
    elements. However, the list of subscribers does not usually change often, so it is a fairly
    reasonable option for the thread-safe Subject implementation.
    根据上图我们可以发现 ,被观察者 拥有设置 对接收通知感冒的观察者 的一个方法 ,
    相反一个被提到的方法 set(订阅和 取消订阅 )用来 注册和 删除订阅者。
    为了广播 ,被观察者有一个提醒全部观察者的方法(采用遍历的方式) ,并且需要调用观察者的 一个接收方法,
    为了确保在多线程情况下的安全 ,
    我们使用 CopyOnWriteArraySet 这个 set集合: 他更新的时候创建他里面元素新的副本,然而,相对而言,更新
    CopyOnWriteArraySet 里面元素的代价是昂贵的,尤其里面有很多的元素的时候。但是 订阅者 通常情况下是不怎么会进行变更的 ,
    所以他是一个线程非常安全的set集合 也非常适合用来保证 观察者模式的线程安全

    下面是一个具体案例

    @Test
    public void observersHandleEventsFromSubject() {
    // given
    Subject<String> subject = new ConcreteSubject();
    Observer<String> observerA = Mockito.spy(new ConcreteObserverA());
    Observer<String> observerB = Mockito.spy(new ConcreteObserverB());
    // when
    subject.notifyObservers("No listeners");
    subject.registerObserver(observerA);
    subject.notifyObservers("Message for A");
    subject.registerObserver(observerB);
    subject.notifyObservers("Message for A & B");
    subject.unregisterObserver(observerA);
    subject.notifyObservers("Message for B");
    subject.unregisterObserver(observerB);
    subject.notifyObservers("No listeners");
    // then
    Mockito.verify(observerA, times(1)).observe("Message for A");
    Mockito.verify(observerA, times(1)).observe("Message for A & B");
    Mockito.verifyNoMoreInteractions(observerA);
    Mockito.verify(observerB, times(1)).observe("Message for A & B");
    Mockito.verify(observerB, times(1)).observe("Message for B");
    Mockito.verifyNoMoreInteractions(observerB);
    }
    
    @Test
    public void subjectLeveragesLambdas() {
    Subject<String> subject = new ConcreteSubject();
    subject.registerObserver(e -> System.out.println("A: " + e));
    subject.registerObserver(e -> System.out.println("B: " + e));
    subject.notifyObservers("This message will receive A & B");
    ...
    }
    
    
    private final ExecutorService executorService =
    Executors.newCachedThreadPool();
    public void notifyObservers(String event) {
    observers.forEach(observer ->
    executorService.submit(
    () -> observer.observe(event)
       )
         );
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    事件监听注解@EventListener

    To play with the Publish-Subscribe pattern in Spring Framework, let’s do an exercise. In
    turn, assume that we have to implement a simple web service that shows the current
    temperature in the room. For this purpose, we have a temperature sensor(温度计), which sends
    events with the current temperature in Celsius from time to time. We potentially want to
    have both mobile and web applications, but for the sake of (为了)conciseness, we are
    implementing only a simple web application. Furthermore, as questions of communication
    with microcontrollers are out of the scope of this book, we are simulating a temperature
    sensor using a random number generator
    我们用spring框架来实现这个 模式,我们有一个 service 来展示当前房间温度
    我们模拟一个情况,房间里有个温度传感器(温度是随机数模拟的),无时无刻记录者温度的变化。

    To make our application following the reactive design, we cannot use an old pulling model
    for data retrieval(数据检索). Fortunately, nowadays we have some well-adopted protocols(方案) for
    asynchronous message propagation (异步消息椽笔)from a server to a client,
    namely WebSockets and Server-Sent Events (SSE). In the current example, we will use the
    last one. The SSE allows a client to receive automatic updates from a server, and is
    commonly used to send message updates or continuous data streams to a browser. With
    the inception(起初) of HTML5, all modern browsers have a JavaScript API called EventSource,(事件源)
    which is used by clients who request a particular URL to receive an event stream.
    The EventSource also autore connects by default in the case of communication issues. It is
    important to highlight that SSE is an excellent candidate for fulfilling communication needs
    between components in the reactive system. On par with WebSocket, SSE is used a lot in
    this book.
    1.我们使用新的框架完成异步通信,例如Websocket 或者是 SSE
    SSE 运行客户端自动接收 来自服务端的更新 ,
    SSE也通常用来发送更新消息或者持续的数据流到服务器上.
    2.在HTML5的起初,所有的 浏览器 都有一个 JavaScript API 叫EventSource
    这个api 在客户端经常使用
    客户端请求一个特定的URL 去接收事件流
    在通信的问题下,EventSource 默认自连接
    很重要的一点是,SSE是满足组件间通信需求的优秀框架 在 响应式系统中。
    下面是我和 hxd对EventSource的讨论
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    Bootstrapping a Spring application

    To implement our usecase, we are using the well-known Spring modules Spring Web and
    Spring Web MVC. Our application will not use the new features of Spring 5, so it will run
    similarly on Spring Framework 4.x. To simplify our development process and even more,
    we are leveraging Spring Boot, which is described in more detail later. To bootstrap our
    application, we may configure and download a Gradle project from the Spring Initializer
    website at start.spring.io. For now, we need to select the preferred Spring Boot version
    and dependency for the web (the actual dependency identifier in Gradle config will
    be org.springframework.boot:spring-boot-starter-web), as shown in the
    following screenshot:
    1.为了完成这个案例 我们使用spring5框架为了简化我们的开发进度,我们使用springboot
    我们将配置和下载一个 Gradle 项目
    后面的下个章节再更新吧…

  • 相关阅读:
    橙河网络:国外问卷调查赚钱的项目可靠吗?
    “兼职开发的程序员,为什么不受企业待见?”
    Github每日精选(第73期):Go 的 JSON 解析器gjson
    [架构之路-18]:目标系统 - 硬件平台 - 案例1 - 单片机MCU STM32 芯片的工作原理与启动流程
    RapidEye快鸟、SPOT卫星遥感影像数据
    模拟实现stl库中的list(支持const迭代器、移动语义)
    Node-简介
    STM32串口printf通过DMA打印(含实测代码)
    批量解决opencv cv2.imread读取32位抠图png图像后,出现隐藏背景无法去除的问题
    掌握Perl并发:线程与进程编程全攻略
  • 原文地址:https://blog.csdn.net/weixin_45699541/article/details/126398136