Spring提供ApplicationListener用来监听其他bean的事件,使用ApplicationContext容器对事件进行发布后,所有监听该事件的监听器,都将对该事件进行处理
类似于java的监听器模式,只是在实现上略有不同,基本的思想是一致的。
1.基于java.util.Observable实现的监听器
* 被监听者Watched实现如下:
// 被监听对象
public class Watched extends Observable {
private String data = "";
public String getData() {
return data;
}
public void setData(String data) {
if(!this.data.equals(data)){
this.data = data;
// 修改状态
setChanged();
}
// 通知所有观察者
notifyObservers();
}
}
* 监听者实现如下:
// 监听器
public class Watcher implements Observer {
public Watcher(Observable o) {
o.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
System.out.println("statue changed:" + ((Watched) o).getData());
}
}
* 测试类
public class Test {
public static void main(String[] args) {
// create watcher
Watched watched = new Watched();
// create observer
Observer watcher = new Watcher(watched);
// set data
watched.setData("now");
}
}
可以看到,当Watched发生变化时,监听器Watcher监听到事件变化,并执行update方法
2.基于Application的监听器
* 创建被监听事件
@Data
// 监听器监听的对象
public class DemoEvent extends ApplicationEvent{
private String msg;
public DemoEvent(Object source,String msg) {
super(source);
this.setMsg(msg);
}
}
* 创建监听器,直接监听DemoEvent事件
@Component
public class DemoListener implements ApplicationListener{
@Override
public void onApplicationEvent(DemoEvent event) {
String msg = event.getMsg();
System.out.println("demoListener接受到了demoPublisher发布的消息:"+msg);
}
}
* 事件发布
@Component
public class DemoPublisher {
@Autowired
ApplicationContext context;
public void published() {
DemoEvent event = new DemoEvent(this, "发布成功!");
System.out.println("发部event:"+event);
context.publishEvent(event);
}
}
* 测试
@Configuration
@ComponentScan({"com.example.demo.eventListener"})
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(EventConfig.class);
DemoPublisher publisher = context.getBean(DemoPublisher.class);
// 发布事件
publisher.published();
context.close();
}
}
注意,需要将以上的监听器及事件相关bean都注入到Spring中,否则不生效;
可以看到,监听器监听到DemoEvent事件,并调用onApplicationEvent方法
3.ApplicationListener相关源码解析
在Test类中,当发布事件后,调用ApplicationContext.publishEvent()方法,下面从该方法看起:
1)由于当前的容器类为AnnotationConfigApplicationContext,故可知真正实现该方法的为AbstractApplicationContext类,代码如下:
protected void publishEvent(Object event, ResolvableType eventType) {
...
else {
// 真正执行方法的语句
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
2)跟踪multicaseEvent方法,代码如下
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// getApplicationListeners获取当前event所有的监听器
for (final ApplicationListener listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
// 执行监听器的onApplicationEvent方法
invokeListener(listener, event);
}
});
}
else {
// 不存在executor的情况下,在这里执行监听器的onApplicationEvent方法
invokeListener(listener, event);
}
}
}
由以上代码可知,在该方法中,获取event所有的监听事件,然后遍历执行监听器的onApplicationEvent方法,可知此方法是核心方法,是真正调用监听器的地方
3)跟踪getApplicationListeners方法,了解如何获取事件所有的监听器
protected Collection sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Quick check for existing entry on ConcurrentHashMap...
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
// 从map中获取,如果之前已经存在该key,则直接从map获取
if (retriever != null) {
return retriever.getApplicationListeners();
}
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// Fully synchronized building and caching of a ListenerRetriever
synchronized (this.retrievalMutex) {
retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true);
// map中没有该key,则先获取所有的监听器
Collection> retrieveApplicationListeners(
ResolvableType eventType, Class sourceType, ListenerRetriever retriever) {
LinkedList>();
Set>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
}
for (ApplicationListener listener : listeners) {
// 2.针对于该Event事件的监听器,放入allListeners
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
allListeners.add(listener);
}
}
if (!listenerBeans.isEmpty()) {
BeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
Class listenerType = beanFactory.getType(listenerBeanName);
if (listenerType == null || supportsEvent(listenerType, eventType)) {
ApplicationListener listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
// 针对于该Event事件的监听器,并且不存在于allListeners中的,则放入allListeners
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListenerBeans.add(listenerBeanName);
}
allListeners.add(listener);
}
}
}
catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition) disappeared -
// probably in the middle of the destruction phase
}
}
}
AnnotationAwareOrderComparator.sort(allListeners);
return allListeners;
}
通过以上代码可确定,监听器列表是从defaultRetriever中获取的,那么defaultRetriever中的applicationListeners是如何被添加进来的呢?
剩下的部分笔者写在另一篇文章ApplicationListener与ApplicationEvent 使用及源码解析(下)