【daisy-framework】事件监听(event,listener)

绝地灬酷狼 2023-02-12 05:40 137阅读 0赞

前言

Github:https://github.com/yihonglei/daisy-framework/tree/master/daisy-springboot-framework(daisy工程)

一 事件监听

Java很多web框架源码都能看到很多事件监听的存在,主要解决的问题就是类似观察者模式的行为,

当一件事情发生的时候,其他的相关事情需要知道并处理。

事件监听三要素: 事件源,事件对象,事件监听器。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lobF9qeHk_size_16_color_FFFFFF_t_70

1、监听器需要注册,即注册到某个数据结构,map或list等等存储起来;

2、一件事情的发生源头,这件事情需要触发监听器,让监听器能感受到,监听器才能去处理业务;

3、调用每一个监听器,执行相关处理;

要理解监听器,需要理解几个问题,监听器存储在什么数据结构?监听器如何被触发?监听器如何处理业务?

上一个代码吧,要不然太蒙了!

二 实例代码

demo基于spring boot环境。

1、自定义事件

Java自定义事件实现EventObject对象。

  1. package com.jpeony.common.event;
  2. import lombok.Data;
  3. import lombok.EqualsAndHashCode;
  4. import java.util.EventObject;
  5. /**
  6. * 自定义事件
  7. *
  8. * @author yihonglei
  9. */
  10. @EqualsAndHashCode(callSuper = true)
  11. @Data
  12. public class DaisyEvent extends EventObject {
  13. DaisyEventEnum event;
  14. Object[] args;
  15. /**
  16. * 事件对象
  17. *
  18. * @param daisyEventEnum 事件类型
  19. * @param source 触发事件初始化对象
  20. * @param args 其他参数
  21. * @author yihonglei
  22. */
  23. public DaisyEvent(DaisyEventEnum daisyEventEnum, Object source, Object... args) {
  24. super(source);
  25. this.event = daisyEventEnum;
  26. this.args = args;
  27. }
  28. /**
  29. * 事件对象
  30. *
  31. * @param daisyEventEnum 事件
  32. * @param source 触发事件初始化对象
  33. * @author yihonglei
  34. */
  35. public DaisyEvent(DaisyEventEnum daisyEventEnum, Object source) {
  36. this(daisyEventEnum, source, null);
  37. }
  38. }

DaisyEventEnum事件类型,args事件参数。

2、定义事件类型

定义事件类型主要是对可以针对事件类型进行发布。

  1. package com.jpeony.common.event;
  2. /**
  3. * 事件枚举
  4. *
  5. * @author yihonglei
  6. */
  7. public enum DaisyEventEnum {
  8. /**
  9. * 初始化
  10. */
  11. INIT_EVENT,
  12. /**
  13. * 测试事件
  14. */
  15. TEST_EVENT;
  16. }

3、事件监听器

事件监听器继承EventListener,定义事件发布的标准方法。

  1. package com.jpeony.common.event;
  2. import java.util.EventListener;
  3. /**
  4. * 自定义事件监听,Spring的ApplicationListener也是这么干的,自定义方便自己对事件进行管理。
  5. * 注意,这里的事件是同步的,如果有需要,可以将事件提交到线程池异步化执行或者使用Spring的异步事件方式,
  6. * 它也是提交到线程池实现异步化。
  7. *
  8. * @author yihonglei
  9. */
  10. public interface DaisyEventListener extends EventListener {
  11. /**
  12. * 触发事件
  13. *
  14. * @param e 相关事件
  15. */
  16. void fire(DaisyEvent e);
  17. }

