前言
Spring Event-Driven 是Java生態(tài)中關(guān)于Event-Driven編程范式的最佳實(shí)踐標(biāo)準(zhǔn)之一 ,在Spring框架之前,已經(jīng)有JDK、Servlet、EJB等框架進(jìn)行過觀察者模式、事件驅(qū)動(dòng)、注解式監(jiān)聽等嘗試,本文簡(jiǎn)要分析一下Spring在事件驅(qū)動(dòng)方面的設(shè)計(jì)。
從觀察者模式說起
Pub-Sub設(shè)計(jì)模式應(yīng)該是由觀察者模式變種而來 ,所以我們有必要了解一下在JDK中提供的Observable/Observer實(shí)現(xiàn)標(biāo)準(zhǔn)。
pacakge java.util;
public class Observable {
private boolean changed = false;
private Vector<Observer> 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 <CODE>null</CODE> 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
* <code>hasChanged</code> method, then notify all of its observers
* and then call the <code>clearChanged</code> method to
* indicate that this object has no longer changed.
* <p>
* Each observer has its <code>update</code> method called with two
* arguments: this observable object and <code>null</code>. In other
* words, this method is equivalent to:
* <blockquote><tt>
* notifyObservers(null)</tt></blockquote>
*
* @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
* <code>hasChanged</code> method, then notify all of its observers
* and then call the <code>clearChanged</code> method to indicate
* that this object has no longer changed.
* <p>
* Each observer has its <code>update</code> method called with two
* arguments: this observable object and the <code>arg</code> 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 <tt>Observable</tt> object as having been changed; the
* <tt>hasChanged</tt> method will now return <tt>true</tt>.
*/
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 <tt>hasChanged</tt> method will now return <tt>false</tt>.
* This method is called automatically by the
* <code>notifyObservers</code> 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.
*
* @return <code>true</code> if and only if the <code>setChanged</code>
* method has been called more recently than the
* <code>clearChanged</code> method on this object;
* <code>false</code> otherwise.
* @see java.util.Observable#clearChanged()
* @see java.util.Observable#setChanged()
*/
public synchronized boolean hasChanged() {
return changed;
}
/**
* Returns the number of observers of this <tt>Observable</tt> object.
*
* @return the number of observers of this object.
*/
public synchronized int countObservers() {
return obs.size();
}
}
package java.util;
/**
* A class can implement the <code>Observer</code> 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 <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
* method.
*/
void update(Observable o, Object arg);
}
如上圖,JDK推薦我們基于Observable和Observer拓展來進(jìn)行觀察者模式編碼。
實(shí)踐代碼如下:
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);
}
}
}
如上圖,簡(jiǎn)單分析一下,這一套標(biāo)準(zhǔn)蹩腳的地方在于:
- Observable進(jìn)行notify的時(shí)候,必須要setChanged狀態(tài),才能真正被觀察到,否則會(huì)忽略掉變化,直接被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方法是個(gè)protected級(jí)別 ,必須得繼承一個(gè)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 ,意思是讓你別玩這個(gè)Observable了。
事件驅(qū)動(dòng)的模型
上面提到了Observable/Observer模型 ,接下來說一說Event-Driven方面在Spring之前前人有過哪些嘗試,相應(yīng)的事件編程模型是什么樣的。
從監(jiān)聽粒度上分為單事件監(jiān)聽和多事件監(jiān)聽
- 單事件監(jiān)聽模型
指的是一個(gè)Listener一次只消費(fèi)一個(gè)事件,比如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);
}
- 多事件監(jiān)聽模型
指的是一個(gè)Listener同時(shí)監(jiān)聽多個(gè)事件,比如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);
}
從監(jiān)聽者注冊(cè)方式來區(qū)分,分為接口式和注解式。
- 接口式
顧名思義就是通過接口實(shí)現(xiàn)的方式,通過addListener的方式注冊(cè)。
context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("Receive event : " + event);
}
});
- 注解式
顧名思義就是用注解指定Listener ,在Spring之前已經(jīng)有開源產(chǎn)品這么玩過了。
如Servlet 3.0 WebListener、JPA、jdk PostConstruct、EJB等注解。
@EventListener
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("@EventListener Receive event : " + event);
}
JDK中的事件驅(qū)動(dòng)規(guī)范
在Java中,規(guī)范要求事件驅(qū)動(dòng)模型是基于以下兩個(gè)類去拓展的。
package java.util;
/**
* <p>
* The root class from which all event state objects shall be derived.
* <p>
* 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 {
}
可以看到,關(guān)于事件驅(qū)動(dòng)并沒有給出任何實(shí)現(xiàn),只是定義了標(biāo)準(zhǔn)的事件模型和監(jiān)聽標(biāo)準(zhǔn),EventListner就有點(diǎn)類似于ArrayList實(shí)現(xiàn)了RamdomAccess標(biāo)記。
Spring事件模型
了解以上背景以后,我們可以揭開Spring編程模型的面紗了,實(shí)際上正是基于前人的基礎(chǔ)上設(shè)計(jì)。
package org.springframework.context;
import java.util.EventListener;
/**
* Interface to be implemented by application event listeners.
*
* <p>Based on the standard {@code java.util.EventListener} interface
* for the Observer design pattern.
*
* <p>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 <E> 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<E extends ApplicationEvent> 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;
}
}
正是一個(gè)標(biāo)準(zhǔn)的java.util事件編程模型拓展,而Spring在此基礎(chǔ)上做了進(jìn)一步拓展,加入了時(shí)間戳。
并且還有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基礎(chǔ)上,又增加了ApplicationContext對(duì)象進(jìn)來,這是為了傳遞Spring容器上下文,基于這個(gè)上下文事件有四個(gè)標(biāo)準(zhǔn)內(nèi)置事件。
/*
* 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進(jìn)行refresh、start、stop、close時(shí),分別會(huì)產(chǎn)生以上事件。
Spring接口式監(jiān)聽事件實(shí)踐
我們剛好借容器內(nèi)置事件進(jìn)行接口式監(jiān)聽的實(shí)踐。
public class ApplicationListenerDemo {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.register(ApplicationListenerDemo.class);
context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
@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注解式監(jiān)聽事件實(shí)踐
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類型,無(wú)需接口約束
@EventListener
public void onApplicationEvent2(ContextStartedEvent event) {
System.out.println("@EventListener Receive ContextStartedEvent order 2");
}
- 泛型
@EventListener
public void onApplicationEvent2(ContextStartedEvent<T> event) {
System.out.println("@EventListener Receive ContextStartedEvent order 2");
}
Spring 4.2版本對(duì)于PayloadEvent的支持。
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
可以看到4.2以后不再?gòu)?qiáng)制要求Event是ApplicationEvent的子類,而是適配成了PayloadApplicationEvent對(duì)象。
/*
* 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.
*
* <p>Mainly intended for internal use within the framework.
*
* @author Stephane Nicoll
* @since 4.2
* @param <T> the payload type of the event
*/
@SuppressWarnings("serial")
public class PayloadApplicationEvent<T> 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 事件發(fā)送器依賴注入和查找
- ApplicationEventPublisher
1.我們可以直接依賴注入ApplicationEventPublisher來進(jìn)行事件發(fā)送
public class ApplicationListenerDemo {
@Autowired
ApplicationEventPublisher applicationEventPublisher;
@PostConstruct
public void init() {
applicationEventPublisher.publishEvent("ExtendSpringEvent");
}
}
- 我們可以通過Aware回調(diào)
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接口的實(shí)現(xiàn),委托applicationEventMulticaster進(jìn)行事件發(fā)送,所以如果是ApplicationContext上下文中,是可以直接調(diào)publishEvent發(fā)送事件的。
其實(shí)底層都是通過ApplicationEventMultiCaster來實(shí)現(xiàn)的,那我們來分析一下源碼。
首先看一下publishEvent實(shí)現(xiàn),來看看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 ,因?yàn)樵贐eanPostProcesser生命周期時(shí),ApplicationEventMultiCaster還沒有init,所以publishEvent會(huì)報(bào)錯(cuò),這里用一組earlyEvents暫存。
而初始化完成后的生命周期中,直接進(jìn)入else邏輯,調(diào)用getApplicationEventMulticaster進(jìn)行事件發(fā)送。
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.
*
* <p>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.
* <p>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.
* <p>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.
* <p>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.
*
* <p>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.
*
* <p>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.
* <p>Default is equivalent to {@link org.springframework.core.task.SyncTaskExecutor},
* executing all listeners synchronously in the calling thread.
* <p>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.
* <p>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.
* <p>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實(shí)現(xiàn)的,里面可以看到,如果沒有指定線程池,將會(huì)同步調(diào)用消費(fèi)者,所以Spring默認(rèn)事件是主線程同步執(zhí)行的。并且ErrorHandler的邏輯也在此可以看到。
Spring全家桶中關(guān)于事件發(fā)送都是基于SimpleApplicationEventMulticaster實(shí)現(xiàn)的。
ApplicationEventMultiCaster初始化
那么這個(gè)SimpleApplicationEventMulticaster是什么時(shí)候初始化的呢?
@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方法這個(gè)高頻面試點(diǎn)不用多介紹了,我們直接進(jìn)入initApplicationEventMulticaster方法實(shí)現(xiàn)。
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 一個(gè)SimpleApplicationEventMulticaster實(shí)現(xiàn)并且通過beanFactory手動(dòng)register單例到容器里。
那么我們就可以想到,如果我自己注冊(cè)一個(gè)實(shí)現(xiàn)別名為applicationEventMulticaster,其實(shí)是可以跳過simple實(shí)現(xiàn)拓展著玩的,如下:
@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]
但是一般不需要拓展這個(gè),因?yàn)檫€需要封裝調(diào)用listener很多特性邏輯,你知道有這個(gè)點(diǎn)裝逼就可以了,氣質(zhì)這塊絕對(duì)不能頹。
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的時(shí)候,很明顯initApplicationEventMulticaster還沒調(diào)到,那么就沒有SimpleApplicationEventMulticaster這個(gè)單例,這時(shí)候事件肯定發(fā)不了報(bào)錯(cuò)了,所以弄出來一個(gè)earlyEvents暫存。
ApplicationEventMulticaster模式上是有點(diǎn)像Observable的,所以說Spring事件整體架構(gòu)上是借鑒了很多前人的思想,然后改良了實(shí)現(xiàn)。
Multicaster異步和異常處理機(jī)制拓展
- 異步處理
異常處理機(jī)制是SimpleApplicationEventMulticaster獨(dú)有的,ApplicationEventMulticaster接口中并沒有taskExecutor對(duì)象,所以要通過類型判斷強(qiáng)轉(zhuǎn)來增加接口式監(jiān)聽的異步化。首先通過beanName拿到ApplicationEventMulticaster接口實(shí)例,然后把提前創(chuàng)建好的線程池賦值給強(qiáng)轉(zhuǎn)后的SimpleMulticaster。
ApplicationEventMulticaster applicationEventMulticaster = context.getBean(
AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME,
ApplicationEventMulticaster.class);
ExecutorService executorService =
new ThreadPoolExecutor(10, 10,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
new CustomizableThreadFactory("my-pool-"));
if (applicationEventMulticaster instanceof SimpleApplicationEventMulticaster) {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster
= (SimpleApplicationEventMulticaster)applicationEventMulticaster;
simpleApplicationEventMulticaster.setTaskExecutor(executorService);
}
要注意的是這里線程池不能自動(dòng)關(guān)閉,兩種做法吧 ,監(jiān)聽容器Close事件來關(guān)閉,或者用jvm的shutdownhook。
context.addApplicationListener(new ApplicationListener<ContextClosedEvent>() {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
if (!executorService.isShutdown()) {
executorService.shutdown();
}
}
});
- 異常處理機(jī)制
同上,給一下異常處理實(shí)現(xiàn)。
simpleApplicationEventMulticaster.setErrorHandler(new ErrorHandler() {
@Override
public void handleError(Throwable throwable) {
System.out.printf("事件消費(fèi)異常:%s\n", throwable.getMessage());
}
});
結(jié)合起來的實(shí)踐案例如下:
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<Runnable>(),
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("事件消費(fèi)異常:%s\n", throwable.getMessage());
}
});
}
context.addApplicationListener(new ApplicationListener<ContextClosedEvent>() {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
if (!executorService.isShutdown()) {
executorService.shutdown();
}
}
});
context.addApplicationListener(new ApplicationListener<ExtendSpringEvent>() {
@Override
public void onApplicationEvent(ExtendSpringEvent event) {
System.err.printf("線程:%s 消費(fèi)事件%s", Thread.currentThread().getName(), event);
}
});
context.addApplicationListener(new ApplicationListener<ExtendSpringEvent>() {
@Override
public void onApplicationEvent(ExtendSpringEvent event) {
throw new RuntimeException("onApplicationEvent報(bào)錯(cuò)了");
}
});
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);
}
}
}
事件消費(fèi)異常:onApplicationEvent報(bào)錯(cuò)了
線程:my-pool-1 消費(fèi)事件org.thinking.in.spring.ioc.lookup.event.ApplicationListenerDemo$ExtendSpringEvent[source=ExtendSpringEvent]
Process finished with exit code 0
總結(jié)
事件驅(qū)動(dòng)內(nèi)存框架其實(shí)很多,但是現(xiàn)在Spring強(qiáng)大的地方在于生態(tài),還有特性的迭代。
像Google EventBus早期好用的地方也有Event可以是任何對(duì)象,早期的Spring Event還必須是ApplicationEvent子類,這個(gè)靈活性優(yōu)勢(shì)也沒有了,其實(shí)工程研發(fā)中更推薦使用ApplicationEvent子類。
EventBus現(xiàn)在比較受限的地方就是線程池,EventBus的事件分發(fā)機(jī)制有三種,其中異步分發(fā)模式是所有事件共用一個(gè)線程池,那么就有可能忙的Event把閑的Event餓死。
在Spring Event-Driven的最佳實(shí)踐方面,更推薦根據(jù)開發(fā)人員、項(xiàng)目背景來靈活運(yùn)用,在了解Spring Event原理以后,做出合適的決策。