《Spring系列》第18章 监听器Listener

前言

陆续阅读了Spring Boot源码中,有不少地方都用到了监听器。每次看到监听器的代码都一头雾水,不懂其中的设计理念,这次决定肝一篇监听器的博文。

一、监听器

1.概述

何为监听器?就是用来监听程序执行的。监听器可以做什么事?可以监听程序执行,使程序在不同的阶段做不同的事情,并且与程序主业务逻辑解耦。

那么读者读完是否很迷茫?如果学过MQ的可以把想象成一种订阅-发布模式,监听器首先订阅事件,然后其它功能主动发布事件,已触发监听器订阅的事件。

举个例子:最常见的购物逻辑中,下订单以后就要扣库存,可以理解为扣库存这个操作监听了订单,这就是一种监听器,当下了订单,此时触发监听器,执行扣库存操作。有没有觉得监听器更像是订阅-发布模式

可能读完这个例子,大家觉得很难理解,这种下订单、扣库存,我在代码里面按照逻辑依次写下去不就完了,为啥要整的这么麻烦?

个人理解:Spring提供的监听器,其实是一种设计模式,目的是为了更好的扩展性,例如像上面的下订单扣库存,肯定代码直接写最省事,但这样就耦合度高了,其它的业务功能都这样写嘛?如果Spring按照这种方式写,那么想扩展功能就只剩改源码了

所以从整体设计的角度,Spring框架为了提供更好的扩展性,采用了观察者模式,设计出了这么一套监听器,以供使用。这样开发人员不仅可以扩展Spring的监听器,也可以自定义新的监听器,一举两得。这样得以让代码更加优雅,便于扩展
《Spring系列》第18章 监听器Listener_第1张图片

2.组成

Spring的监听器由以下3部分组成:

  1. 事件(ApplicationEvent):要广播,发送的消息. 监听器监听的事情
  2. 监听器(ApplicationListener): 观察者模式中的观察者, 监听器监听特定事件, 并在内部定义了事件发生后的相应逻辑.
  3. 事件发布器(ApplicationEventMulticaster):对应于观察者模式中的被观察者/主题.负责通知观察者. 对外提供发布事件和增删事件监听器的接口.维护事件和事件监听器之间的关系.并在事件发生时负责通知事件监听器.

可能看到这3个词觉得很难理解,那么换一种解释:

  1. 事件:负责调用逻辑,对应订阅-发布中的订阅
  2. 监听器:负责执行逻辑,也就是你的自定义逻辑
  3. 事件发布器:负责根据事件类型去调用你的自定义逻辑,底层就是一个循环,根据你传过来的事件类型,遍历调用对应的监听器

3.Hello World

上面介绍到的例子:下单后减库存. 减库存就是一个事件, 这个事件需要一个事件播放器, 将事件播放出去. 然后另一端事件监听器, 接收到信息,进行处理。简单写一下

第1步:创建一个订单实体类】

@Data
public class Order {

    private String id;

    private String name;

}

第2步:创建一个事件,继承ApplicationEvent

// 继承ApplicationEvent必须重写构造方法
public class OrderEvent extends ApplicationEvent {

    // 一般在调用的时候会把this传进来,也就是当前类
    public OrderEvent(Object source) {
        super(source);
    }

    // 可以在this的基础上,继续增加所需的参数
    public OrderEvent(Object source, Order order) {
        super(source);
	    System.out.println("创建了事件-----");
    }

}

第3步:创建监听器,实现ApplicationListener

@Component
public class OrderEventListener implements ApplicationListener<OrderEvent> {

    @Override
    public void onApplicationEvent(OrderEvent event) {
        // 参数event可以接收传过来的参数,一般就是上一步逻辑处理的结果,也可以不传
        System.out.println("触发了事件----------");
    }

}

第4步:启动类】

@Configuration
@ComponentScan
public class TestMain {

    @Test
    public void m1() {
        // 1.创建IOC容器
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(TestMain.class);

        // 2.创建订单
        Order order = new Order();
        order.setId("订单号:i4u1212412312");
        order.setName("商品名称:T恤");
        System.out.println("订单创建完毕---" + order);

        // 3.发布事件, 这里会借助事件分发器去调用监听器
        ctx.publishEvent(new OrderEvent(this, order));
        System.out.println("结束了-------");
    }

}

写完以后,仔细思考一下,就会发现其实就是A调用B,但是Spring把A叫做事件,B叫做监听器,然后调用由事件分发器执行,解耦合

