「这是我参与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 SE
的 API
实现:
java.util.Observable
数据发布者java.util.Observer
数据接收者
Java
中提供了基本的事件处理基类:
EventObject
:所有事件状态对象都将从其派生的根类EventListener
:所有事件侦听器接口必须扩展的标记接口
举个栗子,关开门:
根据铃声关开门,响一声开门,响两声关门。
- 定义事件:铃声事件
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;
}
}
- 定义监听器:开门监听器、关门监听器
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 + " 声, " + "关门啊!!!");
}
}
}
- 测试
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
事件和监听器
- 定义事件
@Data
class ExceptionEvent extends ApplicationEvent {
private String message;
ExceptionEvent(Object source, String message) {
super(source);
this.message = message;
}
}
- 定义事件监听者
@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("发送消息结束 ===>");
}
}
- 创建事件发布者
@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. 异步调用
之前调用都是同步,那么异步如何使用?
有两种方式:
- 直接在监听器上加
@Async
- 对应方法上加
@Async
例如:
- 直接在监听器上加
@Async
@Async
@Slf4j
@Component
public class MessageListener implements ApplicationListener<ExceptionEvent> {
// ... ...
}
- 对应方法上加
@Async
@Component
public class MessageListener {
@Async()
@EventListener(classes = ExceptionEvent.class)
public void onApplicationEvent(ExceptionEvent event) {
// ... ...
}
}