SpringBoot2.X 实战5 - Event

一.简介

当一个事件发生时,我们希望另外一个服务能够感知到这个事件.发布这个事件的对象不需要关心哪个对象会监听这个事件并去处理它,当然监听者也不需要知道发布这个事件的对象是谁,我只关心如何去处理这些事件.这样业务之间的逻辑会更加解耦提高可扩展性.

从Spring框架的一开始,应用程序事件(ApplicationEvent)就成为松散耦合组件交换信息的一种手段,其实现原理为观察者模式.

二.基本用法

1.定义事件
所有的事件均需继承自 ApplicationEvent.ApplicationEvent是个抽象类,这样我们可以在实现类中定义一些用到的数据.继承 ApplicationEvent 后必须要重载构造方法,参数可以自定义.第一个 Object source 表示事件的发布体,发布事件时可以用 this.String data 表示自定义的数据,这里也可以用其他复杂对象来代替.

public class NormalEvent extends ApplicationEvent {

    @Getter
    private String data;

    public NormalEvent(Object source, String data) {
        super(source);
        this.data = data;
    }

}

2.发布事件
发布事件需要我们拿到 ApplicationContext 对象,在这个对象中调用publishEvent 方法.拿到 ApplicationContext 对象方法由很多.
这里先使用最简单的构造方法注入方式.
注意:发布事件时,第一个参数为 this.


@RestController
public class EventController1 {

    private final ApplicationContext applicationContext;

    public EventController1(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @GetMapping("/get1")
    public String getString1() {
        applicationContext.publishEvent(new NormalEvent(this, "NormalEvent"));
        return "get1";
    }
}

3.事件监听器
注册事件监听器方式也很多,但是用 @EventListener 方式是最简单的,在任何方法public void 方法中加上注解都可以. @EventListener 将方法标记为应用程序事件的侦听器的注释。
方法的入口参数即为事件,当入口参数为 Object 时,任何事件均会监听到.


@Slf4j
@Component
public class NormalEventHandler {

    /**
     * @param event 任何类型的事件都会接受
     */
    @EventListener
    public void event(Object event) {
        log.error("NormalEventHandler(Object) 接收到事件:{}", event.getClass());
    }

    /**
     * @param event 会接受 NormalEvent 类型的事件,顺序不定
     */
    @EventListener
    public void event1(NormalEvent event) {
        log.error("NormalEventHandler_event1(NormalEvent) 接收到事件:{}", event.getClass());
    }

    /**
     * @param event 会接受 会接受 NormalEvent 类型的事件,顺序不定
     */
    @EventListener
    public void event2(NormalEvent event) {
        log.error("NormalEventHandler_event2(NormalEvent) 接收到事件:{}", event.getClass());
    }

}

三.SmartEvent

二中介绍的时间监听器之间都是毫无关系的.经常发布的事件需要被多个监听器监听到,并且需要一定的顺序去执行这些监听器.
1.定义事件
这个和普通的事件没有任何区别.


public class SmartEvent extends ApplicationEvent {

    @Getter
    private String data;

    public SmartEvent(Object source, String data) {
        super(source);
        this.data = data;
    }

}

2.发布事件
这个也和普通的事件发布没有任何区别

@RestController
public class EventController1 {

    private final ApplicationContext applicationContext;

    public EventController1(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @GetMapping("/get3")
    public String getString3() {
        applicationContext.publishEvent(new SmartEvent(this, "SmartEvent"));
        return "get3";
    }

}

3.Smart事件监听器

这时时间监听器需要 实现 SmartApplicationListener 接口.SmartApplicationListener 是标准的 ApplicationListener 以及Ordered 接口的扩展变体.暴露更多的元数据,如支持的时间以及源类型.
需要实现 supportsEventType , supportsSourceType , getOrder 三个方法.

  • supportsEventType:表示支持的事件类型,,可以为多个,只要该方法返回 true 即可.
  • supportsSourceType:表示支持的源类型,即是从哪个类中发布的这个事件.
  • getOrder:监听器执行的优先级.数字越大优先级越低.
@Slf4j
@Component
public class SmartEventHandler1 implements SmartApplicationListener {

    @Override
    public boolean supportsEventType(Class aClass) {
        // 只接收 SmartEvent 类型的事件,只有 SmartEvent 类型的事件才会执行下面的逻辑
        return aClass == SmartEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class aClass) {
        // //只有在 EventController1 内发布的 SmartEvent 事件时才会执行下面逻辑
        return aClass == EventController1.class;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        SmartEvent event = (SmartEvent) applicationEvent;
        log.error("SmartEventHandler1:{}", event.getData());
    }

    @Override
    public int getOrder() {
        return 1;
    }

}

四.事件反馈

经常给系统一个输入就希望系统能够给一个反馈,如接口调用.因为发布者不关系监听者,监听者也不关心发布者,所以正常情况下没有办法给接口一个返回,这时只能使用统一异常处理.

1.异常事件

public class NormalExceptionEvent extends ApplicationEvent {

    @Getter
    private String data;

    public NormalExceptionEvent(Object source, String data) {
        super(source);
        this.data = data;
    }

}

2.发布异常事件

@RestController
public class EventController1 {

    private final ApplicationContext applicationContext;

    public EventController1(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @GetMapping("/get2")
    public String getString2() {
        applicationContext.publishEvent(new NormalExceptionEvent(this, "NormalExceptionEvent"));
        return "get2";
    }

}

3.定义异常类

public class EventException extends RuntimeException {

    @Getter
    @Setter
    private String mess;

    public EventException(String mess) {
        this.mess = mess;
    }
    
}

4.定义异常处理类


@RestControllerAdvice
public class EventExceptionHandler {

    @ExceptionHandler(value = EventException.class)
    public String handler1(EventException exception) {
        return "error:" + exception.getMess();
    }

}

5.抛出异常


@Slf4j
@Component
public class NormalExceptionEventHandler {

    /**
     * @param event 会接受 NormalEvent 类型的事件,顺序不定
     */
    @EventListener
    public void event1(NormalExceptionEvent event) {
        log.error("NormalEventHandler_event1(NormalEvent) 接收到事件:{}", event.getClass());
        throw new EventException("NormalExceptionEvent!");
    }

}

注:经测试,普通的事件和 SmartEvent 在 SpringBoot2.0.6 中均支持统一异常处理.但是之前好像有遇到有些 SpringBoot 版本不支持的情况.

五.代码路径

https://github.com/shaopro/SpringBootEvent

你可能感兴趣的:(SpringBoot2.X 实战5 - Event)