天天看点

Spring Boot | 事件监听器异步处理事件,实现代码解耦

一、简介

Spring Boot事件监听器(Event Listener)用于在应用程序的生命周期中,监听Spring Boot应用程序中各种事件的发生,以便在事件发生时执行某些特定的操作。

二、集成步骤

1、创建自定义事件类

package cn.ddcherry.springboot.demo.event;

import lombok.Getter;
import org.springframework.context.ApplicationEvent;

@Getter
public class CustomEvent extends ApplicationEvent {

  private String message;

  public CustomEvent(Object source, String message) {
    super(source);
    this.message = message;
  }
}           

这里笔者汪小成创建了一个简单的事件类,继承了org.springframework.context.ApplicationEvent,事件类只有一个 String 类型的消息属性。

事件类不是必须继承ApplicationEvent,一个普通的类也可以作为一个事件类。

2、创建事件监听器

创建一个监听器类并实现ApplicationListener接口,该接口有一个onApplicationEvent()方法,用于处理事件:

package cn.ddcherry.springboot.demo.listener;

import cn.ddcherry.springboot.demo.event.CustomEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {
  @Override
  public void onApplicationEvent(CustomEvent event) {
    log.info("事件监听器 - 收到消息:{}", event.getMessage());
  }
}           

在上面的代码中,CustomEventListener实现了ApplicationListener<CustomEvent>接口,并且实现了onApplicationEvent方法。当一个CustomEvent事件被发布时,onApplicationEvent方法将被自动调用。

在CustomEventListener上使用@Component注解,将其作为 Spring 组件进行注册。

创建事件监听器有两种方式:

(1) 实现ApplicationListener接口;

(2) 使用@EventListener注解,@EventListener注解可以直接在方法上使用,以指定该方法为事件监听器。

@Component
public class CustomEventListener {
    @EventListener
    public void handleMyEvent(CustomEvent event) {
        //处理事件
    }
}           

3、创建事件发布者

package cn.ddcherry.springboot.demo.publisher;

import cn.ddcherry.springboot.demo.event.CustomEvent;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@AllArgsConstructor
public class CustomEventPublisher {

  private ApplicationEventPublisher publisher;

  public void publish(String message) {
    CustomEvent customEvent = new CustomEvent(this, message);
    publisher.publishEvent(customEvent);
    log.info("事件发布成功 - 消息:{}", message);
  }
}           

上面的示例中,通过构造方法注入的方式自动注入ApplicationEventPublisher,然后在方法内部调用了ApplicationEventPublisher的publishEvent(...)方法发布了一个自定义事件CustomEvent。

4、创建测试控制器类

为了演示事件监听,我们创建了一个控制器。代码如下:

package cn.ddcherry.springboot.demo.controller;

import cn.ddcherry.springboot.demo.publisher.CustomEventPublisher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/event")
public class EventController {

  @Resource
  private CustomEventPublisher customEventPublisher;

  @GetMapping("/publish")
  public Map<String, Object> publish(@RequestParam("message") String message) {
    customEventPublisher.publish(message);

    Map<String, Object> resultMap = new HashMap<>(16);
    resultMap.put("data", "事件发布成功");
    resultMap.put("message", message);
    return resultMap;
  }
}           

5、测试

启动spring Boot项目,然后在浏览器地址栏输入http://localhost:8080/event/publish?message=嗨皮汪小成,在控制台我们可以看到如下输出内容:

事件监听器 - 收到消息:嗨皮汪小成
事件发布成功 - 消息:嗨皮汪小成           

异步处理事件

默认情况下,Spring Boot 事件监听器会同步处理事件。也就是说,当事件触发时,Spring Boot 事件监听器会在同一个线程中处理该事件。这种处理方式的好处是可以保证处理事件的顺序和一致性,但如果事件处理比较耗时,可能会阻塞主线程。

Spring Boot 提供了异步处理事件的方式。可以通过在事件监听器上加上 @Async 注解来实现异步处理事件。同时还要在项目启动类上添加@EnableAsync注解来开启异步执行器的支持。

1、创建异步事件监听器

package cn.ddcherry.springboot.demo.listener;

import cn.ddcherry.springboot.demo.event.CustomEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Slf4j
@Async
@Component
public class CustomAsyncEventListener implements ApplicationListener<CustomEvent> {
  @Override
  public void onApplicationEvent(CustomEvent event) {
    log.info("事件监听器(异步) - 收到消息:{},开始处理。", event.getMessage());
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    log.info("事件监听器(异步) - 处理完成!");
  }
}           

从上面的代码可以看出,笔者汪小成在事件监听器上加上 @Async 注解来实现异步处理事件,使用Thread.sleep(2000)模拟监听器处理事件耗时。

2、在项目启动类上添加@EnableAsync注解

package cn.ddcherry.springboot.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

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

3、测试

启动spring Boot项目,然后在浏览器地址栏输入http://localhost:8080/event/publish?message=嗨皮汪小成,在控制台我们可以看到如下输出内容:

事件监听器(异步) - 收到消息:嗨皮汪小成,开始处理。
事件发布成功 - 消息:嗨皮汪小成
事件监听器(异步) - 处理完成!           

总结

使用事件的好处是解耦,可以方便地实现模块间的通信,同时也可以提高代码的可读性和可维护性。

继续阅读