前言
Spring Event-Driven 是Java生态中关于Event-Driven编程范式的最佳实践标准之一 ,在Spring框架之前,已经有JDK、Servlet、EJB等框架进行过观察者模式、事件驱动、注解式监听等尝试,本文简要分析一下Spring在事件驱动方面的设计。
从观察者模式说起
Pub-Sub设计模式应该是由观察者模式变种而来 ,所以我们有必要了解一下在JDK中提供的Observable/Observer实现标准。
pacakge java.util; public class Observable { private boolean changed = false; private Vector
obs; /** Construct an Observable with zero Observers. */ public Observable() { obs = new Vector<>(); } /** * Adds an observer to the set of observers for this object, provided * that it is not the same as some observer already in the set. * The order in which notifications will be delivered to multiple * observers is not specified. See the class comment. * * @param o an observer to be added. * @throws NullPointerException if the parameter o is null. */ public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } /** * Deletes an observer from the set of observers of this object. * Passing null
to this method will have no effect. * @param o the observer to be deleted. */ public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } /** * If this object has changed, as indicated by the *hasChanged
method, then notify all of its observers * and then call theclearChanged
method to * indicate that this object has no longer changed. ** Each observer has its
update
method called with two * arguments: this observable object andnull
. In other * words, this method is equivalent to: ** notifyObservers(null)* * @see java.util.Observable#clearChanged() * @see java.util.Observable#hasChanged() * @see java.util.Observer#update(java.util.Observable, java.lang.Object) */ public void notifyObservers() { notifyObservers(null); } /** * If this object has changed, as indicated by the *hasChanged
method, then notify all of its observers * and then call theclearChanged
method to indicate * that this object has no longer changed. *
* Each observer has its
update
method called with two * arguments: this observable object and thearg
argument. * * @param arg any object. * @see java.util.Observable#clearChanged() * @see java.util.Observable#hasChanged() * @see java.util.Observer#update(java.util.Observable, java.lang.Object) */ public void notifyObservers(Object arg) { /* * a temporary array buffer, used as a snapshot of the state of * current Observers. */ Object[] arrLocal; synchronized (this) { /* We don't want the Observer doing callbacks into * arbitrary code while holding its own Monitor. * The code where we extract each Observable from * the Vector and store the state of the Observer * needs synchronization, but notifying observers * does not (should not). The worst result of any * potential race-condition here is that: * 1) a newly-added Observer will miss a * notification in progress * 2) a recently unregistered Observer will be * wrongly notified when it doesn't care */ if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } /** * Clears the observer list so that this object no longer has any observers. */ public synchronized void deleteObservers() { obs.removeAllElements(); } /** * Marks this Observable object as having been changed; the * hasChanged method will now return true. */ protected synchronized void setChanged() { changed = true; } /** * Indicates that this object has no longer changed, or that it has * already notified all of its observers of its most recent change, * so that the hasChanged method will now return false. * This method is called automatically by the *notifyObservers
methods. * * @see java.util.Observable#notifyObservers() * @see java.util.Observable#notifyObservers(java.lang.Object) */ protected synchronized void clearChanged() { changed = false; } /** * Tests if this object has changed. * * @returntrue
if and only if thesetChanged
* method has been called more recently than the *clearChanged
method on this object; *false
otherwise. * @see java.util.Observable#clearChanged() * @see java.util.Observable#setChanged() */ public synchronized boolean hasChanged() { return changed; } /** * Returns the number of observers of this Observable object. * * @return the number of observers of this object. */ public synchronized int countObservers() { return obs.size(); } }
package java.util;
/**
* A class can implement the Observer
interface when it
* wants to be informed of changes in observable objects.
*
* @author Chris Warth
* @see java.util.Observable
* @since JDK1.0
*/
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an Observable object's
* notifyObservers
method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the notifyObservers
* method.
*/
void update(Observable o, Object arg);
}
如上图,JDK推荐我们基于Observable和Observer拓展来进行观察者模式编码。
实践代码如下:
public class ObserverDemo {
public static void main(String[] args) {
EventObservable eventObservable = new EventObservable();
eventObservable.addObserver(new EventObserver());
eventObservable.notifyObservers("hello world");
}
static class EventObservable extends Observable {
@Override
public void notifyObservers(Object payload) {
setChanged();
super.notifyObservers(payload);
clearChanged();
}
}
static class EventObserver implements Observer {
@Override
public void update(Observable o, Object payload) {
System.out.println("Receive message :" + payload);
}
}
}
如上图,简单分析一下,这一套标准蹩脚的地方在于:
- Observable进行notify的时候,必须要setChanged状态,才能真正被观察到,否则会忽略掉变化,直接被return了,具体见源码如下:
public void notifyObservers(Object arg) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal;
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
- setChanged方法是个protected级别 ,必须得继承一个Observable才能玩。
如下:
protected synchronized void setChanged() {
changed = true;
}
所以整体封装成如下代码:
static class EventObservable extends Observable {
@Override
public void notifyObservers(Object payload) {
setChanged();
super.notifyObservers(payload);
clearChanged();
}
}
- 拓展性太差
综上所述,JDK 9版本官方直接推荐使用java.util.concurrent.Flow ,意思是让你别玩这个Observable了。
事件驱动的模型
上面提到了Observable/Observer模型 ,接下来说一说Event-Driven方面在Spring之前前人有过哪些尝试,相应的事件编程模型是什么样的。
从监听粒度上分为单事件监听和多事件监听
- 单事件监听模型
指的是一个Listener一次只消费一个事件,比如Java Beans:
public interface PropertyChangeListener extends java.util.EventListener {
/**
* This method gets called when a bound property is changed.
* @param evt A PropertyChangeEvent object describing the event source
* and the property that has changed.
*/
void propertyChange(PropertyChangeEvent evt);
}
- 多事件监听模型
指的是一个Listener同时监听多个事件,比如AWT、Android中
public interface MouseListener extends EventListener {
/**
* Invoked when the mouse button has been clicked (pressed
* and released) on a component.
*/
public void mouseClicked(MouseEvent e);
/**
* Invoked when a mouse button has been pressed on a component.
*/
public void mousePressed(MouseEvent e);
/**
* Invoked when a mouse button has been released on a component.
*/
public void mouseReleased(MouseEvent e);
/**
* Invoked when the mouse enters a component.
*/
public void mouseEntered(MouseEvent e);
/**
* Invoked when the mouse exits a component.
*/
public void mouseExited(MouseEvent e);
}
从监听者注册方式来区分,分为接口式和注解式。
- 接口式
顾名思义就是通过接口实现的方式,通过addListener的方式注册。
context.addApplicationListener(new ApplicationListener() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("Receive event : " + event);
}
});
- 注解式
顾名思义就是用注解指定Listener ,在Spring之前已经有开源产品这么玩过了。
如Servlet 3.0 WebListener、JPA、jdk PostConstruct、EJB等注解。
@EventListener
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("@EventListener Receive event : " + event);
}
JDK中的事件驱动规范
在Java中,规范要求事件驱动模型是基于以下两个类去拓展的。
package java.util; /** *
* The root class from which all event state objects shall be derived. *
* All Events are constructed with a reference to the object, the "source", * that is logically deemed to be the object upon which the Event in question * initially occurred upon. * * @since JDK1.1 */ public class EventObject implements java.io.Serializable { private static final long serialVersionUID = 5516075349620653480L; /** * The object on which the Event initially occurred. */ protected transient Object source; /** * Constructs a prototypical Event. * * @param source The object on which the Event initially occurred. * @exception IllegalArgumentException if source is null. */ public EventObject(Object source) { if (source == null) throw new IllegalArgumentException("null source"); this.source = source; } /** * The object on which the Event initially occurred. * * @return The object on which the Event initially occurred. */ public Object getSource() { return source; } /** * Returns a String representation of this EventObject. * * @return A a String representation of this EventObject. */ public String toString() { return getClass().getName() + "[source=" + source + "]"; } }
package java.util;
/**
* A tagging interface that all event listener interfaces must extend.
* @since JDK1.1
*/
public interface EventListener {
}
可以看到,关于事件驱动并没有给出任何实现,只是定义了标准的事件模型和监听标准,EventListner就有点类似于ArrayList实现了RamdomAccess标记。
Spring事件模型
了解以上背景以后,我们可以揭开Spring编程模型的面纱了,实际上正是基于前人的基础上设计。
package org.springframework.context; import java.util.EventListener; /** * Interface to be implemented by application event listeners. * *
Based on the standard {@code java.util.EventListener} interface * for the Observer design pattern. * *
As of Spring 3.0, an {@code ApplicationListener} can generically declare * the event type that it is interested in. When registered with a Spring * {@code ApplicationContext}, events will be filtered accordingly, with the * listener getting invoked for matching event objects only. * * @author Rod Johnson * @author Juergen Hoeller * @param
the specific {@code ApplicationEvent} subclass to listen to * @see org.springframework.context.ApplicationEvent * @see org.springframework.context.event.ApplicationEventMulticaster * @see org.springframework.context.event.EventListener */ @FunctionalInterface public interface ApplicationListener extends EventListener { /** * Handle an application event. * @param event the event to respond to */ void onApplicationEvent(E event); }
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context;
import java.util.EventObject;
/**
* Class to be extended by all application events. Abstract as it
* doesn't make sense for generic events to be published directly.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see org.springframework.context.ApplicationListener
* @see org.springframework.context.event.EventListener
*/
public abstract class ApplicationEvent extends EventObject {
/** use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = 7099057708183571937L;
/** System time when the event happened. */
private final long timestamp;
/**
* Create a new {@code ApplicationEvent}.
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
/**
* Return the system time in milliseconds when the event occurred.
*/
public final long getTimestamp() {
return this.timestamp;
}
}
正是一个标准的java.util事件编程模型拓展,而Spring在此基础上做了进一步拓展,加入了时间戳。
并且还有Spring特有的Context事件模型如下:
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.event;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
/**
* Base class for events raised for an {@code ApplicationContext}.
*
* @author Juergen Hoeller
* @since 2.5
*/
@SuppressWarnings("serial")
public abstract class ApplicationContextEvent extends ApplicationEvent {
/**
* Create a new ContextStartedEvent.
* @param source the {@code ApplicationContext} that the event is raised for
* (must not be {@code null})
*/
public ApplicationContextEvent(ApplicationContext source) {
super(source);
}
/**
* Get the {@code ApplicationContext} that the event was raised for.
*/
public final ApplicationContext getApplicationContext() {
return (ApplicationContext) getSource();
}
}
可以看到ApplicationContextEvent在ApplicationEvent基础上,又增加了ApplicationContext对象进来,这是为了传递Spring容器上下文,基于这个上下文事件有四个标准内置事件。
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.event;
import org.springframework.context.ApplicationContext;
/**
* Event raised when an {@code ApplicationContext} gets initialized or refreshed.
*
* @author Juergen Hoeller
* @since 04.03.2003
* @see ContextClosedEvent
*/
@SuppressWarnings("serial")
public class ContextRefreshedEvent extends ApplicationContextEvent {
/**
* Create a new ContextRefreshedEvent.
* @param source the {@code ApplicationContext} that has been initialized
* or refreshed (must not be {@code null})
*/
public ContextRefreshedEvent(ApplicationContext source) {
super(source);
}
}
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.event;
import org.springframework.context.ApplicationContext;
/**
* Event raised when an {@code ApplicationContext} gets started.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 2.5
* @see ContextStoppedEvent
*/
@SuppressWarnings("serial")
public class ContextStartedEvent extends ApplicationContextEvent {
/**
* Create a new ContextStartedEvent.
* @param source the {@code ApplicationContext} that has been started
* (must not be {@code null})
*/
public ContextStartedEvent(ApplicationContext source) {
super(source);
}
}
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.event;
import org.springframework.context.ApplicationContext;
/**
* Event raised when an {@code ApplicationContext} gets closed.
*
* @author Juergen Hoeller
* @since 12.08.2003
* @see ContextRefreshedEvent
*/
@SuppressWarnings("serial")
public class ContextClosedEvent extends ApplicationContextEvent {
/**
* Creates a new ContextClosedEvent.
* @param source the {@code ApplicationContext} that has been closed
* (must not be {@code null})
*/
public ContextClosedEvent(ApplicationContext source) {
super(source);
}
}
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.event;
import org.springframework.context.ApplicationContext;
/**
* Event raised when an {@code ApplicationContext} gets stopped.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 2.5
* @see ContextStartedEvent
*/
@SuppressWarnings("serial")
public class ContextStoppedEvent extends ApplicationContextEvent {
/**
* Create a new ContextStoppedEvent.
* @param source the {@code ApplicationContext} that has been stopped
* (must not be {@code null})
*/
public ContextStoppedEvent(ApplicationContext source) {
super(source);
}
}
在SpringContext进行refresh、start、stop、close时,分别会产生以上事件。
Spring接口式监听事件实践
我们刚好借容器内置事件进行接口式监听的实践。
public class ApplicationListenerDemo {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.register(ApplicationListenerDemo.class);
context.addApplicationListener(new ApplicationListener() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("Receive event : " + event);
}
});
context.refresh();
context.start();
context.stop();
context.close();
}
}
Receive event : org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@5910e440, started on Sun Jun 20 15:33:56 CST 2021]
Receive event : org.springframework.context.event.ContextStartedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@5910e440, started on Sun Jun 20 15:33:56 CST 2021]
Receive event : org.springframework.context.event.ContextStoppedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@5910e440, started on Sun Jun 20 15:33:56 CST 2021]
Receive event : org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@5910e440, started on Sun Jun 20 15:33:56 CST 2021]
Spring注解式监听事件实践
public class ApplicationListenerDemo {
public static void main(String[] args) {
//GenericApplicationContext context = new GenericApplicationContext();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ApplicationListenerDemo.class);
context.refresh();
context.start();
context.stop();
context.close();
}
@EventListener
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("@EventListener Receive event : " + event);
}
}
@EventListener Receive event : org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@21b8d17c, started on Sun Jun 20 15:36:25 CST 2021]
@EventListener Receive event : org.springframework.context.event.ContextStartedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@21b8d17c, started on Sun Jun 20 15:36:25 CST 2021]
@EventListener Receive event : org.springframework.context.event.ContextStoppedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@21b8d17c, started on Sun Jun 20 15:36:25 CST 2021]
@EventListener Receive event : org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@21b8d17c, started on Sun Jun 20 15:36:25 CST 2021]
注解还有以下特性:
- 异步
@EventListener
@Async
public void onApplicationEventAsync(ApplicationEvent event) {
System.out.println("@EventListener Receive event async: " + event);
}
@EnableAsync
@Configuration
public class ApplicationListenerDemo {
}
- 顺序
@EventListener
@Order(1)
public void onApplicationEvent1(ApplicationEvent event) {
System.out.println("@EventListener Receive event order 1");
}
@EventListener
@Order(2)
public void onApplicationEvent2(ApplicationEvent event) {
System.out.println("@EventListener Receive event order 2");
}
- 支持多ApplicationEvent类型,无需接口约束
@EventListener
public void onApplicationEvent2(ContextStartedEvent event) {
System.out.println("@EventListener Receive ContextStartedEvent order 2");
}
- 泛型
@EventListener
public void onApplicationEvent2(ContextStartedEvent event) {
System.out.println("@EventListener Receive ContextStartedEvent order 2");
}
Spring 4.2版本对于PayloadEvent的支持。
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
}
}
可以看到4.2以后不再强制要求Event是ApplicationEvent的子类,而是适配成了PayloadApplicationEvent对象。
/* * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.context; import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableTypeProvider; import org.springframework.util.Assert; /** * An {@link ApplicationEvent} that carries an arbitrary payload. * *
Mainly intended for internal use within the framework. * * @author Stephane Nicoll * @since 4.2 * @param
the payload type of the event */ @SuppressWarnings("serial") public class PayloadApplicationEvent extends ApplicationEvent implements ResolvableTypeProvider { private final T payload; /** * Create a new PayloadApplicationEvent. * @param source the object on which the event initially occurred (never {@code null}) * @param payload the payload object (never {@code null}) */ public PayloadApplicationEvent(Object source, T payload) { super(source); Assert.notNull(payload, "Payload must not be null"); this.payload = payload; } @Override public ResolvableType getResolvableType() { return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getPayload())); } /** * Return the payload of the event. */ public T getPayload() { return this.payload; } }
Spring 事件发送器依赖注入和查找
- ApplicationEventPublisher
1.我们可以直接依赖注入ApplicationEventPublisher来进行事件发送
public class ApplicationListenerDemo {
@Autowired
ApplicationEventPublisher applicationEventPublisher;
@PostConstruct
public void init() {
applicationEventPublisher.publishEvent("ExtendSpringEvent");
}
}
- 我们可以通过Aware回调
public class ApplicationListenerDemo implements ApplicationEventPublisherAware {
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
applicationEventPublisher.publishEvent("ExtendSpringEvent");
}
@PostConstruct
public void init() {
applicationEventPublisher.publishEvent("ExtendSpringEvent");
}
}
- 通过ApplicationContext容器间接引入
在AbstractApplicationContext中有publishEvent方法,本身就是ApplicationEventPublisher接口的实现,委托applicationEventMulticaster进行事件发送,所以如果是ApplicationContext上下文中,是可以直接调publishEvent发送事件的。
其实底层都是通过ApplicationEventMultiCaster来实现的,那我们来分析一下源码。
首先看一下publishEvent实现,来看看ApplicationEventMultiCaster的作用。
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);
}
}
}
payload这段讲过了,下面来到earlyApplicationEvents判断这里,这里是解决Spring 3.0版本的bug ,因为在BeanPostProcesser生命周期时,ApplicationEventMultiCaster还没有init,所以publishEvent会报错,这里用一组earlyEvents暂存。
而初始化完成后的生命周期中,直接进入else逻辑,调用getApplicationEventMulticaster进行事件发送。
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;
}
/* * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.context.event; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; /** * Interface to be implemented by objects that can manage a number of * {@link ApplicationListener} objects and publish events to them. * *
An {@link org.springframework.context.ApplicationEventPublisher}, typically * a Spring {@link org.springframework.context.ApplicationContext}, can use an * {@code ApplicationEventMulticaster} as a delegate for actually publishing events. * * @author Rod Johnson * @author Juergen Hoeller * @author Stephane Nicoll * @see ApplicationListener */ public interface ApplicationEventMulticaster { /** * Add a listener to be notified of all events. * @param listener the listener to add */ void addApplicationListener(ApplicationListener listener); /** * Add a listener bean to be notified of all events. * @param listenerBeanName the name of the listener bean to add */ void addApplicationListenerBean(String listenerBeanName); /** * Remove a listener from the notification list. * @param listener the listener to remove */ void removeApplicationListener(ApplicationListener listener); /** * Remove a listener bean from the notification list. * @param listenerBeanName the name of the listener bean to remove */ void removeApplicationListenerBean(String listenerBeanName); /** * Remove all listeners registered with this multicaster. *
After a remove call, the multicaster will perform no action * on event notification until new listeners are registered. */ void removeAllListeners(); /** * Multicast the given application event to appropriate listeners. *
Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)} * if possible as it provides better support for generics-based events. * @param event the event to multicast */ void multicastEvent(ApplicationEvent event); /** * Multicast the given application event to appropriate listeners. *
If the {@code eventType} is {@code null}, a default type is built * based on the {@code event} instance. * @param event the event to multicast * @param eventType the type of event (can be {@code null}) * @since 4.2 */ void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType); }
/* * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.context.event; import java.util.concurrent.Executor; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; import org.springframework.util.ErrorHandler; /** * Simple implementation of the {@link ApplicationEventMulticaster} interface. * *
Multicasts all events to all registered listeners, leaving it up to * the listeners to ignore events that they are not interested in. * Listeners will usually perform corresponding {@code instanceof} * checks on the passed-in event object. * *
By default, all listeners are invoked in the calling thread. * This allows the danger of a rogue listener blocking the entire application, * but adds minimal overhead. Specify an alternative task executor to have * listeners executed in different threads, for example from a thread pool. * * @author Rod Johnson * @author Juergen Hoeller * @author Stephane Nicoll * @see #setTaskExecutor */ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { @Nullable private Executor taskExecutor; @Nullable private ErrorHandler errorHandler; /** * Create a new SimpleApplicationEventMulticaster. */ public SimpleApplicationEventMulticaster() { } /** * Create a new SimpleApplicationEventMulticaster for the given BeanFactory. */ public SimpleApplicationEventMulticaster(BeanFactory beanFactory) { setBeanFactory(beanFactory); } /** * Set a custom executor (typically a {@link org.springframework.core.task.TaskExecutor}) * to invoke each listener with. *
Default is equivalent to {@link org.springframework.core.task.SyncTaskExecutor}, * executing all listeners synchronously in the calling thread. *
Consider specifying an asynchronous task executor here to not block the * caller until all listeners have been executed. However, note that asynchronous * execution will not participate in the caller's thread context (class loader, * transaction association) unless the TaskExecutor explicitly supports this. * @see org.springframework.core.task.SyncTaskExecutor * @see org.springframework.core.task.SimpleAsyncTaskExecutor */ public void setTaskExecutor(@Nullable Executor taskExecutor) { this.taskExecutor = taskExecutor; } /** * Return the current task executor for this multicaster. */ @Nullable protected Executor getTaskExecutor() { return this.taskExecutor; } /** * Set the {@link ErrorHandler} to invoke in case an exception is thrown * from a listener. *
Default is none, with a listener exception stopping the current * multicast and getting propagated to the publisher of the current event. * If a {@linkplain #setTaskExecutor task executor} is specified, each * individual listener exception will get propagated to the executor but * won't necessarily stop execution of other listeners. *
Consider setting an {@link ErrorHandler} implementation that catches * and logs exceptions (a la * {@link org.springframework.scheduling.support.TaskUtils#LOG_AND_SUPPRESS_ERROR_HANDLER}) * or an implementation that logs exceptions while nevertheless propagating them * (e.g. {@link org.springframework.scheduling.support.TaskUtils#LOG_AND_PROPAGATE_ERROR_HANDLER}). * @since 4.1 */ public void setErrorHandler(@Nullable ErrorHandler errorHandler) { this.errorHandler = errorHandler; } /** * Return the current error handler for this multicaster. * @since 4.1 */ @Nullable protected ErrorHandler getErrorHandler() { return this.errorHandler; } @Override public void multicastEvent(ApplicationEvent event) { multicastEvent(event, resolveDefaultEventType(event)); } @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for (ApplicationListener listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } } private ResolvableType resolveDefaultEventType(ApplicationEvent event) { return ResolvableType.forInstance(event); } /** * Invoke the given listener with the given event. * @param listener the ApplicationListener to invoke * @param event the current event to propagate * @since 4.1 */ 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({"rawtypes", "unchecked"}) 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.isTraceEnabled()) { logger.trace("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } } private boolean matchesClassCastMessage(String classCastMessage, Class eventClass) { // On Java 8, the message starts with the class name: "java.lang.String cannot be cast..." if (classCastMessage.startsWith(eventClass.getName())) { return true; } // On Java 11, the message starts with "class ..." a.k.a. Class.toString() if (classCastMessage.startsWith(eventClass.toString())) { return true; } // On Java 9, the message used to contain the module name: "java.base/java.lang.String cannot be cast..." int moduleSeparatorIndex = classCastMessage.indexOf('/'); if (moduleSeparatorIndex != -1 && classCastMessage.startsWith(eventClass.getName(), moduleSeparatorIndex + 1)) { return true; } // Assuming an unrelated class cast failure... return false; } }
所以最终都是通过SimpleApplicationEventMulticaster#multicastEvent实现的,里面可以看到,如果没有指定线程池,将会同步调用消费者,所以Spring默认事件是主线程同步执行的。并且ErrorHandler的逻辑也在此可以看到。
Spring全家桶中关于事件发送都是基于SimpleApplicationEventMulticaster实现的。
ApplicationEventMultiCaster初始化
那么这个SimpleApplicationEventMulticaster是什么时候初始化的呢?
@Override
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.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
refresh方法这个高频面试点不用多介绍了,我们直接进入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() + "]");
}
}
}
所以我们可以看到,Spring首先通过beanName查找依赖,如果找不到,new 一个SimpleApplicationEventMulticaster实现并且通过beanFactory手动register单例到容器里。
那么我们就可以想到,如果我自己注册一个实现别名为applicationEventMulticaster,其实是可以跳过simple实现拓展着玩的,如下:
@Configuration
public class ApplicationListenerDemo {
@Autowired
ApplicationEventPublisher applicationEventPublisher;
@PostConstruct
public void init() {
applicationEventPublisher.publishEvent(new ExtendSpringEvent("ExtendSpringEvent"));
}
@Component(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
static class MyApplicationEventMulticaster implements ApplicationEventMulticaster {
@Override
public void addApplicationListener(ApplicationListener listener) {
System.out.println("MyApplicationEventMulticaster addApplicationListener");
}
@Override
public void addApplicationListenerBean(String listenerBeanName) {
}
@Override
public void removeApplicationListener(ApplicationListener listener) {
}
@Override
public void removeApplicationListenerBean(String listenerBeanName) {
}
@Override
public void removeAllListeners() {
}
@Override
public void multicastEvent(ApplicationEvent event) {
}
@Override
public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
System.out.println("MyApplicationEventMulticaster multicastEvent: " + event);
}
}
public static void main(String[] args) {
//GenericApplicationContext context = new GenericApplicationContext();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ApplicationListenerDemo.class);
context.refresh();
context.start();
context.stop();
context.close();
}
static class ExtendSpringEvent extends ApplicationEvent {
/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public ExtendSpringEvent(Object source) {
super(source);
}
}
}
MyApplicationEventMulticaster multicastEvent: org.thinking.in.spring.ioc.lookup.event.ApplicationListenerDemo$ExtendSpringEvent[source=ExtendSpringEvent]
MyApplicationEventMulticaster multicastEvent: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@21b8d17c, started on Sun Jun 20 16:05:27 CST 2021]
MyApplicationEventMulticaster multicastEvent: org.springframework.context.event.ContextStartedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@21b8d17c, started on Sun Jun 20 16:05:27 CST 2021]
MyApplicationEventMulticaster multicastEvent: org.springframework.context.event.ContextStoppedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@21b8d17c, started on Sun Jun 20 16:05:27 CST 2021]
MyApplicationEventMulticaster multicastEvent: org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@21b8d17c, started on Sun Jun 20 16:05:27 CST 2021]
但是一般不需要拓展这个,因为还需要封装调用listener很多特性逻辑,你知道有这个点装逼就可以了,气质这块绝对不能颓。
Spring 3.0 的bug是啥呢?回过头看refresh方法编排。
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
registerBeanPostProcessors的时候,很明显initApplicationEventMulticaster还没调到,那么就没有SimpleApplicationEventMulticaster这个单例,这时候事件肯定发不了报错了,所以弄出来一个earlyEvents暂存。
ApplicationEventMulticaster模式上是有点像Observable的,所以说Spring事件整体架构上是借鉴了很多前人的思想,然后改良了实现。
Multicaster异步和异常处理机制拓展
- 异步处理
异常处理机制是SimpleApplicationEventMulticaster独有的,ApplicationEventMulticaster接口中并没有taskExecutor对象,所以要通过类型判断强转来增加接口式监听的异步化。首先通过beanName拿到ApplicationEventMulticaster接口实例,然后把提前创建好的线程池赋值给强转后的SimpleMulticaster。
ApplicationEventMulticaster applicationEventMulticaster = context.getBean(
AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME,
ApplicationEventMulticaster.class);
ExecutorService executorService =
new ThreadPoolExecutor(10, 10,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(),
new CustomizableThreadFactory("my-pool-"));
if (applicationEventMulticaster instanceof SimpleApplicationEventMulticaster) {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster
= (SimpleApplicationEventMulticaster)applicationEventMulticaster;
simpleApplicationEventMulticaster.setTaskExecutor(executorService);
}
要注意的是这里线程池不能自动关闭,两种做法吧 ,监听容器Close事件来关闭,或者用jvm的shutdownhook。
context.addApplicationListener(new ApplicationListener() {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
if (!executorService.isShutdown()) {
executorService.shutdown();
}
}
});
- 异常处理机制
同上,给一下异常处理实现。
simpleApplicationEventMulticaster.setErrorHandler(new ErrorHandler() {
@Override
public void handleError(Throwable throwable) {
System.out.printf("事件消费异常:%s\n", throwable.getMessage());
}
});
结合起来的实践案例如下:
public class ApplicationListenerDemo {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.refresh();
ApplicationEventMulticaster applicationEventMulticaster = context.getBean(
AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME,
ApplicationEventMulticaster.class);
ExecutorService executorService =
new ThreadPoolExecutor(10, 10,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(),
new CustomizableThreadFactory("my-pool-"));
if (applicationEventMulticaster instanceof SimpleApplicationEventMulticaster) {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster
= (SimpleApplicationEventMulticaster)applicationEventMulticaster;
simpleApplicationEventMulticaster.setTaskExecutor(executorService);
simpleApplicationEventMulticaster.setErrorHandler(new ErrorHandler() {
@Override
public void handleError(Throwable throwable) {
System.out.printf("事件消费异常:%s\n", throwable.getMessage());
}
});
}
context.addApplicationListener(new ApplicationListener() {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
if (!executorService.isShutdown()) {
executorService.shutdown();
}
}
});
context.addApplicationListener(new ApplicationListener() {
@Override
public void onApplicationEvent(ExtendSpringEvent event) {
System.err.printf("线程:%s 消费事件%s", Thread.currentThread().getName(), event);
}
});
context.addApplicationListener(new ApplicationListener() {
@Override
public void onApplicationEvent(ExtendSpringEvent event) {
throw new RuntimeException("onApplicationEvent报错了");
}
});
context.publishEvent(new ExtendSpringEvent("ExtendSpringEvent"));
context.close();
}
static class ExtendSpringEvent extends ApplicationEvent {
/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public ExtendSpringEvent(Object source) {
super(source);
}
}
}
事件消费异常:onApplicationEvent报错了
线程:my-pool-1 消费事件org.thinking.in.spring.ioc.lookup.event.ApplicationListenerDemo$ExtendSpringEvent[source=ExtendSpringEvent]
Process finished with exit code 0
总结
事件驱动内存框架其实很多,但是现在Spring强大的地方在于生态,还有特性的迭代。
像Google EventBus早期好用的地方也有Event可以是任何对象,早期的Spring Event还必须是ApplicationEvent子类,这个灵活性优势也没有了,其实工程研发中更推荐使用ApplicationEvent子类。
EventBus现在比较受限的地方就是线程池,EventBus的事件分发机制有三种,其中异步分发模式是所有事件共用一个线程池,那么就有可能忙的Event把闲的Event饿死。
在Spring Event-Driven的最佳实践方面,更推荐根据开发人员、项目背景来灵活运用,在了解Spring Event原理以后,做出合适的决策。