在講解事件監聽機制前,我們先回顧下設計模式中的觀察者模式,因為事件監聽機制可以說是在典型觀察者模式基礎上的進一步抽象和改進。我們可以在JDK或者各種開源框架比如Spring中看到它的身影,從這個意義上說,事件監聽機制也可以看做是一種對傳統觀察者模式的具體實現,不同的框架對其實現方式會有些許差別。
典型的觀察者模式將有依賴關系的對象抽象為了觀察者和主題兩個不同的角色,多個觀察者同時觀察一個主題,兩者只通過抽象接口保持松耦合狀態,這樣雙方可以相對獨立的進行擴展和變化:比如可以很方便的增刪觀察者,修改觀察者中的更新邏輯而不用修改主題中的代碼。但是這種解耦進行的並不徹底,這具體體現在以下幾個方面:
- ① 抽象主題需要依賴抽象觀察者,而這種依賴關系完全可以去除。
- ② 主題需要維護觀察者列表,並對外提供動態增刪觀察者的接口。
- ③ 主題狀態改變時需要由自己去通知觀察者進行更新。
我們可以把主題(Subject)替換成事件(event),把對特定主題進行觀察的觀察者(Observer)替換成對特定事件進行監聽的監聽器(EventListener),而把原有主題中負責維護主題與觀察者映射關系以及在自身狀態改變時通知觀察者的職責從中抽出,放入一個新的角色事件發布器(EventPublisher)中,事件監聽模式的輪廓就展現在了我們眼前,如下圖所示:
常見事件監聽機制的主要角色如下:
- 事件及事件源:對應於觀察者模式中的主題。事件源發生某事件是特定事件監聽器被觸發的原因。
- 事件監聽器:對應於觀察者模式中的觀察者。監聽器監聽特定事件,並在內部定義了事件發生后的響應邏輯。
- 事件發布器:事件監聽器的容器,對外提供發布事件和增刪事件監聽器的接口,維護事件和事件監聽器之間的映射關系,並在事件發生時負責通知相關監聽器。
Spring框架對事件的發布與監聽提供了相對完整的支持,它擴展了JDK中對自定義事件監聽提供的基礎框架,並與Spring的IOC特性作了整合,使得用戶可以根據自己的業務特點進行相關的自定義,並依托Spring容器方便的實現監聽器的注冊和事件的發布。因為Spring的事件監聽依托於JDK提供的底層支持,為了更好的理解,先來看下JDK中為用戶實現自定義事件監聽提供的基礎框架。
JDK中對事件監聽機制的支持
JDK為用戶實現自定義事件監聽提供了兩個基礎的類。一個是代表所有可被監聽事件的事件基類java.util.EventObject,所有自定義事件類型都必須繼承該類,類結構如下所示:
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 + "]";
}
}
該類內部有一個Object類型的source變量,邏輯上表示發生該事件的事件源,實際中可以用來存儲包含該事件的一些相關信息。
另一個則是對所有事件監聽器進行抽象的接口java.util.EventListener,這是一個標記接口,內部沒有任何抽象方法,所有自定義事件監聽器都必須實現該標記接口。
/**
* A tagging interface that all event listener interfaces must extend.
* @since JDK1.1
*/
public interface EventListener {
}
以上就是JDK為我們實現自定義事件監聽提供的底層支持。針對具體業務場景,我們通過擴展java.util.EventObject來自定義事件類型,同時通過擴展java.util.EventListener來定義在特定事件發生時被觸發的事件監聽器。當然,不要忘了還要定義一個事件發布器來管理事件監聽器並提供發布事件的功能。
基於JDK實現對任務執行結果的監聽
想象我們正在做一個關於Spark的任務調度系統,我們需要把任務提交到集群中並監控任務的執行狀態,當任務執行完畢(成功或者失敗),除了必須對數據庫進行更新外,還可以執行一些額外的工作:比如將任務執行結果以郵件的形式發送給用戶。這些額外的工作后期還有較大的變動可能:比如還需要以短信的形式通知用戶,對於特定的失敗任務需要通知相關運維人員進行排查等等,所以不宜直接寫死在主流程代碼中。最好的方式自然是以事件監聽的方式動態的增刪對於任務執行結果的處理邏輯。為此我們可以基於JDK提供的事件框架,打造一個能夠對任務執行結果進行監聽的彈性系統。
任務結束事件的事件源
因為要對任務執行結束這一事件進行監聽,所以必須對任務這一概念進行定義,如下:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Task {
private String name;
private TaskFinishStatus status;
}
任務包含任務名和任務狀態,其中任務狀態是個枚舉常量,只有成功和失敗兩種取值。
public enum TaskFinishStatus {
SUCCEED,
FAIL;
}
任務結束事件TaskFinishEvent
自定義事件類型TaskFinishEvent繼承自JDK中的EventObject,構造時會傳入Task作為事件源。
public class TaskFinishEvent extends EventObject {
public TaskFinishEvent(Object source) {
super(source);
}
}
該事件的監聽器抽象
繼承標記接口EventListner表示該接口的實現類是一個監聽器,同時在內部定義了事件發生時的響應方法onTaskFinish(event),接收一個TaskFinishEvent作為參數。
public interface TaskFinishEventListner extends EventListener {
void onTaskFinish(TaskFinishEvent event);
}
郵件服務監聽器
該郵件服務監聽器將在監聽到任務結束事件時將任務的執行結果發送給用戶。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MailTaskFinishListener implements TaskFinishEventListener {
private String email;
@Override
public void onTaskFinish(TaskFinishEvent event) {
System.out.println("Send Email to " + email + " Task:" + event.getSource());
}
}
自定義事件發布器
public class TaskFinishEventPublisher {
private List<TaskFinishEventListener> listeners = new ArrayList();
//注冊監聽器
public synchronized void register(TaskFinishEventListener listner) {
if (!listeners.contains(listner)) {
listeners.add(listner);
}
}
//移除監聽器
public synchronized boolean remove(TaskFinishEventListener listener) {
return listeners.remove(listener);
}
//發布任務結束事件
public void publishEvent(TaskFinishEvent event) {
for (TaskFinishEventListener listener : listeners) {
listener.onTaskFinish(event);
}
}
}
測試代碼如下
public class TestTaskFinishListener {
public static void main(String[] args) {
//------------- 1、事件源和事件
//事件源
Task source = new Task("用戶統計", TaskFinishStatus.SUCCEED);
//任務結束事件(即事件源會發生某事件)
TaskFinishEvent event = new TaskFinishEvent(source);
//------------- 2、監聽器
//郵件服務監聽器
MailTaskFinishListener mailListener = new MailTaskFinishListener("harvey@163.com");
//------------- 3、事件發布器、注冊監聽器、發布事件
//事件發布器(用來發布事件)
TaskFinishEventPublisher publisher = new TaskFinishEventPublisher();
//注冊郵件服務監聽器
publisher.register(mailListener);
//發布事件(由於有前一步的注冊服務監聽器,所以發布事件其實就是間接的是調用監聽器的方法)
publisher.publishEvent(event);
}
}
如果后期因為需求變動需要在任務結束時將結果以短信的方式發送給用戶,則可以再添加一個短信服務監聽器:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SmsTaskFinishListener implements TaskFinishEventListener {
private String address;
@Override
public void onTaskFinish(TaskFinishEvent event) {
System.out.println("Send Message to " + address + " Task:" + event.getSource());
}
}
在測試代碼中添加如下代碼向事件發布器注冊該監聽器:
SmsTaskFinishListener smsListener = new SmsTaskFinishListener("123456789");
//注冊短信服務監聽器
publisher.register(smsListener);
基於JDK的支持要實現對自定義事件的監聽還是比較麻煩的,要做的工作比較多。而且自定義的事件發布器也不能提供對所有事件的統一發布支持。基於Spring框架實現自定義事件監聽則要簡單很多,功能也更加強大。
Spring容器對事件監聽機制的支持
基於SpringBoot 2.6.2
Spring容器,具體而言是ApplicationContext接口定義的容器提供了一套相對完善的事件發布和監聽框架,其遵循了JDK中的事件監聽標准,並使用容器來管理相關組件,使得用戶不用關心事件發布和監聽的具體細節,降低了開發難度也簡化了開發流程。下面看看對於事件監聽機制中的各主要角色,Spring框架中是如何定義的,以及相關的類體系結構。
事件
Spring為容器內事件定義了一個抽象類ApplicationEvent,該類繼承了JDK中的事件基類EventObject。因而自定義容器內事件除了需要繼承ApplicationEvent之外,還要傳入事件源作為構造參數。
事件監聽器
Spring定義了一個ApplicationListener接口作為事件監聽器的抽象,接口定義如下:
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
(1)該接口繼承了JDK中表示事件監聽器的標記接口EventListener,內部只定義了一個抽象方法onApplicationEvent(evnt),當監聽的事件在容器中被發布,該方法將被調用。
(2)同時,該接口是一個泛型接口,其實現類可以通過傳入泛型參數指定該事件監聽器要對哪些事件進行監聽。這樣有什么好處?這樣所有的事件監聽器就可以由一個事件發布器進行管理,並對所有事件進行統一發布,而具體的事件和事件監聽器之間的映射關系,則可以通過反射讀取泛型參數類型的方式進行匹配,稍后我們會對原理進行講解。
(3)最后,所有的事件監聽器都必須向容器注冊,容器能夠對其進行識別並委托容器內真正的事件發布器進行管理。
事件發布器
ApplicationContext接口繼承了ApplicationEventPublisher接口,從而提供了對外發布事件的能力,如下所示:
那么是否可以說ApplicationContext,即容器本身就擔當了事件發布器的角色呢?其實這是不准確的,容器本身僅僅是對外提供了事件發布的接口,真正的工作其實是委托給了具體容器內部一個ApplicationEventMulticaster對象,其定義在AbstractApplicationContext抽象類內部,如下所示:
/** Helper class used in event publishing */
private ApplicationEventMulticaster applicationEventMulticaster;
所以,真正的事件發布器是ApplicationEventMulticaster,這是一個接口,定義了事件發布器需要具備的基本功能:管理事件監聽器以及發布事件。其默認實現類是
SimpleApplicationEventMulticaster,該組件會在容器啟動時被自動創建,並以單例的形式存在,管理了所有的事件監聽器,並提供針對所有容器內事件的發布功能。
org.springframework.context.support.AbstractApplicationContext#refresh方法中的initApplicationEventMulticaster()步驟會判斷是否存在,否則就實例化一個SimpleApplicationEventMulticaster。
基於Spring實現對任務執行結果的監聽
基於Spring框架來實現對自定義事件的監聽流程十分簡單,只需要三步:
- ① 自定義事件類
- ② 自定義事件監聽器並向容器注冊
- ③ 發布事件
自定任務結束事件
定義一個任務結束事件TaskFinishEvent2,該類繼承抽象類ApplicationEvent來遵循容器事件規范。
public class TaskFinishEvent2 extends ApplicationEvent {
public TaskFinishEvent2(Object source) {
super(source);
}
}
自定義郵件服務監聽器並向容器注冊
該類實現了容器事件規范定義的監聽器接口,通過泛型參數指定對上面定義的任務結束事件進行監聽,通過@Component注解向容器進行注冊。
@Component
public class MailTaskFinishListener2 implements ApplicationListener<TaskFinishEvent2> {
private String email = "harvey@163.com";
@Override
public void onApplicationEvent(TaskFinishEvent2 event) {
System.out.println("Send Email to " + email + " Task:" + event.getSource());
}
}
發布事件
從上面對Spring事件監聽機制的類結構分析可知,發布事件的功能定義在ApplicationEventPublisher接口中,而ApplicationContext繼承了該接口,所以最好的方法是通過實現ApplicationContextAware接口獲取ApplicationContext實例,然后調用其發布事件方法。如下所示定義了一個發布容器事件的代理類:
@Component
public class EventPublisher implements ApplicationContextAware {
private ApplicationContext applicationContext;
//發布事件
public void publishEvent(ApplicationEvent event) {
applicationContext.publishEvent(event);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
在此基礎上,還可以自定義一個短信服務監聽器,在任務執行結束時發送短信通知用戶。過程和上面自定義郵件服務監聽器類似:實現ApplicationListner接口並重寫抽象方法,然后通過注解或者xml的方式向容器注冊。
測試發布事件
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {WebApp.class})
public class TestEventPublisher {
@Autowired
private EventPublisher eventPublisher;
@Test
public void test() {
//事件源
Task source = new Task("用戶統計", TaskFinishStatus.SUCCEED);
TaskFinishEvent2 event2 = new TaskFinishEvent2(source);
eventPublisher.publishEvent(event2);
}
}
Spring事件監聽源碼解析
spring事件監聽機制離不開容器IOC特性提供的支持,比如容器會自動創建事件發布器,自動識別用戶注冊的監聽器並進行管理,在特定的事件發布后會找到對應的事件監聽器並對其監聽方法進行回調。Spring幫助用戶屏蔽了關於事件監聽機制背后的很多細節,使用戶可以專注於業務層面進行自定義事件開發。然而我們還是忍不住對其背后的實現原理進行一番探討,比如:
- 事件發布器ApplicationEventMulticaster是何時被初始化的,初始化過程中都做了什么?
- 注冊事件監聽器的過程是怎樣的,容器怎么識別出它們並進行管理?
- 容器發布事件的流程是怎樣的?它如何根據發布的事件找到對應的事件監聽器,事件和由該事件觸發的監聽器之間的匹配規則是怎樣的?
初始化事件發布器流程
真正的事件發布器是ApplicationEventMulticaster,它定義在AbstractApplicationContext中,並在ApplicationContext容器啟動的時候進行初始化。在容器啟動的refrsh()方法中可以找到初始化事件發布器的入口方法,如下圖所示:
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 判斷beanFactory里是否定義了id為applicationEventMulticaster的bean,默認是沒有的
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
} else {
//一般情況會走這里,創建一個SimpleApplicationEventMulticaster並交由容器管理
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}
這里會根據核心容器beanFactory中是否有id為applicationEventMulticaster的bean分兩種情況:
(1)容器中已有id為applicationEventMulticaster的bean:直接從容器緩存獲取或是創建該bean實例,並交由成員變量applicationEventMulticaster保存。當用戶自定義了事件發布器並向容器注冊時會執行該流程。
(2)容器中不存在applicationEventMulticaster的bean:這是容器默認的執行流程,會創建一個SimpleApplicationEventMulticaster,其僅在實現事件發布器基本功能(管理事件監聽器以及發布容器事件)的前提下,增加了可以設置任務執行器Executor和錯誤處理器ErrorHandler的功能,當設置Executor為線程池時,則會以異步的方式對事件監聽器進行回調,而ErrorHandler允許我們在回調方法執行錯誤時進行自定義處理。默認情況下,這兩個變量都為null。
之后會調用beanFactory.registerSingleton方法將創建的SimpleApplicationEventMulticaster實例注冊為容器的單實例bean。
初始化事件發布器的工作非常簡單,一句話總結:由容器實例化用戶自定義的事件發布器或者由容器幫我們創建一個簡單的事件發布器並交由容器管理。
注冊事件監聽器流程
注冊事件監聽器的流程在初始化事件發布器之后,如下圖所示:
其關鍵代碼如下所示:
protected void registerListeners() {
// 首先注冊靜態指定的監聽器。
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// 不要在這里初始化FactoryBeans:我們需要保留所有常規Bean
// 未初始化以允許后處理器應用於它們!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// 發布早期應用程序事件
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
容器事件發布流程
org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object)
org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, ResolvableType)
前面我們介紹了,在啟動的時候如果沒有一個beanName叫做applicationEventMulticaster的ApplicationEventMulticaster,那使用的就是SimpleApplicationEventMulticaster,該組件會在容器啟動時被自動創建,並以單例的形式存在,管理了所有的事件監聽器,並提供針對所有容器內事件的發布功能。
org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(ApplicationEvent, ResolvableType)
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
//獲取事件類型
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//獲取事件發布器內的任務執行器,默認該方法返回null
Executor executor = getTaskExecutor();
//遍歷所有和事件匹配的事件監聽器
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
//異步回調監聽方法
executor.execute(() -> invokeListener(listener, event));
}
else {
//同步回調監聽方法
invokeListener(listener, event);
}
}
}
那就是我們就有個疑問:如何根據事件類型找到匹配的所有事件監聽器?這部分邏輯在org.springframework.context.event.AbstractApplicationEventMulticaster#getApplicationListeners(ApplicationEvent, ResolvableType)
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
//獲取事件中的事件源對象
Object source = event.getSource();
//獲取事件源類型
Class<?> sourceType = (source != null ? source.getClass() : null);
//以事件類型和事件源類型為參數構建一個cacheKey,用於從緩存map中獲取與之匹配的監聽器列表
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// Potential new retriever to populate
CachedListenerRetriever newRetriever = null;
//根據cacheKey從緩存中獲取CachedListenerRetriever
CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
if (existingRetriever == null) {
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
//不存在則創建一個全新的CachedListenerRetriever,並保存到緩存中
newRetriever = new CachedListenerRetriever();
//如果存在重復的cacheKey,那么putIfAbsent不會放入值,如果存在則返回cacheKey對應的值
existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
if (existingRetriever != null) {
newRetriever = null; // no need to populate it in retrieveApplicationListeners
}
}
}
//如果查到CachedListenerRetriever,則從其實例中獲取到所有的監聽器直接返回
if (existingRetriever != null) {
Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
if (result != null) {
return result;
}
// If result is null, the existing retriever is not fully populated yet by another thread.
// Proceed like caching wasn't possible for this current local attempt.
}
//遍歷所有的事件監聽器,並根據事件類型和事件源類型進行匹配。
return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}
如果事件是第一次發布,會遍歷所有的事件監聽器,並根據事件類型和事件源類型進行匹配:org.springframework.context.event.AbstractApplicationEventMulticaster#retrieveApplicationListeners
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {
//這是存放匹配的監聽器的列表
List<ApplicationListener<?>> allListeners = new ArrayList<>();
Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);
Set<ApplicationListener<?>> listeners;
Set<String> listenerBeans;
synchronized (this.defaultRetriever) {
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
//遍歷所有的監聽器
for (ApplicationListener<?> listener : listeners) {
//判斷該事件監聽器是否匹配
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
filteredListeners.add(listener);
}
//將匹配的監聽器加入列表
allListeners.add(listener);
}
}
//這部分可以跳過
if (!listenerBeans.isEmpty()) {
ConfigurableBeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
ApplicationListener<?> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
if (beanFactory.isSingleton(listenerBeanName)) {
filteredListeners.add(listener);
}
else {
filteredListenerBeans.add(listenerBeanName);
}
}
allListeners.add(listener);
}
}
else {
// Remove non-matching listeners that originally came from
// ApplicationListenerDetector, possibly ruled out by additional
// BeanDefinition metadata (e.g. factory method generics) above.
Object listener = beanFactory.getSingleton(listenerBeanName);
if (retriever != null) {
filteredListeners.remove(listener);
}
allListeners.remove(listener);
}
}
catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition) disappeared -
// probably in the middle of the destruction phase
}
}
}
//對匹配的監聽器列表進行排序
AnnotationAwareOrderComparator.sort(allListeners);
if (retriever != null) {
if (filteredListenerBeans.isEmpty()) {
retriever.applicationListeners = new LinkedHashSet<>(allListeners);
retriever.applicationListenerBeans = filteredListenerBeans;
}
else {
retriever.applicationListeners = filteredListeners;
retriever.applicationListenerBeans = filteredListenerBeans;
}
}
return allListeners;
}
判斷監聽器是否匹配的邏輯在supportsEvent(listener, eventType, sourceType)中:org.springframework.context.event.AbstractApplicationEventMulticaster#supportsEvent(ConfigurableBeanFactory, java.lang.String, ResolvableType)
梳理下容器事件發布的整個流程,可以總結如下: