spring ApplicationEvent

老子叫甜甜 提交于 2020-05-07 16:19:22

最近在看apollo源码,发现一个自己没怎么玩过的东西,就研究下看看,就是事件的监听。

定义一个事件类MyEvent。

public class MyEvent extends ApplicationEvent {

    public MyEvent(Object source) {
        super(source);
    }

    public EventDto getEventDto() {
        return (EventDto) getSource();
    }

}

定义一个事件传输对象类EventDto,就是相当于一个事件的载体。

@Data
public class EventDto {
    private String name;
    private String content;
}

定义一个监听类MyListener,用于接受处理订阅事件,如果这里有多个需要订阅的相同的事件的类的话,就可以在写一个MyListenerB,然后实现ApplicationListener<MyEvent>即可。

@Component
public class MyListener implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent event) {
        try {
            EventDto eventDto = event.getEventDto();
            Thread.sleep(2000);
            System.out.println(eventDto);
            System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId());
        } catch (Exception ex) {
            System.out.println(ex);
        }
    }
}

然后写示例代码,发布一个事件。

private final ApplicationEventPublisher publisher;

@RequestMapping("/index")
public String index() {
    EventDto eventDto = new EventDto();
    eventDto.setContent("我是content");
    eventDto.setName("我是name");
    MyEvent myEvent = new MyEvent(eventDto);
    publisher.publishEvent(myEvent);
    return "推送数据成功" + Thread.currentThread().getName() + Thread.currentThread().getId();
}

上述这种事件的订阅会发现每搞一个订阅都要重新建立一个类,并继承Listener,下面介绍一种通过注解@EventListener的方式直接来实现监听,把MyListener类进行调整成下述的代码所示即可。如果同一事件订阅多个监听,复制一份,参数不变即可。

@Component
public class MyListener {
    @EventListener
    public void onApplicationEvent(MyEvent event) {
        try {
            EventDto eventDto = event.getEventDto();
            Thread.sleep(2000);
            System.out.println(eventDto);
            System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId());
        } catch (Exception ex) {
            System.out.println(ex);
        }
    }
}

 

事件的应用就到这里结束,下面简单刨析下源码看看。

1、我们通过ApplicationEventPublisher类的publishEvent方法进去看看,他的实现类是AbstractApplicationContext,因此可以直接到这个类中查看。

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);
      }
   }
}

SimpleApplicationEventMulticaster实现类

@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);
      }
   }
}

AbstractApplicationEventMulticaster类

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);
         //重点就在这里,获取到指定事件类型、传递消息对象的类型监听器对象
         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);
   }
}

拿到指定的监听器类型就可以进行调用相应的监听方法了。

使用事件而不直接调用方法,重点是解耦,比如,村里喇叭响,村民A听到喇叭响就去地里干活,如果只有一个村民,那调就调了,但是如果有N多村民呢?因此每个村民订阅这个喇叭事件,听到就去地里干活,如果新增加一个村民就订阅这个事件即可。

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!