spring ApplicationContext的事件监听机制
目录
- 测试代码
- 继承ApplicationEvent定义自己的事件
- 实现ApplicationListener接口定义listener
- 事件发布
- 测试输出:
- 原理
- 事件如何发布
- listener如何添加的
- 补充:发布-订阅/观察者模式
测试代码
继承ApplicationEvent定义自己的事件
public class MyEvent extends ApplicationEvent {
String message;
public MyEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
实现ApplicationListener接口定义listener
eg:
@Component
public class MyEventListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("MyEventListener received: " + event.getMessage());
}
}
事件发布
@Service
public class MyEventPublisher {
@Autowired
private ApplicationContext context;
public void publish(String message) {
MyEvent myEvent = new MyEvent(this, message);
context.publishEvent(myEvent);
}
}
测试controller中测试发布事件
@RestController
public class TestControl {
@Autowired
private MyEventPublisher myEventPublisher;
@GetMapping(value = "/test")
public String getTest(@Param("msg") String msg) {
myEventPublisher.publish(msg);
return "hello test";
}
测试输出:
原理
事件如何发布
本例用springboot开启一个web项目,内置了ApplicationContext
bean 直接获取到,然后就可以发布事件了
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
走到
org.springframework.context.support.AbstractApplicationContext#publishEvent(org.springframework.context.ApplicationEvent)
,广播事件
获取到对应事件类型监听器,然后丢到线程池去执行
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
调用listener的onApplicationEvent方法
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
listener如何添加的
bean生命周期初始化阶段会执行所有BeanPostProcessor的postProcessAfterInitialization方法
通过内置的org.springframework.context.support.ApplicationListenerDetector
BeanPostProcessor添加到applicationContext的listener缓存中
补充:发布-订阅/观察者模式
观察者模式
- 被观察者直接发消息给观察者,当消息事件触发时,被观察者直接通知所有列表中的观察者。
- 被观察者耦合了一个观察者列表。
- 异步支持不够,当触发消息事件时,被观察者直接通知观察者。
- 安全性存在一定问题,观察者通常会对被观察者监听,可能得到被观察者内部的状态。
发布订阅模式
- 不再直接通信,发布者和订阅者中间存在一个第三方中介,发布者和订阅者都与这个第三方通信。
- 发布者不再维护观察者列表,甚至发布者和订阅者彼此并不知道对方的存在关系。
- 异步可支持,当触发消息事件时,第三方中介可以进行延迟发送。
- 安全性较好,发布者和订阅者解耦,需要通过第三方中介通信,获取发布者内部状态也必须通过特意暴露的方法才能访问。
发布订阅模式更适合一些大型的、复杂的、有异步要求、安全性要求高的场景,如:消息队列(RabbitMQ)、流处理(Kafka)。观察者模式相对实现更简单,适合一些轻量级、无特别要求的场景,如一些GUI工具。