事件Event监听相关——spring的事件监听相关

谁借莪1个温暖的怀抱¢ 2024-04-17 19:48 179阅读 0赞

应用场景:

事件驱动模型简介

事件驱动模型也就是我们常说的观察者,或者发布-订阅模型。理解它的几个关键点:

1、首先是对象间的一对多的关系;最简单的如交通信号灯,信号灯是目标(一方),行人注视着信号灯(多方);

2、当目标发送改变(发布),观察者(订阅者)就可以接收到改变;

3、观察者如何处理(如行人如何走,是快走/慢走/不走,目标不会管的),目标无需干涉;所以就松散耦合了它们之间的关系。

比分最常见的用户注册之后可能需要做很多操作:

1、加积分

2、发优惠券

3、创建初始化数据等…………

问题:

UserService和其他Service耦合严重,增删功能比较麻烦;
有些功能可能需要调用第三方系统,如增加积分/索引用户,速度可能比较慢,此时需要异步支持。

所以需要解耦这些Service之间的依赖关系。事件监听就是一种最佳的解决方案。

如何实现呢?面向接口编程(即面向抽象编程),而非面向实现。即事件和动作可以定义为接口,这样它俩的依赖是最小的。

在Java中接口还一个非常重要的好处:接口是可以多实现的,类/抽象类只能单继承,所以使用接口可以非常容易扩展新功能。

一、最简单基本的事件机制:

java中的事件机制的参与者有3种角色:

1.event object:事件状态对象,集成java.util.EventObject。用于listener的相应的方法之中,作为参数,一般存在于listerner的方法之中。

2.event listener:对每个明确的事件的发生,都相应地定义一个明确的Java方法。这些方法都集中定义在事件监听者(EventListener)接口中,这个接口要实现 java.util.EventListener。 实现了事件监听者接口中一些或全部方法的类就是事件监听者。

3.event source:具体的事件源,比如说,注册事件,值改变事件,事件源就是event source,要想对这些事件进行响应,就需要注册特定的listener。

示例代码:

1、被监控值改变的对象:

  1. package my.mark.mybaibaoxiang.eventDemo.easyEvent;
  2. /**
  3. * @author twotiger-wxm.
  4. * @date 2019-9-11.
  5. */
  6. public class Door {
  7. /**==================单例模式begin=================*/
  8. private final static Door INSTANCE = new Door(new DoorStateListener(),new DoorNameListener());
  9. //私有化构造函数,不让外部调用。
  10. private Door(){}
  11. private Door(DoorStateListener stateListener,DoorNameListener nameListener){
  12. this.stateListener= stateListener;
  13. this.nameListener= nameListener;
  14. }
  15. public static Door getInstance(){
  16. return INSTANCE;
  17. }
  18. /**==================单例模式end=================*/
  19. private String state = "";
  20. private String name = "";
  21. private DoorStateListener stateListener;
  22. private DoorNameListener nameListener;
  23. public void setState(String newValue) {
  24. if (state != newValue) {
  25. state = newValue;
  26. if (stateListener != null){
  27. //注意参数传递
  28. DoorEvent event = new DoorEvent(this, "state",newValue);
  29. stateListener.doorEvent(event);
  30. }
  31. }
  32. }
  33. public void setName(String newValue) {
  34. if (name != newValue) {
  35. name = newValue;
  36. if (nameListener != null){
  37. DoorEvent event = new DoorEvent(this,"name", newValue);
  38. nameListener.doorEvent(event);
  39. }
  40. }
  41. }
  42. public void setStateListener(DoorStateListener stateListener) {
  43. this.stateListener = stateListener;
  44. }
  45. public void setNameListener(DoorNameListener nameListener) {
  46. this.nameListener = nameListener;
  47. }
  48. public String getState() {
  49. return state;
  50. }
  51. public String getName() {
  52. return name;
  53. }
  54. }

2、事件传递对象

  1. package my.mark.mybaibaoxiang.eventDemo.easyEvent;
  2. import java.util.EventObject;
  3. /**
  4. * @author twotiger-wxm.
  5. * @date 2019-9-11.
  6. */
  7. public class DoorEvent extends EventObject {
  8. private static final long serialVersionUID = 6496098798146410884L;
  9. private final String key;
  10. private final String value;
  11. /**source存放Object对象,传递业务数据对象。*/
  12. public DoorEvent(Object source,String key,String value) {
  13. super(source);
  14. this.key = key;
  15. this.value = value;
  16. }
  17. public String getValue() {
  18. return value;
  19. }
  20. public String getKey() {
  21. return key;
  22. }
  23. }

