嗨,您好 我是 vnjohn,在互联网企业担任 Java 开发,CSDN 优质创作者
推荐专栏:Spring、MySQL、Nacos、Java,后续其他专栏会持续优化更新迭代
文章所在专栏:Spring
我当前正在学习微服务领域、云原生领域、消息中间件等架构、原理知识
向我询问任何您想要的东西,ID:vnjohn
觉得博主文章写的还 OK,能够帮助到您的,感谢三连支持博客
代词: vnjohn
⚡ 有趣的事实:音乐、跑步、电影、游戏
目录
在 Spring 运用中,观察者模式运用的场景很多,只不过在 Spring 内部为观察者模式定义为了抽象,使用多路广播器方式将观察者、被观察者、行为抽象了事件、监听器、事件源等名,下面对该内容进行详细分析
监听器:模式中的观察者角色
多路广播器:被观察者触发事件调用,多个观察者监听到事件处理对应方法的逻辑
在 Spring 里面其实就是运用观察者模式进行设计的,它对比传统的观察者模式基础上进行了加工,概念上已经改为了基于事件驱动触发行为动作,下面对传统观察者模式、Spring 加工后的观察者模式进行介绍
观察者模式又称为发布/订阅(Publish/Subscribe)模式,在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新触发行为
观察者、被观察者共有行为
/**
* 被观察者
*/
public interface Observable {
public void addObserver(Observer observer);
public void deleteObserver(Observer observer);
public void notifyObserver(String str);
}
/**
* 观察者
*/
public interface Observer {
public void make(String str);
}
被观察者实现类
public class BadMan implements Observable {
private ArrayList<Observer> observers = new ArrayList<Observer>();
@Override
public void addObserver(Observer observer) {
this.observers.add(observer);
}
@Override
public void deleteObserver(Observer observer) {
this.observers.remove(observer);
}
@Override
public void notifyObserver(String str) {
for (Observer observer1 : observers) {
observer1.make(str);
}
}
public void run(){
System.out.println("罪犯要逃跑了");
this.notifyObserver("追击罪犯");
}
public void play(){
System.out.println("罪犯在玩");
this.notifyObserver("不动做任何事情,静观其变");
}
}
观察者
public class Police implements Observer {
@Override
public void make(String str) {
System.out.println("police(警察)开始行动");
System.out.println("--------"+str);
}
}
public class Soldier implements Observer {
@Override
public void make(String str) {
System.out.println("军官(soldier)开始行动");
System.out.println("====="+str);
}
}
测试类
public class Test {
public static void main(String[] args) {
// 创建被观察者
BadMan bm = new BadMan();
// 创建观察者
Police gm = new Police();
Soldier gm2 = new Soldier();
// 向被观察者中添加观察者
bm.addObserver(gm);
bm.addObserver(gm2);
// 等待罪犯触发某些行为
bm.run();
}
}
java.util 包下有提供 Observer
接口、Observable
类
在 Spring 中,观察者模式被理解为 “事件驱动”,通过对传统模式的理解进行细化拆分
ApplicationEvent
接口,交给子类在不同的事件情况时去执行自己内部的逻辑ApplicationListener
接口,重写 onApplicationEvent 方法,方法内部通过不同的事件【ApplicationEvent】类型进行调用ApplicationEventMulticaster 子类 SimpleApplicationEventMulticaster
进行事件广播操作publishEvent(new ContextRefreshedEvent(this))
发布事件,比如 AbstractApplicationContext 就是指的事件源,它实现了 ApplicationEventPublisher 接口,可以调用 publishEvent 方法,类图如下事件驱动的准备工作如下:
onApplicationEvent
方法进行相关的逻辑处理工作Spring 相关的源码
初始化事件监听多路广播器
protected void initApplicationEventMulticaster() {
// 获取当前bean工厂,一般是DefaultListableBeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 判断容器中是否存在bdName为applicationEventMulticaster的bd,也就是说自定义的事件监听多路广播器,必须实现ApplicationEventMulticaster接口
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
// 如果有,则从bean工厂得到这个bean对象
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
// 如果没有,则默认采用 SimpleApplicationEventMulticaster
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
默认采用的是 SimpleApplicationEventMulticaster 多路广播器,它的父类 AbstractApplicationEventMulticaster
内部类专门使用了一个集合来存储所有加载的监听器
// 创建监听器助手类,用于存放应用程序的监听器集合,参数是否是预过滤监听器为false
private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
private class ListenerRetriever {
// ApplicationListener 对象集合
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
// BeanFactory 中的 applicationListener 类型 Bean 名集合
// 在设置监听器时会通过 beanName 去获取监听器,再放入 applicationListeners 集合中
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
.......
}
注册监听器的源码:在所有注册的 bean 中查找监听器,将监听器注册到消息广播器中
protected void registerListeners() {
// 遍历应用程序中存在的监听器集合,并将对应的监听器添加到监听器的多路广播器中
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// 从容器中获取所有实现了ApplicationListener接口的bd的bdName
// 放入 ApplicationListenerBeans 集合中
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
// 可以直接获取监听器实例进去,无须在取用的时候再次 getBean getApplicationEventMulticaster().addApplicationListener(this.getBean(listenerBeanName,ApplicationListener.class));
}
// 此处先发布早期的监听器集合
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
事件驱动的逻辑执行过程如下:
首先加载 META-INF/spring.factories
文件中的 key:ApplicationListener,value:全限定类名
,然后获取到所有的实例存入 ApplicationContext 中,方便后续创建多播器对象时可以获取到这些监听器实例,注入:ApplicationContext 实例以后,就可以调用它来进行事件发布动作
通过观察以上内置监听器的源码,可以发现在这些监听器里都实现了 ApplicationListener
同时会使用到抽象类 ApplicationEvent
,但这些内置监听器中的事件都是基于它抽象的实现,定义自己的事件类
事件驱动机制是基于观察者设计模式的实现
,通过 ApplicationEvent 类、ApplicationListener 接口,可以通过 ApplicationContext 实现事件的发布
如下图整理了 SpringBoot 内置的监听器和事件:
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
前戏: 在事件准备发布时,首先会通过 getRunListeners 方法来获取我们在 META-INF/spring.factories
文件中的定义 SpringApplicationRunListener 接口实现类 EventPublishingRunListener
,同时会完成该类的实例化操作,调用构造方法时,会初始化多路广播器对象:SimpleApplicationEventMulticaster
,同时从上下文获取到前面加载好的 11 个监听器,进行绑定操作.
前戏工作做完以后,就到了发布事件的时候了
SimpleApplicationEventMulticaster#multicastEvent:在容器启动时会调用 listener#starting
方法(事件名:ApplicationStartingEvent)、环境对象准备前会调用 listener#environmentPrepared
(事件名:ApplicationEnvironmentPreparedEvent)
以 ConfigFileApplicationListener 为例,会在其内部处理配置文件的解析工作,接受 ApplicationEnvironmentPreparedEvent 事件的处理,源码如下:
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event);
}
if (event instanceof ApplicationPreparedEvent) {
this.onApplicationPreparedEvent(event);
}
}
通过几个不同类型自定义事件案例来加深对事件驱动机制的理解
先创建一个自定义监听器,来监听所有的事件;创建一个 Java 类,实现 ApplicationListener 接口在泛型中指定要监听的事件类型即可,如果要监听所有的事件,那么泛型就写 ApplicationEvent
public class MySpringApplicationListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("自定义监听器--->" + event);
}
}
之后为了在容器启动中能够发现我们的监听器并且添加到 SimpleApplicationEventMulticaster 中,我们需要在 spring.factories/META-INF
中注册自定义的监听器
org.springframework.context.ApplicationListener=\
com.vnjohn.demo.listener.MySpringApplicationListener
这样当我们启动服务的时候就可以看到相关事件发布,我们的监听器被触发了,会打印对应的信息
如果是监听特定的事件,我们只需要在泛型出指定类型即可
public class MySpringApplicationStartingListener implements ApplicationListener<ApplicationStartingEvent> {
@Override
public void onApplicationEvent(ApplicationStartingEvent event) {
// 该事件是启动启动时就会进行发布的,查询容器启动日志信息即可
System.out.println("MySpringApplicationStartingListener--------->" + event);
}
}
org.springframework.context.ApplicationListener=\
com.vnjohn.demo.listener.MySpringApplicationListener,\
com.vnjohn.demo.listener.MySpringApplicationStartingListener
启动服务时可以看到相关的事件发布
若我们想要通过自定义的监听器来监听自定义的事件呢?首先创建自定义的事件类,非常简单,只需要继承 ApplicationEvent 即可
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
然后在自定义的监听器中监听自定义的事件
public class MyCustomerEventListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("MyCustomerEventListener ---》自定义事件触发" + event);
// 触发对应的事件后 业务处理
new Thread(()->{
// 业务....
}).start();
}
}
事件的监听和发布是同步执行的,如果想让其异步的进行,可以抛给一个线程进行处理
org.springframework.context.ApplicationListener=\
com.vnjohn.demo.listener.MySpringApplicationListener,\
com.vnjohn.demo.listener.MySpringApplicationStartingListener,\
com.vnjohn.demo.listener.MyCustomerEventListener
之后我们就可以在我们特定的业务场景中类发布对应的事件了
@Component
public class MyApplicationContextAware implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
MyApplicationContextAware.applicationContext = applicationContext;
}
public static void publishEvent(ApplicationEvent applicationEvent) {
applicationContext.publishEvent(applicationEvent);
}
}
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
MyApplicationContextAware.publishEvent(new MyEvent(new Object()));
return "hello---";
}
}
当提交请求后,对应的监听器就触发了,这样一来不光搞清楚了 SpringBoot 中的监听机制,而且也可以扩展使用到我们业务开发中了
希望对你有所帮助,您的支持是对我最大的鼓励,关注+赞+收藏三连,感谢!
如果觉得博文不错,关注我 vnjohn,后续会有更多实战、源码、架构干货分享!
大家的「关注❤️ + 点赞 + 收藏⭐」就是我创作的最大动力!谢谢大家的支持,我们下文见!