一、什么是程序世界里的事件?
最常见的点击操作是一种事件,刷新操作是一种事件,类似的还有很多,在程序的世界里,事件的起源是输入设备的一个信号(比如中断响应),进而将事件(或者说信号及所带的信息)一步步地传递给需要响应的监听器(JS的事件机制就比较典型)。可以看出来,事件具有几个要素:起源、事件信息、可传递、可被响应。
起源:事实上大部分的应用里讲的事件,已经是被OS,被所在APP层层传递和包装后的事件
事件信息:事件传递到不同的应用层再往下继续传递的信息可能会有所不同,APP会根据自己的需要做相应的处理
可传递:比如:JS的事件冒泡
可被响应 :这是具体业务应该关心的点,触发了的事件该做什么事
理解了事件本质再来理解 spring event 就变得非常容易了,继续往下看
二、观察者模式
有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
当我们聚焦到软件系统内部时,同样可以找到很多“事件”的缩影,观察者模式为解决这类问题提供了一个方案。
三、Spring Event
Spring Event 同样遵循事件几个特性,有事件的起源,可被传递,可被响应等,使用Spring Event的好处是可以被Spring容器管理,可以使用容器资源。
起源:在 Spring Event 中,事件的起源可能是一段业务代码,当业务变更时,调用 Event publisher post 一个Event(事件信息)
//...需要触发事件的业务方法
ExampleEvent exampleEvent = new ExampleEvent();
//exampleEvent.set...
examplePublisher.publishEvent(event);
//...需要触发事件的业务方法
ExampleEvent exampleEvent = new ExampleEvent();
//exampleEvent.set...
examplePublisher.publishEvent(event);
@Component("examplePublisher")
public class ExamplePublisherImpl implements ApplicationContextAware, ExamplePublisher {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
@Override
public void publishEvent(ExampleEvent event) {
this.context.publishEvent(event);
}
}
@Component("examplePublisher")
public class ExamplePublisherImpl implements ApplicationContextAware, ExamplePublisher {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
@Override
public void publishEvent(ExampleEvent event) {
this.context.publishEvent(event);
}
}
那么,Spring Event 内部是如何将事件 Publish 出去的呢?
//AbstractApplicationContext.java
/**
* Publish the given event to all listeners.
* <p>Note: Listeners get initialized after the MessageSource, to be able
* to access it within listener implementations. Thus, MessageSource
* implementations cannot publish events.
* @param event the event to publish (may be application-specific or a
* standard framework event)
*/
public void publishEvent(ApplicationEvent event) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
getApplicationEventMulticaster().multicastEvent(event);
if (this.parent != null) {
this.parent.publishEvent(event);
}
}
/**
* Return the internal ApplicationEventMulticaster used by the context.
* @return the internal ApplicationEventMulticaster (never {@code null})
* @throws IllegalStateException if the context has not been initialized yet
*/
private ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
if (this.applicationEventMulticaster == null) {
throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
"call 'refresh' before multicasting events via the context: " + this);
}
return this.applicationEventMulticaster;
}
//AbstractApplicationContext.java
/**
* Publish the given event to all listeners.
* <p>Note: Listeners get initialized after the MessageSource, to be able
* to access it within listener implementations. Thus, MessageSource
* implementations cannot publish events.
* @param event the event to publish (may be application-specific or a
* standard framework event)
*/
public void publishEvent(ApplicationEvent event) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
getApplicationEventMulticaster().multicastEvent(event);
if (this.parent != null) {
this.parent.publishEvent(event);
}
}
/**
* Return the internal ApplicationEventMulticaster used by the context.
* @return the internal ApplicationEventMulticaster (never {@code null})
* @throws IllegalStateException if the context has not been initialized yet
*/
private ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
if (this.applicationEventMulticaster == null) {
throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
"call 'refresh' before multicasting events via the context: " + this);
}
return this.applicationEventMulticaster;
}
事件通过 ApplicationContext 通知给相应的监听,实际上是调用 ApplicationEventMulticaster 接口实现的
//AbstractApplicationEventMulticaster.java
public void multicastEvent(final ApplicationEvent event) {
for (final ApplicationListener listener : getApplicationListeners(event)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
public void run() {
listener.onApplicationEvent(event);
}
});
}
else {
listener.onApplicationEvent(event);
}
}
}
//AbstractApplicationEventMulticaster.java
public void multicastEvent(final ApplicationEvent event) {
for (final ApplicationListener listener : getApplicationListeners(event)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
public void run() {
listener.onApplicationEvent(event);
}
});
}
else {
listener.onApplicationEvent(event);
}
}
}
突然想到一个问题,ApplicationContext并不是只处理一个事件,ApplicationEevent也并不是只有一种,不同的事件有自己的实现,那 multicasterEvent 时如何准确将事件 publish ?
/**
* Return a Collection of ApplicationListeners matching the given
* event type. Non-matching listeners get excluded early.
* @param event the event to be propagated. Allows for excluding
* non-matching listeners early, based on cached matching information.
* @return a Collection of ApplicationListeners
* @see org.springframework.context.ApplicationListener
*/
protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
Class<? extends ApplicationEvent> eventType = event.getClass();
Class sourceType = event.getSource().getClass();
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); //这里定义了一个类用来做为缓存key,实际是重写了equal,比较的是eventType&sourceType
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
else {
retriever = new ListenerRetriever(true);
LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
Set<ApplicationListener> listeners;
Set<String> listenerBeans;
synchronized (this.defaultRetriever) { //此处参考正确使用Java事件通知
listeners = new LinkedHashSet<ApplicationListener>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
}
for (ApplicationListener listener : listeners) {
if (supportsEvent(listener, eventType, sourceType)) { //通过反射判断Listener实例的参数是不是与提供的event一致
retriever.applicationListeners.add(listener);
allListeners.add(listener);
}
}
if (!listenerBeans.isEmpty()) {
BeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
retriever.applicationListenerBeans.add(listenerBeanName);
allListeners.add(listener);
}
}
}
OrderComparator.sort(allListeners);
this.retrieverCache.put(cacheKey, retriever);
return allListeners;
}
}
/**
* Return a Collection of ApplicationListeners matching the given
* event type. Non-matching listeners get excluded early.
* @param event the event to be propagated. Allows for excluding
* non-matching listeners early, based on cached matching information.
* @return a Collection of ApplicationListeners
* @see org.springframework.context.ApplicationListener
*/
protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
Class<? extends ApplicationEvent> eventType = event.getClass();
Class sourceType = event.getSource().getClass();
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); //这里定义了一个类用来做为缓存key,实际是重写了equal,比较的是eventType&sourceType
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
}
else {
retriever = new ListenerRetriever(true);
LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
Set<ApplicationListener> listeners;
Set<String> listenerBeans;
synchronized (this.defaultRetriever) { //此处参考正确使用Java事件通知
listeners = new LinkedHashSet<ApplicationListener>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
}
for (ApplicationListener listener : listeners) {
if (supportsEvent(listener, eventType, sourceType)) { //通过反射判断Listener实例的参数是不是与提供的event一致
retriever.applicationListeners.add(listener);
allListeners.add(listener);
}
}
if (!listenerBeans.isEmpty()) {
BeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
retriever.applicationListenerBeans.add(listenerBeanName);
allListeners.add(listener);
}
}
}
OrderComparator.sort(allListeners);
this.retrieverCache.put(cacheKey, retriever);
return allListeners;
}
}
至此,Spring Event 已经可以实现事件触发,前半部分已经结束了,并且我们已经知道 listener 中的方法是如何被调用了,但是这些 listener 是如何被添加到 Multicaster 的广播列表中的呢?
//AbstractApplicationContext.java
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
...
// Check for listener beans and register them.
registerListeners();
...
}
}
/**
* Add beans that implement ApplicationListener as listeners.
* Doesn't affect other listeners, which can be added without being beans.
*/
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String lisName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(lisName);
}
}
//AbstractApplicationContext.java
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
...
// Check for listener beans and register them.
registerListeners();
...
}
}
/**
* Add beans that implement ApplicationListener as listeners.
* Doesn't affect other listeners, which can be added without being beans.
*/
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String lisName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(lisName);
}
}
ApplicationContext 在启动时初始化各种 Bean,同时也会注册监听的事件(所有ApplicationListener接口的实现),这里讲的注册就是把监听类实例(只限那些特定的静态的监听实例)或者类名称放入 Multicaster 的监听队列中,只是放名称的目的是让ApplicationContext初始化完后再做注入。
与整个过程相关的还有两个对象,一个是 ApplicationListener 接口,所有Listener都要实现这个接口,否则 ApplicationContext 启动时无法注册监听,另一个是 ApplicationEvent -> EventObject,事件信息的传递需要 ApplicationEvent,事件信息需要继承 ApplicationEvent,另外ApplicationEvent本身继承自JDK的EventObject,其中 source 对象为同一个事件类,在不同的情况下,使用不同监听处理提供了可能。
另外,事件还可以设定为异步执行,只要往容器注入相应的Executor。
四、Guava EventBus
Event是事件的另一种优秀实现,相对来讲,比 Spring event 更容易上手,也更容易使用。EventBus的设计思想可参考:http://beneo.iteye.com/blog/1984072
五、相关文章
操作系统中断方式与轮询方式:http://lionwq.spaces.eepw.com.cn/articles/article/item/18936
javascript事件机制底层实现原理:
观察者模式:
正确使用Java事件通知:http://www.importnew.com/15446.html