4.分类

上面简单介绍了监听器,那么监听器有哪些呢?两种类型:一种是内置监听器, 一种是自定义监听器

1) 内置监听器

Spring定义了一个内置监听器的父类ApplicationContextEvent,实现该类的就是内置监听器

《Spring系列》第18章 监听器Listener_第2张图片

一共有4类实现了ApplicationContextEvent

Event 说明
ContextRefreshEvent 当容器被实例化或者refresh时发布.如调用refresh()方法. 此处的实例化是指所有的bean都已被加载,后置处理器都被激活,所有单例bean都已被实例化,所有的容器对象都已经准备好可使用. 如果容器支持热重载,则refresh()可以被触发多次(XmlWebApplicationContext支持热刷新, 而GenericApplicationContext不支持热刷新)
ContextStartedEvent 当容器启动时发布, 即调用start()方法, 已启用意味着所有的lifecycle都已显示收到了start的信号
ContextStoppedEvent 当容器停止时发布. 即调用stop()方法, 既所有的lifecycle bean都已显示接收了stop信号, 关闭的容器可以通过start()方法重启
ContextClosedEvent 当容器关闭时发布. 即调用close()方法, 关闭意味着所有的单例bean都已被销毁. 关闭的容器不能被重启或refresh()

总结:这些内置监听器事件是Spring框架自己使用的,以供扩展Spring源码可以使用,例如:我想在Spring初始化完成以后执行某个操作,可以自定义监听器类,监听ContextRefreshEvent事件,然后执行自定义逻辑

在简单点说,这些是Spring框架初始化默认发布的事件,无法更改事件,但你可以监听执行自定义逻辑,当然也可以自定义事件

举个例子】可以直接创建一个自定义监听器,监听ContextRefreshedEvent事件,这样当Spring容器初始化完成,就会执行这个监听器的逻辑

@Component
public class ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> {

    private String name = "qqweqwe123";

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("所有的bean初始化完成了");
    }
}

2) 自定义监听器

自定义监听器,其实上面的例子已经写过1遍了,那么这里介绍一下实现的2种方式:

方式1:基于接口】

@Component
public class HelloEventListener implements ApplicationListener<OrderEvent> {

  @Override
  public void onApplicationEvent(OrderEvent event) {
      if (event.getName().equals("减库存")) {
        System.out.println("减库存....");
      }
  }
}

方式2:基于注解】

@Component
public class OrderEventListener {

  @EventListener(OrderEvent.class)
  public void onApplicationEvent(OrderEvent event) {
    if (event.getName().equals("减库存")) {
      System.out.println("减库存....");
    }
  }
}

二、源码分析

1.初始化

监听器的初始化,都是在refresh()中,那么下面就来看一下,跟监听器有关的类:
《Spring系列》第18章 监听器Listener_第3张图片

1) initApplicationEventMulticaster()

初始化一个监听器多播器,默认是SimpleApplicationEventMulticaster,它负责将事件分发到监听器

protected void initApplicationEventMulticaster() {
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
      this.applicationEventMulticaster =
            beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
      if (logger.isTraceEnabled()) {
         logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
      }
   }
   else {
      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() + "]");
      }
   }
}

2) registerListeners()

注册监听器,来源分为3步:

  1. 静态指定的。也就是META-INF/spring.factories中配置的
  2. 容器中获取。包括:引入其它依赖携带的监听器 + 自定义开发的监听器
  3. 把早期的监听器给注入进来,很少用到
protected void registerListeners() {
   // Register statically specified listeners first.
   for (ApplicationListener<?> listener : getApplicationListeners()) {
      getApplicationEventMulticaster().addApplicationListener(listener);
   }

   // Do not initialize FactoryBeans here: We need to leave all regular beans
   // uninitialized to let post-processors apply to them!
   String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
   for (String listenerBeanName : listenerBeanNames) {
      getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
   }

   // Publish early application events now that we finally have a multicaster...
   Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
   this.earlyApplicationEvents = null;
   if (earlyEventsToProcess != null) {
      for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
         getApplicationEventMulticaster().multicastEvent(earlyEvent);
      }
   }
}

3) finishRefresh()

