目录
  • 前言
  • 引导案例
  • 一、通过实现ApplicationListener接口实现步骤
    • 1、自定义一个事件类(对象),继承ApplicationEvent
    • 2、自定义业务类实现ApplicationListener 接口
    • 3、主线业务发布事件
  • 二、通过添加 @EventListener 注解来实现
    • 三、使用异步

      前言

      在spring框架中,提供了很多动态灵活且可扩展的机制,开发者可以利用这些机制完成一些巧妙的业务,实现一些业务中的解耦,

      引导案例

      下面看一个简单的案例,

      @Configuration
      public class SelfBusiness {
          public static void main(String[] args) {
              AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SelfBusiness.class);
              context.getBean(MyService.class).doBusiness();
              context.close();
          }
          @Component
          static class MyService {
              private static final Logger logger = LoggerFactory.getLogger(MyService.class);
              @Autowired
              private ApplicationEventPublisher publisher;
              public void doBusiness (){
                  logger.debug("主线业务");
                  logger.debug("发送短信");
                  logger.debug("发送邮件");
              }
          }
      

      运行上面的代码,观察效果

      springboot 事件监听器的案例详解

      结合输出结果,这这段代码要实现的逻辑是,在主线业务执行完成后,需要执行发短信,发邮件等操作,这样写也没毛病,但不够优雅,从后续的业务可扩展性上来讲,不够友好,如果后续主线业务执行完毕,还需再增加一个其他的审计操作,则需要新增代码逻辑,这就将主线业务和支线逻辑紧密的耦合了起来;

      就是说,我们期待的效果是,主线业务根本不关心其他的业务操作,只需要完成自身的逻辑就ok了,这就需要使用到spring提供的事件监听器功能;

      使用事件监听器改造过程

      springboot(spring)的事件监听器使用主要有两种方式,通过实现ApplicationListener接口,另一个就是在类上添加 @EventListener 注解来实现,接下来将对这两种方式逐一说明;

      一、通过实现ApplicationListener接口实现步骤

      1、自定义一个事件类(对象),继承ApplicationEvent

       static class MyEvent extends ApplicationEvent {
              public MyEvent(Object source) {
                  super(source);
              }
          }

      可以这么理解,在代码中,可能有很多种类型的事件,不同的业务对应着不同的事件,对于某个具体的监听器来说,它只想监听A这种类型的事件;

      2、自定义业务类实现ApplicationListener 接口

        
      	@Data
          static class Params {
              private String id ;
              private String name;
              private String phone;
          }
      	@Component
          static class SmsApplicationListener implements ApplicationListener<MyEvent> {
              private static final Logger logger = LoggerFactory.getLogger(SmsApplicationListener.class);
              @Override
              public void onApplicationEvent(MyEvent myEvent) {
                  Object source = myEvent.getSource();
                  try {
                      Params params = objectMapper.readValue(source.toString(), Params.class);
                      logger.debug("userId : {}",params.getId());
                  } catch (JsonProcessingException e) {
                      e.printStackTrace();
                  }
                  logger.debug("执行 sms 发短信业务");
              }
          }
          @Component
          static class EmailApplicationListener implements ApplicationListener<MyEvent> {
              private static final Logger logger = LoggerFactory.getLogger(SmsApplicationListener.class);
              @Override
              public void onApplicationEvent(MyEvent myEvent) {
                  Object source = myEvent.getSource();
                  logger.debug("执行 email 发邮件业务");
              }
          }
      

      显然,这里的监听器要监听的事件类型,正是上面我们定义的MyEvent ,这样,当业务被触发的时候,就可以在onApplicationEvent中拿到传递过来的参数,从而执行发短信(发邮件)业务操作了

      3、主线业务发布事件

      @Component
          static class MyService {
              private static final Logger logger = LoggerFactory.getLogger(MyService.class);
              @Autowired
              private ApplicationEventPublisher publisher;
              public void doBusiness (){
                  Params params = new Params();
                  params.setId("001");
                  params.setName("xiaoma");
                  params.setPhone("133******");
                  logger.debug("主线业务");
                  try {
                      publisher.publishEvent(new MyEvent(objectMapper.writeValueAsString(params)));
                  } catch (JsonProcessingException e) {
                      e.printStackTrace();
                  }
                  //publisher.publishEvent(new MyEvent("MyService doBusiness()"));
                  //logger.debug("发送短信");
                  //logger.debug("发送邮件");
              }
          }
      

      对主线业务来说,这时候就不再需要写发送短信或邮件逻辑了,只需要一个publisher将事件发布出去即可,如果需要传递参数,将参数一起传递过去

      完整的代码

      @Configuration
      public class SelfBusiness {
          private static ObjectMapper objectMapper = new ObjectMapper();
          public static void main(String[] args) {
              AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SelfBusiness.class);
              context.getBean(MyService.class).doBusiness();
              context.close();
          }
          @Data
          static class Params {
              private String id ;
              private String name;
              private String phone;
          }
          /**
           * 自定义事件对象
           */
          static class MyEvent extends ApplicationEvent {
              public MyEvent(Object source) {
                  super(source);
              }
          }
          @Component
          static class MyService {
              private static final Logger logger = LoggerFactory.getLogger(MyService.class);
              @Autowired
              private ApplicationEventPublisher publisher;
              public void doBusiness (){
                  Params params = new Params();
                  params.setId("001");
                  params.setName("xiaoma");
                  params.setPhone("133******");
                  logger.debug("主线业务");
                  try {
                      publisher.publishEvent(new MyEvent(objectMapper.writeValueAsString(params)));
                  } catch (JsonProcessingException e) {
                      e.printStackTrace();
                  }
                  //publisher.publishEvent(new MyEvent("MyService doBusiness()"));
                  //logger.debug("发送短信");
                  //logger.debug("发送邮件");
              }
          }
          /**
           * 监听事件触发后要执行的业务
           */
          @Component
          static class SmsApplicationListener implements ApplicationListener<MyEvent> {
              private static final Logger logger = LoggerFactory.getLogger(SmsApplicationListener.class);
              @Override
              public void onApplicationEvent(MyEvent myEvent) {
                  Object source = myEvent.getSource();
                  try {
                      Params params = objectMapper.readValue(source.toString(), Params.class);
                      logger.debug("userId : {}",params.getId());
                  } catch (JsonProcessingException e) {
                      e.printStackTrace();
                  }
                  logger.debug("执行 sms 发短信业务");
              }
          }
          @Component
          static class EmailApplicationListener implements ApplicationListener<MyEvent> {
              private static final Logger logger = LoggerFactory.getLogger(SmsApplicationListener.class);
              @Override
              public void onApplicationEvent(MyEvent myEvent) {
                  Object source = myEvent.getSource();
                  logger.debug("执行 email 发邮件业务");
              }
          }
      }
      

      再次运行上面的代码,观察效果,可以看到,仍然能满足预期的效果

      springboot 事件监听器的案例详解

      二、通过添加 @EventListener 注解来实现

      这种方式不再需要实现ApplicationListener 接口,而是直接在监听类的方法上面添加 @EventListener注解即可,相对要简化了一些,下面直接贴出完整的代码

      package com.congge.config;
      import com.fasterxml.jackson.core.JsonProcessingException;
      import com.fasterxml.jackson.databind.ObjectMapper;
      import lombok.Data;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.ApplicationEvent;
      import org.springframework.context.ApplicationEventPublisher;
      import org.springframework.context.ApplicationListener;
      import org.springframework.context.annotation.AnnotationConfigApplicationContext;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.event.EventListener;
      import org.springframework.stereotype.Component;
      @Configuration
      public class SelfBusiness2 {
          private static ObjectMapper objectMapper = new ObjectMapper();
          public static void main(String[] args) {
              AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SelfBusiness2.class);
              context.getBean(MyService.class).doBusiness();
              context.close();
          }
          @Data
          static class Params {
              private String id ;
              private String name;
              private String phone;
          }
          /**
           * 自定义事件对象
           */
          static class MyEvent extends ApplicationEvent {
              public MyEvent(Object source) {
                  super(source);
              }
          }
          @Component
          static class MyService {
              private static final Logger logger = LoggerFactory.getLogger(MyService.class);
              @Autowired
              private ApplicationEventPublisher publisher;
              public void doBusiness (){
                  Params params = new Params();
                  params.setId("001");
                  params.setName("xiaoma");
                  params.setPhone("133******");
                  logger.debug("主线业务");
                  try {
                      publisher.publishEvent(new MyEvent(objectMapper.writeValueAsString(params)));
                  } catch (JsonProcessingException e) {
                      e.printStackTrace();
                  }
              }
          }
          @Component
          static class SmsListenerService {
              private static final Logger logger = LoggerFactory.getLogger(SmsListenerService.class);
              @EventListener
              public void smsListener(MyEvent myEvent){
                  Object source = myEvent.getSource();
                  try {
                      SelfBusiness2.Params params = objectMapper.readValue(source.toString(), SelfBusiness2.Params.class);
                      logger.debug("userId : {}",params.getId());
                  } catch (JsonProcessingException e) {
                      e.printStackTrace();
                  }
                  logger.debug("执行 sms 发短信业务");
              }
          }
          @Component
          static class EmailListenerService {
              private static final Logger logger = LoggerFactory.getLogger(EmailListenerService.class);
              @EventListener
              public void emailListener(MyEvent myEvent){
                  Object source = myEvent.getSource();
                  try {
                      SelfBusiness2.Params params = objectMapper.readValue(source.toString(), SelfBusiness2.Params.class);
                      logger.debug("userId : {}",params.getId());
                  } catch (JsonProcessingException e) {
                      e.printStackTrace();
                  }
                  logger.debug("执行 email 发邮件业务");
              }
          }
      }
      

      运行上面的代码,观察效果,同样可以达到预期的效果

      springboot 事件监听器的案例详解

      三、使用异步

      更进一步来说,为了提升主线业务的逻辑执行效率,我们希望发布事件的业务逻辑异步执行,这个该如何做呢?

      翻阅源码可以知道,ApplicationEventPublisher 默认发布事件时候采用单线程同步发送,如果需要使用异步,需要自定义 ThreadPoolTaskExecutor ,以及SimpleApplicationEventMulticaster ,因此我们只需要覆盖一下这两个组件的bean即可,在上面的业务类中将下面的这两个bean添加进去;

      @Bean
          public ThreadPoolTaskExecutor executor() {
              ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
              executor.setCorePoolSize(5);
              executor.setMaxPoolSize(10);
              executor.setQueueCapacity(100);
              return executor;
          }
          @Bean
          public SimpleApplicationEventMulticaster applicationEventMulticaster(ThreadPoolTaskExecutor executor) {
              SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
              eventMulticaster.setTaskExecutor(executor);
              return eventMulticaster;
          }
      

      这时候再次运行代码,反复运行多次,就可以看到效果

      springboot 事件监听器的案例详解

      对比下上面单线程效果

      springboot 事件监听器的案例详解

      自定义事件发布器

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