Spring Event,业务解耦神器

1,337 阅读2分钟

1、Event应用场景

在实际业务开发过程中,业务逻辑可能非常复杂,有一些业务场景不需要在一次请求中同步执行,比如邮件发送、短信发送等。针对这类业务,如何优雅的将这类业务进行解耦,可能我们第一想法就是使用RocketMQ进行解耦,MQ确实可以解决这个问题,但MQ重啊,非必要不提升架构复杂度。针对这样的业务,Spring Event是一个不错的解决方案。

Spring Event其实就是一个观察者设计模式,即一个 Bean 处理完成任务后希望通知其它 Bean 或者说一个 Bean 想观察监听另一个Bean 的行为。

2、Spring Event实战

启动类:

@SpringBootApplication
public class ElasticsearchApplication {
   public static void main(String[] args) {
      SpringApplication.run(ElasticsearchApplication.class,args);
   }
}

配置文件:

server:
  port: 8091

定义首次登录事件实体:

public class LogonEvent<T> extends ApplicationEvent {

    /**
     * 消费者实体
     */
    private T t;

    public LogonEvent(Object source, T t) {
        super(source);
        this.t = t;
    }

    public LogonEvent(@NonNull Object source) {
        super(source);
    }

    public T getConsumer() {
        return t;
    }

    public void setConsumer(T t) {
        this.t = t;
    }
}

定义事件发布器:

@Component
public class LogonEventPublisher implements ApplicationEventPublisherAware, ApplicationContextAware {

    /**
     * 日志管理组件
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(LogonEventPublisher.class);

    /**
     * 上下文容器
     */
    private ApplicationContext applicationContext;

    /**
     * 事件发布组件
     */
    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void setApplicationEventPublisher(@NonNull ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    /**
     * 广播📢{@link LogonEventPublisher}事件
     *
     * @param t 消费者
     */
    public <T> void publishEvent(T t) {
        LOGGER.info("消费者事件发布开始,参数:{}", JSON.toJSONString(t));
        LogonEvent event = new LogonEvent(applicationContext);
        event.setConsumer(t);
        applicationEventPublisher.publishEvent(event);
        LOGGER.info("事件发布结束");
        LOGGER.info("broadcast FirstLogonEvent");
    }

}

定义事件发布者:

@Slf4j
@RestController
@RequestMapping("/listenerEvent")
public class ListenerEventController {

   @Autowired
   private LogonEventPublisher logonEventPublisher;

   @GetMapping("/publisher")
   public ResponseMessage<Void> publisher(){
      log.info("其它业务逻辑开始执行.......");
      
      log.info("监听事件开始执行.......");
      Consumer consumer = new Consumer();
      consumer.setConsumerName("张三");
      consumer.setSex("男");
      logonEventPublisher.publishEvent(consumer);
      log.info("监听事件Controller结束执行.......");

      return new OverAllExceptionAdvice<Void>().sendSuccessResponse();
   }
}

2.1、Event同步使用

定义事件监听器-同步执行: 监听并处理事件,实现 ApplicationListener 接口或者使用 @EventListener 注解。

@Slf4j
@Service
public class LogonListener {

    /**
     * 异步执行,async注解
     * 登录事件监听器
     */
    @EventListener(LogonEvent.class)
    public void dispatch(LogonEvent event) {
        log.info("监听到登录事件event:{}", event);
        // 模拟异步场景
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Consumer consumer = (Consumer)event.getConsumer();
        log.info("监听到的事件内容时:{}",consumer);
        log.info("登录事件监听完成");
    }
}

执行结果:

Snipaste_2022-07-18_17-15-29.png

2.2、Event异步使用

修改启动类,添加开启异步注解@EnableAsync

@EnableAsync
@SpringBootApplication
public class ElasticsearchApplication {
   public static void main(String[] args) {
      SpringApplication.run(ElasticsearchApplication.class,args);
   }
}

定义事件监听器-异步执行: 在监听方法上添加注解@Async

@Slf4j
@Service
public class LogonListener {

    /**
     * 异步执行,async注解
     * 登录事件监听器
     */
    @EventListener(LogonEvent.class)
    @Async
    public void dispatch(LogonEvent event) {
        log.info("监听到登录事件event:{}", event);
        // 模拟异步场景
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Consumer consumer = (Consumer)event.getConsumer();
        log.info("监听到的事件内容时:{}",consumer);
        log.info("登录事件监听完成");
    }
}

执行结果:

Snipaste_2022-07-18_17-15-29.png