event1

  1. package com.jpeony.core.service.event;
  2. import com.jpeony.common.event.DaisyEvent;
  3. import com.jpeony.common.event.DaisyEventEnum;
  4. import com.jpeony.common.event.DaisyEventListener;
  5. import com.jpeony.common.event.DaisyEventManager;
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.springframework.stereotype.Component;
  8. /**
  9. * 实现daisyEventListener接口,在构造方法添加你的事件即可!
  10. *
  11. * @author yihonglei
  12. */
  13. @Slf4j
  14. @Component
  15. public class Test1EventListener implements DaisyEventListener {
  16. public Test1EventListener() {
  17. DaisyEventManager.addListener(DaisyEventEnum.TEST_EVENT, this);
  18. }
  19. @Override
  20. public void fire(DaisyEvent e) {
  21. try {
  22. String userName = (String) e.getSource();
  23. log.info("【Test1】对发布事件,事件触发,事件参数测试!userName={}", userName);
  24. } catch (Exception ex) {
  25. log.error("出异常了!", ex);
  26. }
  27. }
  28. }

event2

  1. package com.jpeony.core.service.event;
  2. import com.jpeony.common.event.DaisyEvent;
  3. import com.jpeony.common.event.DaisyEventEnum;
  4. import com.jpeony.common.event.DaisyEventListener;
  5. import com.jpeony.common.event.DaisyEventManager;
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.springframework.stereotype.Component;
  8. /**
  9. * 实现daisyEventListener接口,在构造方法添加你的事件即可!
  10. *
  11. * @author yihonglei
  12. */
  13. @Slf4j
  14. @Component
  15. public class Test2EventListener implements DaisyEventListener {
  16. public Test2EventListener() {
  17. DaisyEventManager.addListener(DaisyEventEnum.TEST_EVENT, this);
  18. }
  19. @Override
  20. public void fire(DaisyEvent e) {
  21. try {
  22. String userName = (String) e.getSource();
  23. log.info("【Test2】对发布事件,事件触发,事件参数测试!userName={}", userName);
  24. } catch (Exception ex) {
  25. log.error("出异常了!", ex);
  26. }
  27. }
  28. }

spring boot启动时注册到时间管理器DaisyEventManager(下面有源码)。

4、事件管理器

负责事件的注册,事件触发。

  1. package com.jpeony.common.event;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import java.util.EnumMap;
  5. import java.util.concurrent.CopyOnWriteArrayList;
  6. /**
  7. * 事件管理器
  8. *
  9. * @author yihonglei
  10. */
  11. public class DaisyEventManager {
  12. private static Logger logger = LoggerFactory.getLogger(DaisyEventManager.class);
  13. private static EnumMap<DaisyEventEnum, CopyOnWriteArrayList<DaisyEventListener>> eventListeners = new EnumMap<>(DaisyEventEnum.class);
  14. public static void addListener(DaisyEventEnum event, DaisyEventListener el) {
  15. CopyOnWriteArrayList<DaisyEventListener> listeners = eventListeners.get(event);
  16. if (listeners == null) {
  17. listeners = new CopyOnWriteArrayList<>();
  18. eventListeners.put(event, listeners);
  19. } else {
  20. for (DaisyEventListener listener : listeners) {
  21. if (listener.getClass() == el.getClass()) {
  22. logger.info("more than one instances added, this class is {}", listener.getClass());
  23. return;
  24. }
  25. }
  26. }
  27. listeners.add(el);
  28. }
  29. /**
  30. * 移出指定事件的指定监听器
  31. *
  32. * @author yihonglei
  33. */
  34. public static synchronized void removeListener(DaisyEventEnum event, DaisyEventListener el) {
  35. CopyOnWriteArrayList<DaisyEventListener> listeners = eventListeners.get(event);
  36. if (listeners != null) {
  37. listeners.remove(el);
  38. }
  39. }
  40. /**
  41. * 清除指定事件的所有监听器
  42. *
  43. * @author yihonglei
  44. */
  45. public static void purgeListeners(DaisyEventEnum event) {
  46. CopyOnWriteArrayList<DaisyEventListener> listeners = eventListeners.get(event);
  47. if (listeners != null) {
  48. listeners.clear();
  49. // 移除该事件
  50. eventListeners.remove(event);
  51. }
  52. }
  53. /**
  54. * 触发事件
  55. *
  56. * @author yihonglei
  57. */
  58. public static void fireEvent(DaisyEvent obj) {
  59. CopyOnWriteArrayList<DaisyEventListener> listeners = eventListeners.get(obj.getEvent());
  60. if (listeners == null) {
  61. return;
  62. }
  63. for (DaisyEventListener listener : listeners) {
  64. listener.fire(obj);
  65. }
  66. }
  67. }