protected void finishRefresh() {
   // Clear context-level resource caches (such as ASM metadata from scanning).
   clearResourceCaches();

   // Initialize lifecycle processor for this context.
   initLifecycleProcessor();

   // Propagate refresh to lifecycle processor first.
   getLifecycleProcessor().onRefresh();

   // Publish the final event.
   // 发布一个事件
   publishEvent(new ContextRefreshedEvent(this));

   // Participate in LiveBeansView MBean, if active.
   LiveBeansView.registerApplicationContext(this);
}

4) 总结

经过以上3步,首先事件发布器有了,默认SimpleApplicationEventMulticaster,监听器有了,已经注入进去了,剩下的就是通过事件发布器,来触发监听器

2.发布

初始化完毕以后,接下来就是使用了,当发布事件的时候,如何执行呢?

先说原理:通过publishEvent()发布事件以后,底层就会根据事件类型,循环判断所有的监听器,找到符合类型的,然后执行。

1) publishEvent() 入口

事件发布,然后交由时间发布器执行

// AbstractApplicationContext.java


@Override
public void publishEvent(ApplicationEvent event) {
   publishEvent(event, null);
}

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
   Assert.notNull(event, "Event must not be null");

   // Decorate event as an ApplicationEvent if necessary
   ApplicationEvent applicationEvent;
   if (event instanceof ApplicationEvent) {
      applicationEvent = (ApplicationEvent) event;
   }
   else {
      applicationEvent = new PayloadApplicationEvent<>(this, event);
      if (eventType == null) {
         eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
      }
   }

   // Multicast right now if possible - or lazily once the multicaster is initialized
   if (this.earlyApplicationEvents != null) {
      this.earlyApplicationEvents.add(applicationEvent);
   }
   else {
      // 【获取事件分布器,发布事件】
      getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
   }

   // Publish event via parent context as well...
   if (this.parent != null) {
      if (this.parent instanceof AbstractApplicationContext) {
         ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
      }
      else {
         this.parent.publishEvent(event);
      }
   }
}

2) 发布事件

获取到匹配的监听器,然后循环执行

// SimpleApplicationEventMulticaster.java

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   Executor executor = getTaskExecutor();
   // 【获取到匹配的监听器,然后遍历】
   for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      if (executor != null) {
         executor.execute(() -> invokeListener(listener, event));
      }
      else {
         // 【执行监听器】
         invokeListener(listener, event);
      }
   }
}

3) 获取匹配的监听器

从全部的监听器中,遍历循环,找到支持该事件的监听器,这里代码只展示了大概,具体没有展开,大体就是一个for循环+if判断

protected Collection<ApplicationListener<?>> getApplicationListeners(
      ApplicationEvent event, ResolvableType eventType) {

   Object source = event.getSource();
   Class<?> sourceType = (source != null ? source.getClass() : null);
   ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

   // Quick check for existing entry on ConcurrentHashMap...
   ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
   if (retriever != null) {
      return retriever.getApplicationListeners();
   }

   if (this.beanClassLoader == null ||
         (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
               (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
      // Fully synchronized building and caching of a ListenerRetriever
      synchronized (this.retrievalMutex) {
         retriever = this.retrieverCache.get(cacheKey);
         if (retriever != null) {
            return retriever.getApplicationListeners();
         }
         retriever = new ListenerRetriever(true);
         // 【这里会循环所有的监听器,根据 supportsEventType() 来判断是否支持该事件类型】
         Collection<ApplicationListener<?>> listeners =
               retrieveApplicationListeners(eventType, sourceType, retriever);
         this.retrieverCache.put(cacheKey, retriever);
         return listeners;
      }
   }
   else {
      // No ListenerRetriever caching -> no synchronization necessary
      return retrieveApplicationListeners(eventType, sourceType, null);
   }
}

4) 执行监听器

接下来就是执行监听器了,看到invoke就知道了,反射调用对应的方法即可

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
   ErrorHandler errorHandler = getErrorHandler();
   if (errorHandler != null) {
      try {
         doInvokeListener(listener, event);
      }
      catch (Throwable err) {
         errorHandler.handleError(err);
      }
   }
   else {
      // 【执行】
      doInvokeListener(listener, event);
   }
}

接下来就是执行监听器了,看到invoke就知道了,反射调用对应的方法即可

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
   ErrorHandler errorHandler = getErrorHandler();
   if (errorHandler != null) {
      try {
         doInvokeListener(listener, event);
      }
      catch (Throwable err) {
         errorHandler.handleError(err);
      }
   }
   else {
      // 【执行】
      doInvokeListener(listener, event);
   }
}

你可能感兴趣的:(spring,java,数据库)