3、两个监听值改变了的监听类

  1. package my.mark.mybaibaoxiang.eventDemo.easyEvent;
  2. import java.util.EventListener;
  3. /**
  4. * @author twotiger-wxm.
  5. * @date 2019-9-11.
  6. */
  7. public class DoorNameListener implements EventListener {
  8. public void doorEvent(DoorEvent event) {
  9. Door door = (Door) event.getSource();
  10. System.out.println("I got a new name,named \"" + door.getName() + "\"");
  11. }
  12. }
  13. package my.mark.mybaibaoxiang.eventDemo.easyEvent;
  14. import java.util.EventListener;
  15. /**
  16. * @author twotiger-wxm.
  17. * @date 2019-9-11.
  18. */
  19. public class DoorStateListener implements EventListener {
  20. public void doorEvent(DoorEvent event) {
  21. if (event.getValue() != null && event.getValue().equals("open")) {
  22. System.out.println("门1打开");
  23. } else {
  24. System.out.println("门1关闭");
  25. }
  26. }
  27. }

4、事件触发来源测试类

  1. package my.mark.mybaibaoxiang.eventDemo.easyEvent;
  2. /**
  3. * 最简单的事件监听模式(某个对象的值改变后做相应操作。)
  4. * @author twotiger-wxm.
  5. * @date 2019-9-11.
  6. */
  7. public class EventTest {
  8. public static void main(String[] args) {
  9. Door door = Door.getInstance();
  10. // 开门
  11. door.setState("open");
  12. System.out.println("我已经进来了");
  13. // 关门
  14. door.setState("close");
  15. // 改名
  16. door.setName("dengzy"); }
  17. }

二、spring的事件Event相关

zuul等很多优秀的源码用到了EventListener。

spring的事件监听有三个部分组成,事件(ApplicationEvent)、监听器(ApplicationListener)和事件发布操作。

1、事件类需要继承ApplicationEvent,代码如下:

  1. package my.mark.mybaibaoxiang.eventDemo.spring;
  2. import org.springframework.context.ApplicationEvent;
  3. /**
  4. * 自定义spring事件,自定义的业务事件,在自己的业务逻辑里使用:发布、监听执行。
  5. */
  6. public class WarnEvent extends ApplicationEvent {
  7. private static final long serialVersionUID = -7344918861434285999L;
  8. /**
  9. * 以下自定义参数传需要的业务参数,到监听后执行。
  10. */
  11. private String type;
  12. private String target;
  13. private String content;
  14. public WarnEvent(Object source, String type, String target, String content) {
  15. super(source);
  16. this.type = type;
  17. this.target = target;
  18. this.content = content;
  19. }
  20. public String getType() {
  21. return type;
  22. }
  23. public String getTarget() {
  24. return target;
  25. }
  26. public String getContent() {
  27. return content;
  28. }
  29. }

事件类是一种很简单的pojo,除了需要继承ApplicationEvent也没什么了,这个类有一个构造方法需要super。super使该事件类中可携带任何数据,比方唯一标识,或者事件源业务数据对象。通过source得到事件源。

2、事件监听类

事件监听器需要实现ApplicationListener接口,这是个泛型接口,泛型类类型就是事件类型,其次需要是spring容器托管的bean,所以这里加了@component,只有一个方法,就是onApplicationEvent。

  1. @Component
  2. public class HelloEventListener implements ApplicationListener<HelloEvent> {
  3. private static final Logger logger = LoggerFactory.getLogger(HelloEventListener.class);
  4. @Override
  5. public void onApplicationEvent(HelloEvent event) {
  6. logger.info("receive {} say hello!",event.getName());
  7. }
  8. }

ApplicationListener 接口的定义如下:

  1. @FunctionalInterface
  2. public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
  3. /**
  4. * Handle an application event.
  5. * @param event the event to respond to
  6. */
  7. void onApplicationEvent(E event);
  8. }

它是一个泛型接口,泛型的类型必须是 ApplicationEvent 及其子类,只要实现了这个接口,那么当容器有相应的事件触发时,就能触发 onApplicationEvent 方法。