spring boot启动的时候,Test1EventListener和Test2EventListener通过构造器的事件管理器注册到EnumMap。

DaisyEventManager.addListener(DaisyEventEnum.TEST_EVENT, this);

EnumMap是一个事件类型对应多个listener的数据结构,Map的key是事件类型,value是监听的list集合。

5、测试

  1. package com.jpeony.test.service.event;
  2. import com.jpeony.common.event.DaisyEvent;
  3. import com.jpeony.common.event.DaisyEventEnum;
  4. import com.jpeony.common.event.DaisyEventManager;
  5. import com.jpeony.test.BaseServletTest;
  6. import org.junit.Test;
  7. /**
  8. * @author yihonglei
  9. */
  10. public class EventTest extends BaseServletTest {
  11. @Test
  12. public void test() {
  13. // 事件发布,支持任意参数,可以是一个值,也可以传入对象
  14. String userName = "Tom";
  15. DaisyEventManager.fireEvent(new DaisyEvent(DaisyEventEnum.TEST_EVENT, userName));
  16. }
  17. }

发布TEST_EVENT事件,event1和event2监听并执行。

20200524184050254.png

DaisyEventManager.fireEvent(new DaisyEvent(DaisyEventEnum.TEST_EVENT, userName));

传入TEST_EVENT,事件管理器根据key为TEST_EVENT取出对应的监听器列表并循环执行。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lobF9qeHk_size_16_color_FFFFFF_t_70 1

6、总结

1)监听事件继承EventObject;

2)监听器继承于EventLisneter,也可以不继承,这个接口只是一个标记接口,然后定义统一的事件发布方法;

3)弄一个事件管理器,不弄也行,我这里是为了方便,你可以在某个类里面搞一个Map或list存这些监听器,

数据结构选取可以根据自己的业务来,总之,要把这些监听器对象存起来;

4)事件执行就比较简单,就是根据事件类型取出相应的监听器,循环执行,完事!

三 Tomcat的事件监听

最近无聊看了下tomcat源码,单独简单聊下tomcat里的事件监听怎么实现的。

1、事件定义

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lobF9qeHk_size_16_color_FFFFFF_t_70 2

你会发现,tomcat生命周期管理的自定义事件也只是继承了EventObject,data事件监听器处理需要的参数,

type就是事件类型。

2、监听器定义

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lobF9qeHk_size_16_color_FFFFFF_t_70 3

刚才我自己实现监听器,标识接口用的Java的EventListener,这个是只是个标记接口,

表明跟我这个EventListener相关的都是要实现监听器,你别的不相关的别瞎搞,只是一个规范而已。

tomcat里面就只是自己定义了事件发布标准,监听器都走LifecycleListener接口标识。

3、监听注册

数据结构

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lobF9qeHk_size_16_color_FFFFFF_t_70 4

最后都是调用add方法,把监听都放在这个list里面。

4、监听触发

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lobF9qeHk_size_16_color_FFFFFF_t_70 5

这个地方也是通过type取到监听list,然后循环触发执行。

四 事件监听本质思想

1、事件自定义,继承EventObject;

2、监听器定义,要统一标识接口和发布方法;

3、监听器注册,搞一个合适的数据结构把你的监听分类存起来;

4、事件触发,根据事件类型,从数据结构取出相应监听器,循环执行;

发表评论

表情:
评论列表 (有 0 条评论,137人围观)

还没有评论,来说两句吧...

相关阅读