1.观察者模式
jdk很罕见的提供了相关的类 java.util.Observable,java.util.Observer
类图见下
比较简单,Observable持有Observer列表,当Observable发生状态变动的时候,遍历执行Observer.update方法。
2.观察者模式在Spring的事件模型中的应用
最关键的几个角色
1.ApplicationEvent
2.ApplicationListener
3.ApplicationEventPublisherAware
其实也很好理解,事件驱动,首先得有事件,其次需要有关注事件的监听者,最后需要发布事件。
直接上代码实现《大话设计模式》第14章的例子,老板回来了,看股票的,看NBA的需要赶紧关掉相应的网页。
先定义老板回来了这个事件
public class BossArrivalEvent extends ApplicationEvent {
public BossArrivalEvent(String name) {
super(name);
}
}
再定义对“老板回来了”这个事件感兴趣的观察者, 看NBA的
@Service
public class NBAEmployeer implements ApplicationListener<BossArrivalEvent> {
@Override
public void onApplicationEvent(BossArrivalEvent bossArrivalEvent) {
System.out.println(bossArrivalEvent.getSource()+" come back ,close NBA website");
}
}
看股票的
@Service
public class StockEmployeer implements ApplicationListener<BossArrivalEvent> {
@Override
public void onApplicationEvent(BossArrivalEvent bossArrivalEvent) {
System.out.println(bossArrivalEvent.getSource()+" come back ,close stock website");
}
}
最关键的,定义一个老板,他可以发布“老板回来了”这个事件
@Service
public class Boss implements ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
public void comeBack(){
System.out.println("boss come back");
applicationEventPublisher.publishEvent(new BossArrivalEvent("boss"));
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}
测试代码
@SpringBootApplication
public class Client {
public static void main(String[] args){
SpringApplication.run(Client.class,args);
Boss boss = (Boss)SpringUtil.getBean("boss");
boss.comeBack();
}
}
运行结果
boss come back
boss come back ,close NBA website
boss come back ,close stock website
老板回来了这个事件,被两个正在干其他事情的员工监听到了,做出了响应,关掉了相关的网页。
完整代码见https://github.com/somewaters/designPattern/tree/master/src/main/java/observerModel
3.Spring的事件模型的运行原理
需要搞清楚两件事情
实现了ApplicationListener接口的两个观察者存在哪里了?
事件发布的时候,是怎么通知观察者的?
Spring 的bean生命周期中,在实例化,属性注入,初始化之后,会执行一个
BeanPostProcessor#postProcessAfterInitialization()方法,可以看调用栈
可以看到,调用了ApplicationListenerDetector#postProcessAfterInitialization方法
关键代码见下,在注释的地方进行了观察者的注册。
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof ApplicationListener) {
Boolean flag = (Boolean)this.singletonNames.get(beanName);
if (Boolean.TRUE.equals(flag)) {
//最关键的地方,在这里实现的观察者注册
this.applicationContext.addApplicationListener((ApplicationListener)bean);
} else if (Boolean.FALSE.equals(flag)) {
if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface but is not reachable for event multicasting by its containing ApplicationContext because it does not have singleton scope. Only top-level listener beans are allowed to be of non-singleton scope.");
}
this.singletonNames.remove(beanName);
}
}
return bean;
}
最终存在了这个数据结构中
private class ListenerRetriever {
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
}
第一个问题,观察者是如何注册的,已经得到答案。接着看,事件是怎么通知观察者的。
直接看debug的调用栈
可以看到,当调用boss.comeback()时,最终调用到了SimpleApplicationEventMulticaster#multicastEvent方法
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
//1.根据event事件,取出监听该事件的观察者列表
Iterator var4 = this.getApplicationListeners(event, type).iterator();
while(var4.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
//2.执行listener的方法
this.invokeListener(listener, event);
}
}
}
最终执行了我们定义的NBA,Stock那个观察者的onApplicationEvent方法。
形成了一个典型的观察者模式的应用。
HSF在进行服务发布的时候,也利用了Spring的事件模型,见HSFSpringProviderBean,其监听的是ContextRefreshedEvent,这是容器初始化完成后触发的事件
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
HSFSpringProviderBean.this.providerBean.publish();
setAppInitedStatus();
} else if (event instanceof ContextClosedEvent) {
if (AppInfoUtils.appRunning.compareAndSet(true, false)) {
try {
providerBean.shutdownHSFServer();
} catch (HSFException e) {
LOGGER.error(LoggerHelper.getErrorCodeStr("hsf", "HSF-0037", "环境问题", ""),"Spring容器关闭,销毁HSF相关资源失败!",e);
}
LOGGER.info("Spring容器关闭,设置应用初始化状态为未初始化!");
}
}
}