java中自身设计的观察者模式涉及到的两个类
Observable;可以被观察的类
Observer:观察者接口
当我们想要使用观察者模式的时候,可以根据这两个类进行开发
public class ObservableDemo {
public static void main(String[] args) {
MyObservable observable = new MyObservable();
observable.addObserver(new MyObserver());
observable.setChanged();
}
static class MyObserver implements Observer{
@Override
public void update(Observable o, Object arg) {
System.out.printf("监听到 对象:%s 发生了变化,传来消息 %s",o,arg);
}
}
static class MyObservable extends Observable{
@Override
public synchronized void setChanged() {
super.setChanged();
super.notifyObservers("hello everybody");
super.clearChanged();
}
}
}
观察者模式后来扩展为事件机制,不需要把观察者注册到被观察者这里,当被观察着发生变动,可以发布特定的事件,通知感兴趣的/订阅事件的类。
java的消息机制涉及到下面几个类:
EventObject:事件消息体
EventListener:事件监听接口
spring在设计消息事件机制的时候沿用了 java的设计体系:
Spring涉及到的几个比较重要的类
ApplicationEvent
ApplicationContextEvent
ApplicationListener
ApplicationEventMulticaster
简单的看下
ApplicationEvent extends EventObject 继承了java的事件机制
而ApplicationListener
继承了java的EventListener
而ApplicationEventPublisher 和ApplicationEventMutilcaster
主要指责就是发布事件,ApplicationEventMutilcaster的职责稍微多一点,就是监听器的维护
那么spring自带了几种类型的事件呢。
其实根据类的继承关系我们可以看到
ApplicationContextEvent 继承 ApplicationEvent
ApplicationContextEvent有四个子类
说明在上下文的声明周期中,会发出事件消息:
ContextRefreshEvent:上下文刷新事件
ContextStartEvent:上下文启动事件
ContextStopEvent:上下文停止事件
ContextCloseEvent:上下文关闭事件
我们可以简单的写个demo验证一下
public class SpringEventSequenceDemo implements ApplicationListener {
public static void main(String[] args) {
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.addApplicationListener(new SpringEventSequenceDemo());
applicationContext.refresh();
applicationContext.start();
applicationContext.stop();
applicationContext.close();
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.printf("监听到事件:%s。\n",event);
}
}
尤其注意先要refresh,才能start。这是ApplicationContext的状态决定的,而我们在开发当中,可能需要监听不同的事件做不同的事情,比如当上下文关闭的时候,需要我们关闭掉很多自定义的资源,释放空间。
我们自己实现的ApplicationListener的时候,还可以根据泛型参数实例,监听不同的事件,比如只关心ContextCloseEvent
class ContextCloaseEventListener implements ApplicationListener<ContextClosedEvent>{
@Override
public void onApplicationEvent(ContextClosedEvent event) {
System.out.printf("监听到上线文关闭事件:%s。\n",event);
}
}
事件发布:
如果想要在开发过程中应用spring的事件机制发送事件,需要依靠ApplicationEventPublisher和ApplicationEventMulticaster.
所以我们我们在开发的时候可以借用spring的依赖注入注入ApplicationEventPublisher或者ApplicationEventMutilcater
或者是直接用上下文发布事件。
特别注意:spring的事件默认是单线程处理,要多线程处理需要以来@Async/@EnableAsync注解开始异步处理
特别特别注意:Spring的分层传播很有意思,在ParentContext发生的事件,在SubContext监听不到,在SubContext中发布的事件,在ParentContext中能监听到
public class SubContextEventDemo implements ApplicationListener<ContextClosedEvent> {
public static void main(String[] args) {
GenericApplicationContext currentContext = new GenericApplicationContext();
currentContext.setId("currentContext");
currentContext.addApplicationListener(new SubContextEventDemo());
GenericApplicationContext parentContext = new GenericApplicationContext();
parentContext.setId("parentContext");
parentContext.addApplicationListener(new SubContextEventDemo());
currentContext.setParent(parentContext);
parentContext.refresh();
currentContext.refresh();
parentContext.close();
currentContext.close();
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
System.out.printf("监听到上下文关闭事件:%s。\n",event.getApplicationContext().getId());
}
}
父子上下文各一个事件监听器,在关闭的时候
子上下文的关闭时间被父子上下文的监听器监听到:
所以要注意事件传播的范围。
另外还有一个注意的是,事件发布的节点。
比如,上下文注册监听器的事件是ApplicationContext.refresh()动作完成之前:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context. 初始化事件广播器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event. 发布ContextRefreshEvent
finishRefresh();
}
}
可以看到,spring自带的事件,最早的ContextRefreshEvent事件是在finishRefresh()里面触发的,而事件广播器是在initApplicationEventMutilcaster()初始化完成的,那事件监听器作为一个普通的bean,是在finishBeanFactoryInitialization(beanFactory)中实例化成bean的,所以正常的顺序是:
先初始化事件广播器 ApplicationEventMutilcaster
在实例化Bean 包括自定义事件监听器ApplicationListener
最后发布ContextRefreshEvent事件,这样看来是没有问题的,
但是在refresh()的前几步中:
处理了BeanFactoryPostProcessor 和 BeanPostProcessor的特殊类,如果自定义的bean即是BeanPostProcessor又是ApplicationEventPublisher,这个时候就要注意:
如果在BeanPostProcessor或者在BeanFactoryPostProcessor中发布事件,这些事件会先存储在Application中,等到finishRefresh的时候,先发布早起的事件,再发布正常的上下文事件。
public class EarlyEventPublisherDemo implements ApplicationListener, BeanPostProcessor {
@Autowired
ApplicationContext applicationContext;
@PostConstruct
public void publisherEvent(){
applicationContext.publishEvent("Early Event published");
}
public static void main(String[] args) {
GenericApplicationContext genericApplicationContext = new AnnotationConfigApplicationContext(EarlyEventPublisherDemo.class);
genericApplicationContext.close();
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.printf("监听到事件:%s。\n",event);
}
}