【Spring】事件监听器(必知必会)

117 阅读3分钟

「这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战

一、前言

Spring 事件/监听器机制属于事件/监听器模式,可视为观察者模式(Observer Pattern)的扩展。

观察者模式:当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。

比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

举个栗子,demo 如下:

被观察者:

public interface Observable {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers(String message);
}

观察者:

public interface Observer {
    void notify(String message);
}

默认被观察者实现:

public class DefaultObservable implements Observable {

    private final List<Observer> observers = new ArrayList<>();
    
    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }
    
    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }
    
    @Override
    public void notifyObservers(String message) {
    
        for (Observer observer : observers) {
            observer.notify(message);
        }
    }
}

(1)Java 中事件机制

早在 Java 1.0 时,观察者模式就被 Java SEAPI 实现:

  • java.util.Observable数据发布者
  • java.util.Observer数据接收者

Java 中提供了基本的事件处理基类:

  • EventObject:所有事件状态对象都将从其派生的根类
  • EventListener:所有事件侦听器接口必须扩展的标记接口

举个栗子,关开门:

根据铃声关开门,响一声开门,响两声关门。

  1. 定义事件:铃声事件
public class BellEvent extends EventObject {

    private Integer times;

    public BellEvent(Object source) {
        super(source);
    }

    public BellEvent(Object source, Integer times) {
        super(source);
        this.times = times;
    }

    public Integer getTimes() {
        return this.times;
    }
}
  1. 定义监听器:开门监听器、关门监听器
public interface BellListener extends EventListener {

    void bellEvent(BellEvent bellEvent);
}

class OpenDoorListener implements BellListener {

    @Override
    public void bellEvent(BellEvent bellEvent) {

        Integer times = bellEvent.getTimes();

        if (1 == times) {
            System.out.println("响铃 " + times + " 声, " + "开门啊!!!");
        }
    }
}

class CloseDoorListener implements BellListener {

    @Override
    public void bellEvent(BellEvent bellEvent) {

        Integer times = bellEvent.getTimes();

        if (2 == times) {
            System.out.println("响铃 " + times + " 声, " + "关门啊!!!");
        }
    }
}
  1. 测试
public class Test {

    @Test
    public void test() {

        List<BellListener> bellListeners = new ArrayList<>();
        bellListeners.add(new OpenDoorListener());
        bellListeners.add(new CloseDoorListener());

        BellEvent bellEvent = new BellEvent("铃声", 1);

        for (BellListener bellListener : bellListeners) {

            bellListener.bellEvent(bellEvent);
        }
    }
}

输出如下:

响铃 1 声, 开门啊!!!

(2)实际应用

熟悉了 demo,再来看下如何在实际中应用。

Netty 中观察者模式的运用非常多,平时经常使用的 ChannelFuture#addListener 接口就是观察者模式的实现。

先来看下 ChannelFuture 使用的示例:

ChannelFuture channelFuture = channel.writeAndFlush(object);
channelFuture.addListener(future -> {
    if (future.isSuccess()) {
        // do something
    } else {
        // do something
    }
});

addListener 方法会将添加监听器添加到 ChannelFuture 当中,并在 ChannelFuture 执行完毕的时候立刻通知已经注册的监听器。

所以 ChannelFuture 是被观察者,addListener 方法用于添加观察者。



二、Spring 事件和监听器

  1. 定义事件
@Data
class ExceptionEvent extends ApplicationEvent {

    private String message;

    ExceptionEvent(Object source, String message) {
        super(source);
        this.message = message;
    }
}
  1. 定义事件监听者
@Slf4j
@Component
public class MessageListener implements ApplicationListener<ExceptionEvent> {

    @Override
    public void onApplicationEvent(ExceptionEvent event) {

        log.info("发送消息开始 ===>");
        try {
            log.info("消息: " + event.getMessage());
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        log.info("发送消息结束 ===>");
    }
}
  1. 创建事件发布者
@Slf4j
@Configuration
public class EventRegister {

    @Autowired
    private ApplicationContext context;

    public void sendMessage(String message) {

        context.publishEvent(new ExceptionEvent(this, message));
    }
}

1. 注解方式

采用注解方式,就不用实现对应接口了。 implements ApplicationListener<ExceptionEvent>

@Component
public class MessageListener {

    @EventListener(classes = ExceptionEvent.class)
    public void onApplicationEvent(ExceptionEvent event) {
        // ... ... 
    }
}

2. 添加顺序

只需要添加 @Order 注解即可。

  • 默认:2147483647
  • 数字越小优先级越高
@Component
public class MessageListener {

    @Order(0)
    @EventListener(classes = ExceptionEvent.class)
    public void onApplicationEvent(ExceptionEvent event) {
        // ... ... 
    }
}

3. 异步调用

之前调用都是同步,那么异步如何使用?

有两种方式:

  1. 直接在监听器上加 @Async
  2. 对应方法上加 @Async

例如:

  1. 直接在监听器上加 @Async
@Async
@Slf4j
@Component
public class MessageListener implements ApplicationListener<ExceptionEvent> {

    // ... ...
}
  1. 对应方法上加 @Async
@Component
public class MessageListener {

    @Async()
    @EventListener(classes = ExceptionEvent.class)
    public void onApplicationEvent(ExceptionEvent event) {
        // ... ... 
    }
}