ApplicationContext提供事件处理通过ApplicationEvent类和ApplicationListener接口。如果一个bean在容器中实现ApplicationListener接口,每次一个ApplicationEvent被发布到ApplicationContext中,这类bean就会收到这些通知。从本质上讲,这是标准的观察者设计模式。
观察者模式
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
观察者模式主要有两个角色
- Subject 观察主题对象,也可以叫被观察或者被订阅对象
- Observer 观察者或者订阅者对象,当
Subject
有变动,就会通知到每一个Observer
/**
* 观察者主题对象
*/
public interface Subject {
/**
* 订阅操作
*/
void attach(Observer observer);
/**
* 取消订阅操作
*/
void detach(Observer observer);
/**
* 通知变动
*/
void notifyChanged(Object event);
}
/**
* 观察者订阅人对象
*/
public interface Observer {
/**
* 接收变动通知
*/
void update(Object event);
}
public static class RealSubject implements Subject {
private List<Observer> observerList = new ArrayList<>();
@Override
public void attach(Observer observer) {
observerList.add(observer);
}
@Override
public void detach(Observer observer) {
observerList.remove(observer);
}
@Override
public void notifyChanged(Object event) {
for (Observer observer : observerList) {
observer.update(event);
}
}
}
public static class RealObject implements Observer {
@Override
public void update(Object event) {
System.out.println("接收到了通知:"+event);
}
}
public static void main(String[] args) {
Subject subject = new RealSubject();
Observer observer = new RealObject();
subject.attach(observer);
subject.notifyChanged(new EventObject("一个事件"));
}
在Java中观察者模式被Java API java.util.Observable和java.util.Observer实现。前者可认为是数据的发布者,后者为数据的接受者。Observable和Observer的关联关系为一对多或多对多。
public static class RealSubject extends Observable {
public void makeChanged(Object event) {
setChanged();
notifyObservers(event);
}
}
public static class RealObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("调用了-->");
}
}
public static void main(String[] args) {
RealSubject subject = new RealSubject();
RealObserver observer = new RealObserver();
subject.addObserver(observer);
subject.makeChanged(new EventObject("一个事件"));
}
要点:
-
观察者模式定义了对象之间一对多的关系。
-
主题(也就是可观察者)用一个共同的接口来更新观察者
-
观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。
-
使用此模式时,你可从被观察者处推(push)数据。
-
有多个观察者时,不可以依赖特定的通知次序。
-
Java有多种观察者模式的实现,包括了通用的java.util.Observable。
-
要注意java.util.Observable实现上所带来的一些问题。
-
如果有必要的话,可以实现自己的Observable,这并不难不要害怕。
-
Swing大量使用观察者模式, 许多GUI框架也是如此。
-
此模式也被应用在许多地方, 例如:JavaBeans、RMI。
Spring实现方式
实现Spring事件机制主要有4个类:
ApplicationEvent:事件,每个实现类表示一类事件,可携带数据。
ApplicationListener:事件监听器,用于接收事件处理时间。
ApplicationEventMulticaster:事件管理者,用于事件监听器的注册和事件的广播。
ApplicationEventPublisher:事件发布者,委托ApplicationEventMulticaster完成事件发布。
Spring中的事件:ApplicationEvent
观察者模式中传播的数据比较抽象,如Observable可发布任意的Object,而事件/监听器模式所发布的内容则有类型限制,在Java中他必须是java.util.EventObject对象,虽然在Java语言层面并无此限制,不过这是行之有年的业界规则,所以无论Java Beans还是Java AWT/Swing都遵照改规则,Spring事件自然也不会例外,所以Spring事件抽象类ApplicationEvent必然扩展EventObject。
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
private final long timestamp;
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
public final long getTimestamp() {
return this.timestamp;
}
}
EventObject并不提供默认构造器,它需要外部传递一个名为source的构造器参数用于记录并跟踪事件的来源,如Spring事件ContextRefreshedEvent,其事件源为当前ApplicationContext。
下表描述了Spring提供的标准事件:
事件 | 描述 |
---|---|
ContextRefreshedEvent | 事件发布在ApplicationContext初始化或刷新时(例如,通过在ConfigurableApplicationContext接口使用refresh()方法)。这里,“初始化”意味着所有bean加载,post-processor bean被检测到并且激活,单例预先实例化,ApplicationContext对象可以使用了。只要上下文没有关闭,可以触发多次刷新,ApplicationContext提供了一种可选择的支持这种“热”刷新。例如,XmlWebApplicationContext支持热刷新,但GenericApplicationContext并非如此。具体是在AbstractApplicationContext的finishRefresh()方法中。 |
ContextStartedEvent | 事件发布在ApplicationContext开始使用ConfigurableApplicationContext接口start()方法。这里,“开始”意味着所有生命周期bean接收到一个明确的起始信号。通常,这个信号用于明确停止后重新启动,但它也可以用于启动组件没有被配置为自动运行(例如,组件还没有开始初始化)。 |
ContextStoppedEvent | 事件发布在ApplicationContext停止时通过使用ConfigurableApplicationContext接口上的stop()方法。在这里,“停止”意味着所有生命周期bean接收一个显式的停止信号。停止上下文可以通过重新调用start()方法。 |
ContextClosedEvent | 事件发布在ApplicationContext关闭时通过关闭ConfigurableApplicationContext接口方法。这里,“封闭”意味着所有单例bean被摧毁。一个封闭的环境达到生命的终结。它不能刷新或重启。 |
RequestHandledEvent | 一个特定的web事件告诉所有能处理HTTP请求的bean 。这个事件是在请求完成后发布的。这个事件只适用于使用Spring的DispatcherServlet的web应用程序。 |
pring事件监听者:ApplicationListener
Java事件监听者必须是EventListener实现类,不过EventListener仅为标签接口,内部并没有提供任何实现方法。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
这么设计是因为他很难适用于所有事件监听场景,如MouseListener,它监听了鼠标点击、鼠标按下及鼠标释放等事件。这么设计的好处是一个实现可以集中处理不同的事件,弊端是未来接口需要增加监听方法时客户端不得不为此变更而适配。
Spring事件监听器实现采取了相反的设计理念,通过限定监听方法数量,仅抽象单一方法onApplicationEvent(ApplicaiotnEvent)将其用于监听Spring事件ApplicationEvent,当事件监听器接收到它可以处理的事件,会调用onApplicationEvent()方法。注意到ApplicationListener是泛型参数的这样可以参数化的定制事件。这意味着onApplicationEvent()方法可以保持类型安全,避免任何需要向下类型转换。你可以尽可能多的注册你希望事件侦听器,但是注意,默认情况下,事件监听器同步接收事件。这意味着publishEvent()方法会阻塞直到所有的事件监听器成处理完事件。
这种单线程同步方法的一个特点是,当一个监听器接收到一个事件时如果事务上下文可用,它运行在事务上下文的发布者线程上。如果事件的发布需要另一种策略(譬如多线程)需要实现自己的 ApplicationEventMulticaster接口类。
由于泛型参数的限制,泛型化的ApplicationListener无法监听不同类型的ApplicationEvent,如ApplicationListener<ContextRefreshedEvent>无法同时监听ContextStartedEvent。如果继续使用ApplicationListener<ApplicationEvent>又会有上述的问题。为此Spring提供了SmartApplicationListener接口:
public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
//确定此监听器是否实际支持给定的事件类型。
boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
default boolean supportsSourceType(@Nullable Class<?> sourceType) {
return true;
}
@Override
default int getOrder() {
return LOWEST_PRECEDENCE;
}
}
该接口通过supportsXXXXType方法过滤需要监听的ApplicationEvent类型和事件源类型,从而达到监听不同类型的ApplicationEvent的目的。
SmartApplicationListener是Spring3.0引入的,在4.2又引入了一个GenericApplicationListener可用来代替SmartApplicationListener,它的supportsEventType方法的参数是一个ResolvableType,更加全面的支持泛型化。
public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
boolean supportsEventType(ResolvableType eventType);
default boolean supportsSourceType(@Nullable Class<?> sourceType) {
return true;
}
@Override
default int getOrder() {
return LOWEST_PRECEDENCE;
}
}
Spring事件监听器注册/事件广播:ApplicationEventMulticaster
默认情况下Spring使用SimpleApplicationEventMulticaster广播事件,它是ApplicationEventMulticaster接口的实现类,该接口主要承担两种职责,一是关联ApplicationListener、二是广播ApplicationEvent。
public interface ApplicationEventMulticaster {
void addApplicationListener(ApplicationListener<?> listener);
void addApplicationListenerBean(String listenerBeanName);
void removeApplicationListener(ApplicationListener<?> listener);
void removeApplicationListenerBean(String listenerBeanName);
void removeAllListeners();
void multicastEvent(ApplicationEvent event);
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}
ApplicationEventMulticaster接口方法分为三类,注册事件监听器、移除事件监听器、发布事件。
SimpleApplicationEventMulticaster,它的父类实现了前5个方法依靠一个内部类ListenerRetriever维护了一个Set<ApplicationListener<?>>,本质事件监听器的注册或移除就是对这个Set的添加和移除操作。
public abstract class AbstractApplicationEventMulticaster
implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
synchronized (this.retrievalMutex) {
// 如果已经注册,则显式删除代理的目标,以避免对同一个侦听器进行双重调用。
Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
if (singletonTarget instanceof ApplicationListener) {
this.defaultRetriever.applicationListeners.remove(singletonTarget);
}
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}
@Override
public void removeApplicationListener(ApplicationListener<?> listener) {
synchronized (this.retrievalMutex) {
this.defaultRetriever.applicationListeners.remove(listener);
this.retrieverCache.clear();
}
}
private class ListenerRetriever {
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
private final boolean preFiltered;
public ListenerRetriever(boolean preFiltered) {
this.preFiltered = preFiltered;
}
public Collection<ApplicationListener<?>> getApplicationListeners() {
List<ApplicationListener<?>> allListeners = new ArrayList<>(
this.applicationListeners.size() + this.applicationListenerBeans.size());
allListeners.addAll(this.applicationListeners);
if (!this.applicationListenerBeans.isEmpty()) {
BeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : this.applicationListenerBeans) {
try {
ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (this.preFiltered || !allListeners.contains(listener)) {
allListeners.add(listener);
}
}
catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition) disappeared -
// probably in the middle of the destruction phase
}
}
}
if (!this.preFiltered || !this.applicationListenerBeans.isEmpty()) {
AnnotationAwareOrderComparator.sort(allListeners);
}
return allListeners;
}
}
}
接口后两个方法由子类实现,可以看到SimpleApplicationEventMulticaster拥有一个Executor和ErrorHandler, 分别表示监听器的调用线程池(如果不想使用单线程同步处理则可以设置一个线程池)和监听器处理事件失败的处理者(如果设置了的话)否则抛异常。
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@Nullable
private Executor taskExecutor;
@Nullable
private ErrorHandler errorHandler;
public SimpleApplicationEventMulticaster() {
}
public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
setBeanFactory(beanFactory);
}
public void setTaskExecutor(@Nullable Executor taskExecutor) {
this.taskExecutor = taskExecutor;
}
public void setErrorHandler(@Nullable ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
@Override
public void multicastEvent(ApplicationEvent event) {
//广播事件,可以自动分析出ApplicationEvent是那种事件类型
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//调用父类方法getApplicationListeners只取得能处理此类事件的时间监听器,依次处理
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
return ResolvableType.forInstance(event);
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
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;
}
}
}
}
SimpleApplicationEventMulticaster在进行广播时,调用父类方法AbstractApplicationEventMulticaster.getApplicationListeners(ApplicationEvent, ResolvableType)返回一组能够处理当前事件类型的事件监听器,依次去调用事件监听器的onApplicationEvent方法,默认是同步调用,也可以通过setTaskExecutor方法传入一个Executor来达到异步处理的方式。
下面重点看一下AbstractApplicationEventMulticaster#getApplicationListeners(ApplicationEvent, ResolvableType)
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> 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);
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);
Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
else {
// No ListenerRetriever caching -> no synchronization necessary
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
上面代码主要检索缓存中是否存在,实际上为给定事件和源类型检索应用程序监听器的是retrieveApplicationListeners方法。
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {
List<ApplicationListener<?>> allListeners = new ArrayList<>();
Set<ApplicationListener<?>> listeners;
Set<String> listenerBeans;
synchronized (this.retrievalMutex) {
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
// Add programmatically registered listeners, including ones coming
// from ApplicationListenerDetector (singleton beans and inner beans).
for (ApplicationListener<?> listener : listeners) {
//GenericApplicationListener、SmartApplicationListener通过supportsXXX方法
//普通ApplicationListener通过具体事件类型过滤
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
allListeners.add(listener);
}
}
// Add listeners by bean name, potentially overlapping with programmatically
// registered listeners above - but here potentially with additional metadata.
if (!listenerBeans.isEmpty()) {
ConfigurableBeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
ApplicationListener<?> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
if (beanFactory.isSingleton(listenerBeanName)) {
retriever.applicationListeners.add(listener);
}
else {
retriever.applicationListenerBeans.add(listenerBeanName);
}
}
allListeners.add(listener);
}
}
else {
// Remove non-matching listeners that originally came from
// ApplicationListenerDetector, possibly ruled out by additional
// BeanDefinition metadata (e.g. factory method generics) above.
Object listener = beanFactory.getSingleton(listenerBeanName);
if (retriever != null) {
retriever.applicationListeners.remove(listener);
}
allListeners.remove(listener);
}
}
catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition) disappeared -
// probably in the middle of the destruction phase
}
}
}
AnnotationAwareOrderComparator.sort(allListeners);
if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
retriever.applicationListeners.clear();
retriever.applicationListeners.addAll(allListeners);
}
return allListeners;
}
Spring事件发布:ApplicationEventPublisher
ApplicationEventPublisher仅存在两个发布ApplicationEvent的重载方法publishEvent,并无关联ApplicationListener的操作方法。不过ApplicationEventPublisher接口被ApplicationContext扩展,因此无论使用哪种ApplicationContext实例均具备发布ApplicationEvent的能力。同时ApplicationContext的子接口ConfigurableApplicationContext提供了添加ApplicationListener实例的关联方法addApplicationListener(ApplicationListener)。
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
Assert.notNull(listener, "ApplicationListener must not be null");
if (this.applicationEventMulticaster != null) {
this.applicationEventMulticaster.addApplicationListener(listener);
}
this.applicationListeners.add(listener);
}
可以看到通过ConfigurableApplicationContext的addApplicationListener方法注册事件监听器也会注册到内部成员变量applicationEventMulticaster中。 既然ApplicationContext对象是ApplicationEventPublisher实例,则不难发现AbstractApplicationContext完全实现了ApplicationEventPublisher接口:
@Override
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
@Override
public void publishEvent(Object event) {
publishEvent(event, null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
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);
}
}
}
明显地在publishEvent(Object,ResolvableType)方法中显示地调用了getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType), 而applicationEventMulticaster属性又由initApplicationEventMulticaster()方法初始化。
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
在applicationEventMulticaster初始化中首先判断Spring应用上下文是否存在名为applicationEventMulticaster且类型为ApplicationEventMulticaster的bean, 如果不存在则将其构造为SimpleApplicationEventMulticaster对象注册到Spring容器中。
Spring注解驱动事件监听:@EventListener
@EventListener必须标记在Spring托管bean的public方法中,并且支持单一类型事件监听,除此之外单个@EventListener方法也支持多种事件类型的监听。
当@EventListener方法监听一个或多个ApplicationEvent时,其参数可为零到一个ApplicationEvent。
原理是使用了EventListenerMethodProcessor将每个@EventListener方法转换成了ApplicationListener对象注册到Spring容器中。
@EventListener方法的执行顺序可通过@Order注解来实现监听顺序,通过@Async注解来实现异步监听调用。
@EventListener方法与ApplicationListener接口对比
监听类型 | 访问性 | 顺序控制 | 返回类型 | 参数数量 | 参数类型 | 泛型事件 |
---|---|---|---|---|---|---|
@EventListener同步方法 | public | @Order | 任意 | 0或1 | 事件类型或泛型参数类型 | 支持 |
@EventListener异步方法 | public | @Order | 非原生类型 | 0或1 | 事件类型或泛型参数类型 | 支持 |
实现ApplicationListener | public | @Order或Ordered | void | 1 | 事件类型 | 不支持 |