别再面向for循环编程了,Spring自带的观察者模式就很香!

开发 后端
不用再面试 for 循环编程了,Spring 框架自带的事件监听机制,实现观察者模式、实现解耦轻松帮你全搞定!

 [[393113]]

分享下如何在 Spring/ Spring Boot 中实现观察者模式。

不用再面试 for 循环编程了,Spring 框架自带的事件监听机制,实现观察者模式、实现解耦轻松帮你全搞定!

Spring 事件监听机制

其实在 Spring/ Spring Boot 框架中有一套事件监听机制,可以实现观察者模式。

Spring/ Spring Boot 框架中也都内置了许多事件,我们也可以自定义发布应用程序事件,下面我们会介绍。

其主要涉及到的几个核心类和接口如下 :

ApplicationEvent

ApplicationEvent(应用程序事件)它是一个抽象类,相当于观察者模式中的观察目标。

ApplicationEvent 源码如下: 

  1. public abstract class ApplicationEvent extends EventObject {  
  2.    /** use serialVersionUID from Spring 1.2 for interoperability. */  
  3.    private static final long serialVersionUID = 7099057708183571937L 
  4.    /** System time when the event happened. */  
  5.    private final long timestamp;  
  6.    /**  
  7.     * Create a new {@code ApplicationEvent}.  
  8.     * @param source the object on which the event initially occurred or with  
  9.     * which the event is associated (never {@code null})  
  10.     */  
  11.    public ApplicationEvent(Object source) {  
  12.       super(source);  
  13.       this.timestamp = System.currentTimeMillis();  
  14.    }  
  15.    /**  
  16.     * Return the system time in milliseconds when the event occurred.  
  17.     */  
  18.    public final long getTimestamp() {  
  19.       return this.timestamp;  
  20.    }  

ApplicationEvent 继承自 Java 中的 EventObject 事件对象类,Spring 框架中的所有事件都继承自 ApplicationEvent 类,它是所有事件的父类。

ApplicationEvent 主要的核心是类构造器,它可以初始化一个 source 事件关联对象,以便在事件监听器中获取并通知更新。

ApplicationListener

ApplicationListener(应用程序事件监听器)它是一个接口,相当于观察者模式中的观察者。

ApplicationListener 源码如下: 

  1. public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {  
  2.    /**  
  3.     * Handle an application event.  
  4.     * @param event the event to respond to  
  5.     */  
  6.    void onApplicationEvent(E event);  

ApplicationListener 继承自 Java 中的 EventListener 事件监听接口,ApplicationListener 类中只有一个 onApplicationEvent 方法,当指定监听的事件被发布后就会被触发执行,可以通过 event 获取事件中的关联对象。

ApplicationEventPublisher

应用程序事件发布接口,封装了事件发布功能的基础接口。 

  1. public interface ApplicationEventPublisher {  
  2.    /**  
  3.     * Notify all <strong>matching</strong> listeners registered with this  
  4.     * application of an application event. Events may be framework events  
  5.     * (such as ContextRefreshedEvent) or application-specific events.  
  6.     * <p>Such an event publication step is effectively a hand-off to the  
  7.     * multicaster and does not imply synchronous/asynchronous execution  
  8.     * or even immediate execution at all. Event listeners are encouraged  
  9.     * to be as efficient as possible, individually using asynchronous  
  10.     * execution for longer-running and potentially blocking operations.  
  11.     * @param event the event to publish  
  12.     * @see #publishEvent(Object)  
  13.     * @see org.springframework.context.event.ContextRefreshedEvent  
  14.     * @see org.springframework.context.event.ContextClosedEvent  
  15.     */  
  16.    default void publishEvent(ApplicationEvent event) {  
  17.       publishEvent((Object) event);  
  18.    }  
  19.    /**  
  20.     * Notify all <strong>matching</strong> listeners registered with this  
  21.     * application of an event. 
  22.     * <p>If the specified {@code event} is not an {@link ApplicationEvent},  
  23.     * it is wrapped in a {@link PayloadApplicationEvent}.  
  24.     * <p>Such an event publication step is effectively a hand-off to the  
  25.     * multicaster and does not imply synchronous/asynchronous execution  
  26.     * or even immediate execution at all. Event listeners are encouraged  
  27.     * to be as efficient as possible, individually using asynchronous  
  28.     * execution for longer-running and potentially blocking operations.  
  29.     * @param event the event to publish  
  30.     * @since 4.2  
  31.     * @see #publishEvent(ApplicationEvent)  
  32.     * @see PayloadApplicationEvent  
  33.     */  
  34.    void publishEvent(Object event);  

ApplicationEventPublisher 有一个默认接口方法和接口方法,接口方法需要由具体的子类容器实现。

ApplicationContext

ApplicationContext 这个类就再熟悉不过了,它是 Spring 框架中的核心容器。

如下图所示,ApplicationContext 接口继承了 ApplicationEventPublisher 接口,所以常用的 ApplicationContext 就可以用来发布事件。

以上介绍的 Spring 事件监听发布角色串起来就是,通过 ApplicationEventPublisher 或者 ApplicationContext 容器发布  ApplicationEvent 事件并关联事件对象,然后 ApplicationListener 监听该事件,当事件发布后,监听器就会收执行并获取到事件及关联对象。

Spring Boot 观察者模式实战

搞懂了 Spring 框架中的事件和监听机制,那我们还是以上篇中观察者模式的例子来改造下。

Spring Boot 基础性的知识和搭建过程就不介绍了,不熟悉的可以关注公众号Java技术栈,在后台回复关键字 "boot" 阅读我之前写的系列教程。

所有 Spring Boot 教程实战源码在下面个仓库:

https://github.com/javastacks/spring-boot-best-practice

新增观察者目标类 

  1. import lombok.Getter;  
  2. import org.springframework.context.ApplicationEvent;  
  3. /**  
  4.  * 观察目标:栈长  
  5.  * 来源微信公众号:Java技术栈  
  6.  */  
  7. @Getter  
  8. public class JavaStackEvent extends ApplicationEvent {  
  9.     /**  
  10.      * Create a new {@code ApplicationEvent}.  
  11.      * 
  12.      * @param source the object on which the event initially occurred or with  
  13.      *               which the event is associated (never {@code null})  
  14.      */  
  15.     public JavaStackEvent(Object source) {  
  16.         super(source);  
  17.     }  

实现 Spring 框架中的 ApplicationEvent 应用程序事件接口,相当于是一个观察者目标。

新增观察者类 

  1. import lombok.NonNull;  
  2. import lombok.RequiredArgsConstructor;  
  3. import org.springframework.context.ApplicationListener;  
  4. import org.springframework.scheduling.annotation.Async;  
  5. /**  
  6.  * 观察者:读者粉丝  
  7.  * 来源微信公众号:Java技术栈  
  8.  */  
  9. @RequiredArgsConstructor  
  10. public class ReaderListener implements ApplicationListener<JavaStackEvent> {  
  11.     @NonNull  
  12.     private String name;  
  13.     private String article; 
  14.     @Async  
  15.     @Override  
  16.     public void onApplicationEvent(JavaStackEvent event) {  
  17.         // 更新文章 
  18.         updateArticle(event);  
  19.     }  
  20.     private void updateArticle(JavaStackEvent event) {  
  21.         this.article = (String) event.getSource();  
  22.         System.out.printf("我是读者:%s,文章已更新为:%s\n", this.name, this.article);  
  23.     }  

实现 Spring 框架中的 ApplicationListener 应用监听接口,相当于是观察者。

观察目标和观察者类结构图如下:

新增测试配置类 

  1. import lombok.extern.slf4j.Slf4j;  
  2. import org.springframework.boot.CommandLineRunner;  
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.context.annotation.Bean; 
  5. import org.springframework.context.annotation.Configuration;  
  6. @Slf4j  
  7. @Configuration  
  8. public class ObserverConfiguration {  
  9.     @Bean  
  10.     public CommandLineRunner commandLineRunner(ApplicationContext context) {  
  11.         return (args) -> {  
  12.             log.info("发布事件:什么是观察者模式?");  
  13.             context.publishEvent(new JavaStackEvent("什么是观察者模式?"));  
  14.         };  
  15.     }  
  16.     @Bean  
  17.     public ReaderListener readerListener1(){  
  18.         return new ReaderListener("小明"); 
  19.     }  
  20.     @Bean  
  21.     public ReaderListener readerListener2(){  
  22.         return new ReaderListener("小张");  
  23.     }  
  24.     @Bean  
  25.     public ReaderListener readerListener3(){  
  26.         return new ReaderListener("小爱"); 
  27.     } 

在 Spring 配置中创建了三个读者 Bean,在 Spring Boot 启动后发布一个观察者模式事件,然后这三个 Bean 就会收到通知。

输出结果:

这里每个读者创建一个 Bean 可能不太合适,因为要模仿上一个观察者模式的应用。

实际中的观察者模式应用应该是指具体业务,举例说一个电商支付场景,在用户支付完后可以发布一个支付事件,然后会有扣减积分,短信通知、赠送优惠券等一系列后续的事件监听器观察者,这样可以实现业务解耦,这是一种很典型的应用场景。

如果大家有用到消息中间件,其实也是观察者模式中发布订阅模式的概念。

总结

利用 Spring 中的事件监听机制也可以轻松实现观察者模式,观察目标也不需要维护观察者列表了,相当于发布-订阅模式,它们之间是完全解耦的,但每个观察者需要创建一个 Bean。

好了,今天的分享就到这里了,又学了一种设计模式的写法吧,后面栈长我会更新其他设计模式的实战文章,公众号Java技术栈第一时间推送。

本节教程所有实战源码已上传到这个仓库:

https://github.com/javastacks/spring-boot-best-practice 

 

责任编辑:庞桂玉 来源: Java编程
相关推荐

2021-03-29 07:14:28

Spring观察者模式

2020-10-26 08:45:39

观察者模式

2021-09-06 10:04:47

观察者模式应用

2022-01-29 22:12:35

前端模式观察者

2013-11-26 17:09:57

Android设计模式

2021-07-08 11:28:43

观察者模式设计

2011-04-29 09:22:22

2012-08-27 10:52:20

.NET架构观察者模式

2022-07-13 08:36:57

MQ架构设计模式

2024-02-18 12:36:09

2015-11-25 11:10:45

Javascript设计观察

2009-03-30 09:39:04

观察者思想换位设计模式

2024-06-04 13:11:52

Python行为设计模式开发

2021-01-25 05:38:04

设计原理VueSubject

2022-11-15 07:35:50

Spring事件观察者模式

2021-09-29 19:45:24

观察者模式Observable

2021-06-03 12:26:28

观察者模式面试阿里P6

2020-12-09 05:18:17

面试观察者订阅模式

2022-05-09 10:50:13

观察者模式设计模式

2021-06-07 20:03:04

监听器模式观察者
点赞
收藏

51CTO技术栈公众号