目录
  • 前言
  • 需求背景
  • 事件概念
    • 定义
    • 组成
  • 事件实现
    • 时序图

      前言

      前段时间因为工作的需要用到Spring事件,翻翻文档将功能实现了,但是存在少许理解不畅的地方,今天有空来梳理梳理。

      需求背景

      叶子同学在新入职公司,老大让他实现登陆功能,叶子随手写完,上线无bug,一切安好

      //登陆伪代码
      public void login(....){
          userLogin(....);
      }

      几天之后,老大说为维护用户的粘度,每天登陆送积分。叶子同学,二话不说,一顿操作后,上线无bug,一切安好

      //登陆伪代码
      public void login(....){
          //登陆
          userLogin(....);
          //送积分
          loginPoint(....)
      }

      又几天后,老大说,为了客户安全,每次异地登陆发送邮件。叶子同学稍微抱怨,看在钱份上又是一顿操作后,上线无bug, 一切安好

      //登陆伪代码
      public void login(....){
          //登陆
          userLogin(....);
          //送积分
          loginPoint(....)
          //发送邮件
          sendEmail(....)
      }

      又又几天后,老大说,部分客户不用邮件,用短信。叶子同学压着怒气,看着银行卡,又是一顿操作后,上线无bug, 一切安好

      //登陆伪代码
      public void login(....){
          //登陆
          userLogin(....);
          //送积分
          loginPoint(....)
          //发送邮件
          sendEmail(....)
          //发短信
          sendSms(...)
      }

      又又又几天后,老大还没开口,叶子同学就忍无可忍啦,得加钱。老大哄了好久,说不改需求了。改bug,用户抱怨登陆慢,有时还不成功。叶子二话不说,接手排查,查出问题啦

      • 1> 邮件发送耗时
      • 2>同步实现,如果邮件发送超时,登陆会出异常

      代码改进:

      //登陆伪代码
      public void login(....){
          //登陆
          userLogin(....);
       
          try{
              //送积分
              new Thread(()->loginPoint(....)).start();
          
              //发送邮件
              new Thread(()->sendEmail(....)).start();
       
              //发短信
              new Thread(()->sendSms(....)).start();
          }catch(Exception e){
             //异常处理
          }
      }

      问题解决,功能实现,ok~

      又又又又几天之后,老大说,只需要实现登陆功能即可,其他都不要~

      叶子同学一句卧槽,然后是一段含母非常高的国粹。

      此时,问:如果你是叶子同学,你有啥方案能优雅应对上面的需求变更呢?

      一种优雅方案:事件监听机制

      事件概念

      定义

      事件监听机制:就是对一个事件(行为动作)进行监听,当外界触发某事件时,监听程序马上被捕获该事件,并触发相应的响应,这过程称之为事件监听机制。

      组成

      事件监听机制有3个核心组成部分:

      • 1>事件,标记某种行为动作,比如:鼠标点击事件,鼠标移动事件等。
      • 2>事件源,被监控的对象或组件,事件发生的地方。比如:点击按钮,触发点击事件,按钮就是实现源。
      • 3>事件监听器,监听事件的操作类,一旦事件发生(被触发),则执行事件监听器预设的逻辑,进行事件响应。

      Java Spring 事件监听详情解析

      • 1>定义事件,并绑定到事件源中
      • 2>定义事件监听器,监听事件源
      • 3>用户某行为触发事件
      • 4>事件监听器监控到事件发送,执行事件响应逻辑。

      以上面的登录为例:

      • 事件源:login方法
      • 事件:用户login行为
      • 事件监听器:此处没有,需要额外定制,但是事件响应:送积分,发邮件,发短信。

      事件实现

      以登录为例子实现事件监听机制

      1>定义抽象事件

      /**
       * 抽象事件类
       * 作用:定制事件逻辑
       */
      public class AbstractEvent {
          //绑定的事件源
          private Object source;
          public AbstractEvent(Object source) {
              this.source = source;
          }
          public Object getSource() {
              return source;
          }
          public void setSource(Object source) {
              this.source = source;
          }
      }

      2>定制登陆事件

       
      /**
       * 登陆事件
       */
      public class LoginEvent extends AbstractEvent{
          public LoginEvent(Object source) {
              super(source);
          }
      }

      3>定义事件监听器

      /**
       * 事件监听器
       * 作用:当监控的事件发送时,执行预设的逻辑
       */
      public interface EventListener<E extends AbstractEvent> {
       
          /**
           * 预设逻辑方法
           * 事件被触发,马上执行
           */
          void onEvent(E event);
      }

      4>定制登陆事件监听器

      积分监听器

      /**
       * 加积分监听器器:
       * 当用户登陆事件触发后,马上执行
       */
      public class PointsListener implements EventListener<LoginEvent> {
          public void onEvent(LoginEvent event) {
              System.out.println(event.getSource() + "发生后,执行积分+1操作");
       
              System.out.println(Thread.currentThread().getName());
          }
      }

      短信监听器

      /**
       * 加积分监听器器:
       * 当用户登陆事件触发后,马上执行
       */
      public class SmsListener implements EventListener<LoginEvent> {
          public void onEvent(LoginEvent event) {
              System.out.println(event.getSource() + "发生后,执行发送短信操作");
              System.out.println(Thread.currentThread().getName());
          }
      }

      邮件监听器

      /**
       * 加积分监听器器:
       * 当用户登陆事件触发后,马上执行
       */
      public class EmailListener implements EventListener<LoginEvent> {
          public void onEvent(LoginEvent event) {
              try {
                  //模拟10s延时
                  Thread.sleep(10000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println(event.getSource() + "发生后,执行发送邮件操作");
              System.out.println(Thread.currentThread().getName());
          }
      }

      5>定义事件广播器

      /**
       * 事件广播器
       * 1>注册事件监听器
       * 2>删除事件监听器
       * 3>事件触发时,广播事件
       */
      public interface EventMulticaster {
          //广播事件
          void multicastEvent(AbstractEvent event);
      
          //注册事件监听器
          void registListener(EventListener listener);
      
          //删除事件监听器
          void removeListener(EventListener listener);
      }

      6>定制简单的事件广播器 

      /**
       * 事件广播器实现类
       * 作用:维护事件监听器
       */
      public class SimpleEventMulticaster implements EventMulticaster {
          //key:事件字节码对象, value:当前事件绑定的事件监听器
          private Map<Class<?>, List<EventListener>> map = new HashMap<Class<?>, List<EventListener>>();
       
          public void multicastEvent(AbstractEvent event) {
              List<EventListener> eventListeners = map.get(event.getClass());
              if(eventListeners != null){
                  ExecutorService executorService = Executors.newCachedThreadPool();
                  for (EventListener eventListener : eventListeners) {
                      //异步
                      executorService.submit(()-> eventListener.onEvent(event));
                      //同步
                      //eventListener.onEvent(event);
                  }
                  executorService.shutdown();
              }
          }
          public void registListener(EventListener listener) {
              //获取监听器绑定的事件
              ParameterizedType getType  = (ParameterizedType)listener.getClass().getGenericInterfaces()[0];
              Type type = getType.getActualTypeArguments()[0];
              Class<?> clz = (Class<?>) type;
       
              List<EventListener> listeners = map.get(clz);
              if(listeners == null){
                  listeners = new ArrayList<EventListener>();
                  map.put(clz, listeners);
              }
              listeners.add(listener);
          }
          public void removeListener(EventListener listener) {
              //获取监听器绑定的事件
              ParameterizedType getType  = (ParameterizedType)listener.getClass().getGenericInterfaces()[0];
              Type type = getType.getActualTypeArguments()[0];
              Class<?> clz = (Class<?>) type;
       
              List<EventListener> listeners = map.get(clz);
              if(listener != null){
                  listeners.remove(listener);
              }
       
          }
      }
      

      7>综合测试

      public class App {
       
          //1:初始化事件广播器
          public static SimpleEventMulticaster multicaster = new SimpleEventMulticaster();
          static {
              //2:注册监听器
              //登陆事件上绑定3个监听器
              multicaster.registListener(new PointsListener());
              multicaster.registListener(new EmailListener());
              multicaster.registListener(new SmsListener());
          }
          //3:模拟登陆
          public static void login(){
              //4:用户登陆成功触发登陆事件
              System.out.println("用户执行登陆逻辑");
              System.out.println(Thread.currentThread().getName());
              //5:广播登陆事件
              multicaster.multicastEvent(new LoginEvent("用户登陆啦"));
              System.out.println("登陆成功.....");
          }
          public static void main(String[] args) {
              App.login();
              System.out.println(Thread.currentThread().getName());
          }
      }

      时序图

      Java Spring 事件监听详情解析

      声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。