上一篇:JDK 自带的观察者模式就很香!
前段时间栈长给大家分享了什么是观察者模式,以及在 JDK 中如何实现观察者模式,现在都是 Spring 的天下了,今天就再分享下如何在 Spring/ Spring Boot 中实现观察者模式。
不用再面试 for 循环编程了,Spring 框架自带的事件监听机制,实现观察者模式、实现解耦轻松帮你全搞定!
其实在 Spring/ Spring Boot 框架中有一套事件监听机制,可以实现观察者模式。
Spring/ Spring Boot 框架中也都内置了许多事件,我们也可以自定义发布应用程序事件,下面我们会介绍。
其主要涉及到的几个核心类和接口如下 :
ApplicationEvent(应用程序事件)它是一个抽象类,相当于观察者模式中的观察目标。
ApplicationEvent 源码如下:
public abstract class ApplicationEvent extends EventObject { /** use serialVersionUID from Spring 1.2 for interoperability. */ private static final long serialVersionUID = 7099057708183571937L; /** System time when the event happened. */ private final long timestamp; /** * 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(); } /** * Return the system time in milliseconds when the event occurred. */ public final long getTimestamp() { return this.timestamp; } }
ApplicationEvent 继承自 Java 中的 EventObject 事件对象类,Spring 框架中的所有事件都继承自 ApplicationEvent 类,它是所有事件的父类。
ApplicationEvent 主要的核心是类构造器,它可以初始化一个 source 事件关联对象,以便在事件监听器中获取并通知更新。
ApplicationListener(应用程序事件监听器)它是一个接口,相当于观察者模式中的观察者。
ApplicationListener 源码如下:
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { /** * Handle an application event. * @param event the event to respond to */ void onApplicationEvent(E event); }
ApplicationListener 继承自 Java 中的 EventListener 事件监听接口,ApplicationListener 类中只有一个 onApplicationEvent 方法,当指定监听的事件被发布后就会被触发执行,可以通过 event 获取事件中的关联对象。
应用程序事件发布接口,封装了事件发布功能的基础接口。
public interface ApplicationEventPublisher { /** * Notify all <strong>matching</strong> listeners registered with this * application of an application event. Events may be framework events * (such as ContextRefreshedEvent) or application-specific events. * <p>Such an event publication step is effectively a hand-off to the * multicaster and does not imply synchronous/asynchronous execution * or even immediate execution at all. Event listeners are encouraged * to be as efficient as possible, individually using asynchronous * execution for longer-running and potentially blocking operations. * @param event the event to publish * @see #publishEvent(Object) * @see org.springframework.context.event.ContextRefreshedEvent * @see org.springframework.context.event.ContextClosedEvent */ default void publishEvent(ApplicationEvent event) { publishEvent((Object) event); } /** * Notify all <strong>matching</strong> listeners registered with this * application of an event. * <p>If the specified {@code event} is not an {@link ApplicationEvent}, * it is wrapped in a {@link PayloadApplicationEvent}. * <p>Such an event publication step is effectively a hand-off to the * multicaster and does not imply synchronous/asynchronous execution * or even immediate execution at all. Event listeners are encouraged * to be as efficient as possible, individually using asynchronous * execution for longer-running and potentially blocking operations. * @param event the event to publish * @since 4.2 * @see #publishEvent(ApplicationEvent) * @see PayloadApplicationEvent */ void publishEvent(Object event); }
ApplicationEventPublisher 有一个默认接口方法和接口方法,接口方法需要由具体的子类容器实现。
ApplicationContext 这个类就再熟悉不过了,它是 Spring 框架中的核心容器。
如下图所示,ApplicationContext 接口继承了 ApplicationEventPublisher 接口,所以常用的 ApplicationContext 就可以用来发布事件。
以上介绍的 Spring 事件监听发布角色串起来就是,通过 ApplicationEventPublisher 或者 ApplicationContext 容器发布 ApplicationEvent 事件并关联事件对象,然后 ApplicationListener 监听该事件,当事件发布后,监听器就会收执行并获取到事件及关联对象。
搞懂了 Spring 框架中的事件和监听机制,那我们还是以上篇中观察者模式的例子来改造下。
Spring Boot 基础性的知识和搭建过程就不介绍了,不熟悉的可以关注公众号Java技术栈,在后台回复关键字 "boot" 阅读我之前写的系列教程。
所有 Spring Boot 教程实战源码在下面个仓库:
https://github.com/javastacks/spring-boot-best-practice
import lombok.Getter; import org.springframework.context.ApplicationEvent; /** * 观察目标:栈长 * 来源微信公众号:Java技术栈 */ @Getter public class JavaStackEvent extends ApplicationEvent { /** * 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 JavaStackEvent(Object source) { super(source); } }
实现 Spring 框架中的 ApplicationEvent 应用程序事件接口,相当于是一个观察者目标。
import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.springframework.context.ApplicationListener; import org.springframework.scheduling.annotation.Async; /** * 观察者:读者粉丝 * 来源微信公众号:Java技术栈 */ @RequiredArgsConstructor public class ReaderListener implements ApplicationListener<JavaStackEvent> { @NonNull private String name; private String article; @Async @Override public void onApplicationEvent(JavaStackEvent event) { // 更新文章 updateArticle(event); } private void updateArticle(JavaStackEvent event) { this.article = (String) event.getSource(); System.out.printf("我是读者:%s,文章已更新为:%s\n", this.name, this.article); } }
实现 Spring 框架中的 ApplicationListener 应用监听接口,相当于是观察者。
观察目标和观察者类结构图如下:
import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Slf4j @Configuration public class ObserverConfiguration { @Bean public CommandLineRunner commandLineRunner(ApplicationContext context) { return (args) -> { log.info("发布事件:什么是观察者模式?"); context.publishEvent(new JavaStackEvent("什么是观察者模式?")); }; } @Bean public ReaderListener readerListener1(){ return new ReaderListener("小明"); } @Bean public ReaderListener readerListener2(){ return new ReaderListener("小张"); } @Bean public ReaderListener readerListener3(){ return new ReaderListener("小爱"); } }
在 Spring 配置中创建了三个读者 Bean,在 Spring Boot 启动后发布一个观察者模式事件,然后这三个 Bean 就会收到通知。
输出结果:
这里每个读者创建一个 Bean 可能不太合适,因为要模仿上一个观察者模式的应用。
实际中的观察者模式应用应该是指具体业务,举例说一个电商支付场景,在用户支付完后可以发布一个支付事件,然后会有扣减积分,短信通知、赠送优惠券等一系列后续的事件监听器观察者,这样可以实现业务解耦,这是一种很典型的应用场景。
如果大家有用到消息中间件,其实也是观察者模式中发布订阅模式的概念。
利用 Spring 中的事件监听机制也可以轻松实现观察者模式,观察目标也不需要维护观察者列表了,相当于发布-订阅模式,它们之间是完全解耦的,但每个观察者需要创建一个 Bean。
好了,今天的分享就到这里了,又学了一种设计模式的写法吧,后面栈长我会更新其他设计模式的实战文章,公众号Java技术栈第一时间推送。
本节教程所有实战源码已上传到这个仓库:
https://github.com/javastacks/spring-boot-best-practice
最后,觉得我的文章对你用收获的话,动动小手,给个在看、转发,原创不易,栈长需要你的鼓励。
版权申明:本文系公众号 "Java技术栈" 原创,原创实属不易,转载、引用本文内容请注明出处,禁止抄袭、洗稿,请自重,尊重他人劳动成果和知识产权。
近期热文推荐:
1.600+ 道 Java面试题及答案整理(2021最新版)
2.终于靠开源项目弄到 IntelliJ IDEA 激活码了,真香!
3.阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!
4.Spring Cloud 2020.0.0 正式发布,全新颠覆性版本!
5.《Java开发手册(嵩山版)》最新发布,速速下载!
觉得不错,别忘了随手点赞+转发哦!