我们可以直接去监听ApplicationEvent去监听spring的所有事件:

  1. @Component
  2. public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
  3. @Override
  4. public void onApplicationEvent(ApplicationEvent event) {
  5. System.out.println("事件触发:"+event.getClass().getName());
  6. }
  7. }

或者直接用注解将某个方法变成监听处理器

  1. @Async
  2. @EventListener
  3. public void send(WarnEvent event) {
  4. try {
  5. logger.info("===================WarnEvent走了");
  6. /*SendService sendService = factory.getSendService(event.getType());
  7. sendService.send(event.getTarget(), event.getContent());*/
  8. } catch (Exception e) {
  9. logger.error("消息发送失败! cause by:" + e.getMessage(), e);
  10. }
  11. }

EventListener这个注解其实可以接受参数来表示事件源的,有两个参数classes和condition,顾明思议前者是表示哪一个事件类,后者是当满足什么条件是会调用该方法,但其实都可以不用用到,直接在方法上写参数HelloEvent就行。

3、事件发布操作

  1. applicationContext.publishEvent(new WarnEvent(UUID.randomUUID(), type, target, content));

要获取应用上下文applicationContext,可实现implements ApplicationContextAware,通过接口方法获取。

  1. @Override
  2. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  3. this.applicationContext = applicationContext;
  4. }

事件发布操作和事件监听后操作默认同步,但如果给监听方法加spring的异步线程注解@Async使用默认线程池或者指定线程池,也可以让变成异步。

三、Spring事件驱动

spring的事件驱动最大的特征是监听器的添加方式,spring一般的添加方式是实现ApplicationListener接口,然后把实现类注册为Bean,spring的上下文初始化的时候会自动扫描路径下的实现ApplicationListener接口的类,然后添加到监听器集合里面,发布事件的时候会通知所有的监听器(同老虎的监听设计一样),这一切要依赖spring的容器管理Bean的功能。

四、监听事件顺序问题

Spring 提供了一个SmartApplicationListener类,可以支持listener之间的触发顺序,普通的ApplicationListener优先级最低(最后触发)。可以对同一个事件多个监听需顺序执行下使用。

  1. @Component
  2. public class MyListener implements SmartApplicationListener {
  3. private volatile AtomicBoolean isInit = new AtomicBoolean(false);
  4. @Override
  5. public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
  6. return eventType == ContextRefreshedEvent.class;
  7. }
  8. @Override
  9. public boolean supportsSourceType(Class<?> sourceType) {
  10. return sourceType == String.class;//或者直接return true;看需求有没有该限制要求。一般上边那个方法限制事件类型就满足了。
  11. }
  12. @Override
  13. public void onApplicationEvent(ApplicationEvent event) {
  14. System.out.println("孙悟空在沙僧之后收到新的内容:" + event.getSource());
  15. }
  16. //值越小,就先触发
  17. @Override
  18. public int getOrder() {
  19. return 2;
  20. }
  21. }
  1. supportsEventType:用于指定支持的事件类型,只有支持的才调用onApplicationEvent;
  2. supportsSourceType:支持的目标类型,只有支持的才调用onApplicationEvent;
  3. getOrder:即顺序,越小优先级越高

SmartApplicationListener的源码:

  1. public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
  2. /**
  3. * Determine whether this listener actually supports the given event type.
  4. * @param eventType the event type (never {@code null})
  5. */
  6. boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
  7. /**
  8. * Determine whether this listener actually supports the given source type.
  9. * <p>The default implementation always returns {@code true}.
  10. * @param sourceType the source type, or {@code null} if no source
  11. */
  12. default boolean supportsSourceType(@Nullable Class<?> sourceType) {
  13. return true;//默认true。
  14. }
  15. /**
  16. * Determine this listener's order in a set of listeners for the same event.
  17. * <p>The default implementation returns {@link #LOWEST_PRECEDENCE}.
  18. */
  19. @Override
  20. default int getOrder() {
  21. return LOWEST_PRECEDENCE;//默认排序值最大,优先级最低。取自Ordered接口。
  22. }
  23. }
  24. public interface Ordered {
  25. int HIGHEST_PRECEDENCE = -2147483648;
  26. int LOWEST_PRECEDENCE = 2147483647;
  27. int getOrder();
  28. }

发表评论

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

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

相关阅读