暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Spring事件监听机制的应用

程序员舒克 2022-08-11
196

基本概念

ApplicationContext 中的事件处理是通过 ApplicationEvent 类和 ApplicationListener 接口提供的。如果将实现 ApplicationListener 接口的 bean 部署到上下文中,则每次将 ApplicationEvent 发布到 ApplicationContext 时,都会通知该 bean。本质上,这是标准的观察者设计模式。

事件监听三要素


  • 事件(Event):想要自定义一个事件,我们通常会继承Spring的
    ApplicationEvent来声明一个事件,ApplicationEvent的父类是Jdk包下的EventObjeck类,Spring做了进一步的封装,且目前我们也正在用Spring框架,这使得我们的开发变得简单,所以我们继承Spring的ApplicationEvent类即可声明一个事件
  • 事件监听(Listener):ApplicationListener 通常使用自定义事件的类型进行参数化。这意味着 onApplicationEvent() 方法可以保持类型安全,避免任何向下转换的需要。默认情况下,事件监听器会同步接收事件,也就是说publishEvent()方法(“事件发布的方法”)会阻塞
  • 发布事件(publishEvent):ApplicationContext提供了publishEvent()方法,每次调用该方法时,会将事件发布到ApplicationContext中,ApplicationContext负责通知事件监听(Listener)

事件样例


  • 自定义事件
    @Getter
    @Setter
    @ToString
    public class BaseEvent extends ApplicationEvent {


    private final Person person;


    public BaseEvent(Object source, Person person) {
    super(source);
    this.person = person;
    }
    }
    • 自定义监听
      @Component
      public class MyApplicationEventListener implements ApplicationListener<BaseEvent> {


      /**
      * Handle an application event.
      *
      * @param event the event to respond to
      */
      @Override
      public void onApplicationEvent(BaseEvent event) {
      Person person = event.getPerson();
      System.out.println(person);
      }
      }
      • 触发 onApplicationEvent() 方法,发布事件
        @Component
        public class PublishEventService {


        @Resource
        private ApplicationContext applicationContext;


        public void pushNotice() {
        Person person = new Person();
        person.setAge(18);
        person.setName("老王");
        BaseEvent objectBaseEvent = new BaseEvent(this, person);
        applicationContext.publishEvent(objectBaseEvent);
        }
        }

        使用注解实现监听


          @Component
          public class MyApplicationAnnotationEventListener {


          /**
          * Handle an application event.
          *
          * @param event the event to respond to
          */
          @EventListener
          public void onApplicationEvent(BaseEvent event) {


          Person person = event.getPerson();


          System.out.println(person);
          }
          }

          @EventListener注解提供了 事件类的选择,也就是说,监听方法的形参可以不写,具体如下

            @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
            @Retention(RetentionPolicy.RUNTIME)
            @Documented
            public @interface EventListener {


            /**
            * Alias for {@link #classes}.
            */
            @AliasFor("classes")
            Class<?>[] value() default {};


            /**
            * The event classes that this listener handles.
            * <p>If this attribute is specified with a single value, the
            * annotated method may optionally accept a single parameter.
            * However, if this attribute is specified with multiple values,
            * the annotated method must <em>not</em> declare any parameters.
            */
            @AliasFor("value")
            Class<?>[] classes() default {};
              }
              @Component
              public class MyApplicationAnnotationEventListener {


              @EventListener(value = BaseEvent.class)
              public void handle() {
                      System.err.println("ApplicationContext 发布了事件,当前线程:" + Thread.currentThread().getName());
              }
              }

              还可以通过使用定义SpEL表达式的注释的condition属性来添加额外的运行时筛选,该注释应该与针对特定事件的方法的实际调用相匹配

                @Component
                public class MyApplicationAnnotationEventListener {


                /**
                * Handle an application event.
                *
                * @param event the event to respond to
                */
                    @EventListener(condition = "#event.person.age == 18")
                public void onApplicationEvent(BaseEvent event) {
                Person person = event.getPerson();
                System.err.println(person);
                }
                }

                异步监听


                注:上面的【监听三要素】里提到,publishEvent()方法是阻塞的,若事件多时,同步可能不能满足现有业务,Spring也想到这点了,它为我们提供了异步的监听,如下所示

                • 要想使用异步监听,必须先开启异步,我们只需在Springboot启动类添加@EnablAsync注解即可开启异步监听,监听的方法上添加@Async声明方法为异步方法

                  @Component
                  public class MyApplicationAnnotationEventListener {


                  /**
                  * Handle an application event.
                  *
                  * @param event the event to respond to
                  */
                  @Async
                  @EventListener(condition = "#event.person.age == 18")
                  public void onApplicationEvent(BaseEvent event) {
                  System.out.println("异步监听,当前线程:" + Thread.currentThread().getName());
                  }


                  @EventListener(value = BaseEvent.class)
                  public void handle() {
                  System.out.println("同步监听,当前线程:" + Thread.currentThread().getName());
                  }
                  }


                  注:观察发现,加了@Async注解的方法为异步方法,当前线程非主线程,注解生效

                  下一篇文章【Spring事件监听的源码解析】正在准备中...

                  文章转载自程序员舒克,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                  评论