Spring事件執行流程源碼分析

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":" 1. 背景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲啥突然想到寫這個?起因就是看到了Nacos的"},{"type":"codeinline","content":[{"type":"text","text":"#3757 ISSUE"}]},{"type":"text","text":",理解錯誤, 以爲是服務啓動,沒有註冊上服務,實際namespace不同,導致服務無法註冊。 但這絲毫不影響再去研究了一波代碼,順便也看到了Nacos是如何利用Spring的事件來進行服務註冊的。分享一波,歡迎大家學習指正!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2. 娓娓道來 - Spring事件 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.1 怎麼發佈事件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們大家應該都知道,在Spring中,是通過實現"},{"type":"codeinline","content":[{"type":"text","text":"org.springframework.context.ApplicationEventPublisher"}]},{"type":"text","text":"來發佈一個事件。"},{"type":"codeinline","content":[{"type":"text","text":"ApplicationEcentPublisher"}]},{"type":"text","text":"是一個接口,我們來看一下接口中邏輯。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface ApplicationEventPublisher {\n\n\t/**\n\t * Notify all matching listeners registered with this\n\t * application of an application event. Events may be framework events\n\t * (such as RequestHandledEvent) or application-specific events.\n\t * @param event the event to publish\n\t * @see org.springframework.web.context.support.RequestHandledEvent\n\t */\n\tdefault void publishEvent(ApplicationEvent event) {\n\t\tpublishEvent((Object) event);\n\t}\n\n\t/**\n\t * Notify all matching listeners registered with this\n\t * application of an event.\n\t *

If the specified {@code event} is not an {@link ApplicationEvent},\n\t * it is wrapped in a {@link PayloadApplicationEvent}.\n\t * @param event the event to publish\n\t * @since 4.2\n\t * @see PayloadApplicationEvent\n\t */\n\tvoid publishEvent(Object event);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接口很簡單,裏面有兩個方法,都是發佈事件,而接口默認實現,是調用"},{"type":"codeinline","content":[{"type":"text","text":"publishEvent(Object event)"}]},{"type":"text","text":"的實現,唯一的區別就是default方法的參數是具體的事件類。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"既然這是個接口,那一定有實現類,那麼我們肯定是要進入實現類,快捷鍵,查看一下具體實現類。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7e/7e6c97cca5d3f072204e11c1e94543f9.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們通過實現類,可以看到,應該是從"},{"type":"codeinline","content":[{"type":"text","text":"AbstractApplicationContext"}]},{"type":"text","text":"進去(其他Context都是實現類,憑感覺和代碼提示,從第一個類進如代碼)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進去"},{"type":"codeinline","content":[{"type":"text","text":"org.springframework.context.support.AbstractApplicationContext"}]},{"type":"text","text":",我們直奔主題,查看是如何實現PublishEvent。具體代碼如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n\t * Publish the given event to all listeners.\n\t * @param event the event to publish (may be an {@link ApplicationEvent}\n\t * or a payload object to be turned into a {@link PayloadApplicationEvent})\n\t * @param eventType the resolved event type, if known\n\t * @since 4.2\n\t */\n\tprotected void publishEvent(Object event, @Nullable ResolvableType eventType) {\n\t\tAssert.notNull(event, \"Event must not be null\");\n\n\t\t// Decorate event as an ApplicationEvent if necessary\n\t\tApplicationEvent applicationEvent;\n\t\tif (event instanceof ApplicationEvent) {\n // 實現的具體的ApplicationEvent, 直接進行轉化即可\n\t\t\tapplicationEvent = (ApplicationEvent) event;\n\t\t}\n\t\telse {\n // 將事件裝飾成PayloadApplicationEvent,\n\t\t\tapplicationEvent = new PayloadApplicationEvent<>(this, event);\n\t\t\tif (eventType == null) {\n\t\t\t\teventType = ((PayloadApplicationEvent>) applicationEvent).getResolvableType();\n\t\t\t}\n\t\t}\n\n\t\t// Multicast right now if possible - or lazily once the multicaster is initialized\n\t\tif (this.earlyApplicationEvents != null) {\n // 廣播還未初始化完成,放入earlyApplicationEvents.\n\t\t\tthis.earlyApplicationEvents.add(applicationEvent);\n\t\t}\n\t\telse {\n // 核心關注點: 使用廣播廣播事件,\n\t\t\tgetApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);\n\t\t}\n\n\t\t// Publish event via parent context as well...\n // 父容器的監聽器進行廣播\n\t\tif (this.parent != null) {\n\t\t\tif (this.parent instanceof AbstractApplicationContext) {\n\t\t\t\t((AbstractApplicationContext) this.parent).publishEvent(event, eventType);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.parent.publishEvent(event);\n\t\t\t}\n\t\t}\n\t}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這段代碼中,我們得到了兩點有用的信息,分別是:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"發佈事件的整體流程。(封裝事件,廣播事件)"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"發佈的時間通過"},{"type":"codeinline","content":[{"type":"text","text":"getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);"}]},{"type":"text","text":"廣播事件"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過代碼可以知道,我們接下來的關注點是"},{"type":"codeinline","content":[{"type":"text","text":"getApplicationEventMulticaster()"}]},{"type":"text","text":",如何通過"},{"type":"codeinline","content":[{"type":"text","text":"multicastEvent()"}]},{"type":"text","text":"方法將事件廣播出去呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.2 什麼是ApplicationEventMulticaster"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進入到核心關注點代碼,"},{"type":"codeinline","content":[{"type":"text","text":"getApplicationEventMulticaster()"}]},{"type":"text","text":"是什麼?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {\n // ...略... 沒什麼重要信息\n return this.applicationEventMulticaster;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼很簡單,沒什麼邏輯,但是讓我們明白了"},{"type":"codeinline","content":[{"type":"text","text":"getApplicationEventMulticaster()"}]},{"type":"text","text":"獲取到的對象是"},{"type":"codeinline","content":[{"type":"text","text":"ApplicationEventMulticaster"}]},{"type":"text","text":",那我們接下來搞懂這個東西("},{"type":"codeinline","content":[{"type":"text","text":"ApplicationEventMulticaster"}]},{"type":"text","text":")是如何初始化?理清楚後可以弄清楚邏輯了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"輕輕鬆鬆翻到"},{"type":"codeinline","content":[{"type":"text","text":"ApplicationEventMulticaster"}]},{"type":"text","text":"初始化的代碼(給自己鼓個掌👏),代碼如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * Initialize the ApplicationEventMulticaster.\n * Uses SimpleApplicationEventMulticaster if none defined in the context.\n * @see org.springframework.context.event.SimpleApplicationEventMulticaster\n */\nprotected void initApplicationEventMulticaster() {\n ConfigurableListableBeanFactory beanFactory = getBeanFactory();\n // 判斷容器中是否已經創建ApplicationEventMulticaster\n if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {\n this.applicationEventMulticaster =\n beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);\n }\n else {\n // 初始化默認ApplicationEventMulticaster\n this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);\n beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼很簡單,就是判斷容器中是否已經包含了"},{"type":"codeinline","content":[{"type":"text","text":"ApplicationEventMulticaster"}]},{"type":"text","text":"(可以理解爲是否自己實現了"},{"type":"codeinline","content":[{"type":"text","text":"ApplicationEventMulticaster"}]},{"type":"text","text":"),如果沒實現,則採用Spring的默認實現"},{"type":"codeinline","content":[{"type":"text","text":"SimpleApplicationEventMulticaster"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到目前爲止,我們已經知道的信息是:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"SimpleApplicationEventMulticaster"}]},{"type":"text","text":"是"},{"type":"codeinline","content":[{"type":"text","text":"ApplicationEventMulticaster"}]},{"type":"text","text":"的默認實現"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"搞到了這個有用的信息,那麼我們去搞懂"},{"type":"codeinline","content":[{"type":"text","text":"SimpleApplicationEventMulticaster"}]},{"type":"text","text":"是什麼就可以知道Spring的廣播流程了"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2.1 SimpleApplicationEventMulticaster 是什麼?如何廣播事件?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"帶着思考與疑惑,我們進入"},{"type":"codeinline","content":[{"type":"text","text":"SimpleApplicationEventMulticaster"}]},{"type":"text","text":",就看看"},{"type":"codeinline","content":[{"type":"text","text":"multicastEvent()"}]},{"type":"text","text":"方法, 因爲上面代碼中有寫到,發送事件是這樣操作的:"},{"type":"codeinline","content":[{"type":"text","text":"getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"方法如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {\n // 解析事件類型。(不知道沒關係,先通過名字猜,然後再去驗證自己的猜想)\n ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));\n // 遍歷所有的監聽者,\n for (final ApplicationListener> listener : getApplicationListeners(event, type)) {\n Executor executor = getTaskExecutor();\n if (executor != null) {\n // 有線程池,則用線程池\n executor.execute(() -> invokeListener(listener, event));\n }\n else {\n // 沒有線程池則直接調用\n invokeListener(listener, event);\n }\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼看到這, 我們已經有了大概的瞭解,但大家應該還有兩個疑問,分別是:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"getApplicationListeners(event, type)"}]},{"type":"text","text":"是什麼?(這個很重要,包含著爲什麼特定的Listener能接收到特定的事件)"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"invokeListener(listener, event);"}]},{"type":"text","text":"裏面的邏輯是什麼?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這兩個問題中,第一個問題我們暫時先放下,因爲這暫時不涉及事件調用流程,後邊說。我們就先說一下第二個以爲。"},{"type":"codeinline","content":[{"type":"text","text":"invokeListener(listener, event);"}]},{"type":"text","text":"邏輯是什麼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們帶着疑惑,接着進入代碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {\n try {\n // 監聽器處理事件\n listener.onApplicationEvent(event);\n }\n catch (ClassCastException ex) {\n ...略...\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到這,知道了。邏輯蠻簡單的,就是調用Listener處理事件的邏輯。我們腦海中的有個大概流程圖了,如下圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/97/97000d74acb55db696dc562e0d9c430e.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過"},{"type":"codeinline","content":[{"type":"text","text":"AbstractApplicationContext實現類"}]},{"type":"text","text":"發佈事件,在通過"},{"type":"codeinline","content":[{"type":"text","text":"SimpleApplicationEventMulticaster"}]},{"type":"text","text":"將事件廣播到所有Listener,就完成了整個過程的調用。但是,我們還有其中二塊沒有搞清楚,分別是:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"allListener是怎麼來的呢? "}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"爲什麼能根據事件類型來決定調用哪些Listener?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.3 Listener的祕密"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼疑問,回到代碼。從"},{"type":"codeinline","content":[{"type":"text","text":"multicastEvent()"}]},{"type":"text","text":"方法中的"},{"type":"codeinline","content":[{"type":"text","text":"getApplicationListeners(event, type)"}]},{"type":"text","text":"進入,發現進入到了"},{"type":"codeinline","content":[{"type":"text","text":"AbstractApplicationEventMulticaster"}]},{"type":"text","text":"代碼中,發現了Listener的增減和刪除邏輯(爲什麼先看這兩個?類的代碼不多,可以先隨便瞅瞅,不要太侷限)。增加代碼邏輯如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"// 通過實現類添加listener\npublic void addApplicationListener(ApplicationListener> listener) {\n synchronized (this.retrievalMutex) {\n // Explicitly remove target for a proxy, if registered already,\n // in order to avoid double invocations of the same listener.\n Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);\n if (singletonTarget instanceof ApplicationListener) {\n this.defaultRetriever.applicationListeners.remove(singletonTarget);\n }\n this.defaultRetriever.applicationListeners.add(listener);\n this.retrieverCache.clear();\n }\n}\n// 通過beanName添加listener\npublic void addApplicationListenerBean(String listenerBeanName) {\n synchronized (this.retrievalMutex) {\n this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);\n this.retrieverCache.clear();\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從參數可以看出,兩個方法的差一點,就不細說。但是我們好奇啊,代碼中的"},{"type":"codeinline","content":[{"type":"text","text":"defaultRetriever"}]},{"type":"text","text":"是什麼,有屬性?爲什麼listener保存在這個裏面?還是帶着疑惑,進入代碼,核心代碼如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public Collection> getApplicationListeners() {\n List> allListeners = new ArrayList<>(\n this.applicationListeners.size() + this.applicationListenerBeans.size());\n allListeners.addAll(this.applicationListeners);\n if (!this.applicationListenerBeans.isEmpty()) {\n BeanFactory beanFactory = getBeanFactory();\n for (String listenerBeanName : this.applicationListenerBeans) {\n try {\n ApplicationListener> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);\n // 是否需要過濾重複的listener\n if (this.preFiltered || !allListeners.contains(listener)) {\n allListeners.add(listener);\n }\n }\n catch (NoSuchBeanDefinitionException ex) {\n // Singleton listener instance (without backing bean definition) disappeared -\n // probably in the middle of the destruction phase\n }\n }\n }\n if (!this.preFiltered || !this.applicationListenerBeans.isEmpty()) {\n AnnotationAwareOrderComparator.sort(allListeners);\n }\n return allListeners;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼邏輯蠻簡單的,就是講beanName方式的listener和listener實現類直接結合放在一個集合中,並且進行排序返回。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接着,我們在來看"},{"type":"codeinline","content":[{"type":"text","text":"getApplicationListeners(event, type)"}]},{"type":"text","text":"的具體實現:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * Return a Collection of ApplicationListeners matching the given\n * event type. Non-matching listeners get excluded early.\n * @param event the event to be propagated. Allows for excluding\n * non-matching listeners early, based on cached matching information.\n * @param eventType the event type\n * @return a Collection of ApplicationListeners\n * @see org.springframework.context.ApplicationListener\n */\nprotected Collection> getApplicationListeners(\n ApplicationEvent event, ResolvableType eventType) {\n\n Object source = event.getSource();\n Class> sourceType = (source != null ? source.getClass() : null);\n // 通過事件類型和事件源類型構建緩存key\n ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);\n\n // Quick check for existing entry on ConcurrentHashMap...\n // 是否有緩存,有緩存就快速返回\n ListenerRetriever retriever = this.retrieverCache.get(cacheKey);\n if (retriever != null) {\n return retriever.getApplicationListeners();\n }\n\n if (this.beanClassLoader == null ||\n (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&\n (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {\n ... 刪掉了複雜邏輯,直接看下面else代碼\n }\n else {\n // No ListenerRetriever caching -> no synchronization necessary\n // 如何區分事件的執行邏輯\n return retrieveApplicationListeners(eventType, sourceType, null);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過上面代碼,我們發現,spring是在本地對事件有緩存,通過事件類型和事件源類型構建ListenerCacheKey。 但是還沒有到核心點, 如何區分事件的呢?接着我們進入"},{"type":"codeinline","content":[{"type":"text","text":"retrieveApplicationListeners(eventType, sourceType, null);"}]},{"type":"text","text":"中去尋找事情的真正原因。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * Actually retrieve the application listeners for the given event and source type.\n * @param eventType the event type\n * @param sourceType the event source type\n * @param retriever the ListenerRetriever, if supposed to populate one (for caching purposes)\n * @return the pre-filtered list of application listeners for the given event and source type\n */\nprivate Collection> retrieveApplicationListeners(\n ResolvableType eventType, @Nullable Class> sourceType, @Nullable ListenerRetriever retriever) {\n\n List> allListeners = new ArrayList<>();\n Set> listeners;\n Set listenerBeans;\n synchronized (this.retrievalMutex) {\n listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);\n listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);\n }\n // 已經加裝的Listener\n for (ApplicationListener> listener : listeners) {\n if (supportsEvent(listener, eventType, sourceType)) {\n if (retriever != null) {\n retriever.applicationListeners.add(listener);\n }\n allListeners.add(listener);\n }\n }\n // beanName的listener\n if (!listenerBeans.isEmpty()) {\n BeanFactory beanFactory = getBeanFactory();\n for (String listenerBeanName : listenerBeans) {\n try {\n Class> listenerType = beanFactory.getType(listenerBeanName);\n if (listenerType == null || supportsEvent(listenerType, eventType)) {\n ApplicationListener> listener =\n beanFactory.getBean(listenerBeanName, ApplicationListener.class);\n if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {\n if (retriever != null) {\n if (beanFactory.isSingleton(listenerBeanName)) {\n retriever.applicationListeners.add(listener);\n }\n else {\n retriever.applicationListenerBeans.add(listenerBeanName);\n }\n }\n allListeners.add(listener);\n }\n }\n }\n catch (NoSuchBeanDefinitionException ex) {\n // Singleton listener instance (without backing bean definition) disappeared -\n // probably in the middle of the destruction phase\n }\n }\n }\n AnnotationAwareOrderComparator.sort(allListeners);\n if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {\n retriever.applicationListeners.clear();\n retriever.applicationListeners.addAll(allListeners);\n }\n return allListeners;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼很簡單,就是把beanName的Listener和listener實現類拿出去,找出支持eventType和sourceType的。 自此,我們也弄懂了,是如何添加listener的,大功告成!!!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"別捉急,我們得把上面看的代碼在整理一下,把流程圖完善一下,如下圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a6/a6d90adccc7f8a595310239c7be4f07a.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3 大功告成"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至此,分析完了整個流程,弄懂了Spring整個事件廣播流程,是不是特別簡單?!!!!, 沒搞懂的時候覺得難,仔細看一下發現如此簡單哇! 給自己加個油,可以的!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"4. Spring中的初始化流程與Nacos中的使用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這部分知識還有點多,暫時先不寫,先放在下篇文章中,下篇文章將具體說一下我的分析ISSUE的流程,以及我的發現-----Nacos中如何使用事件來將服務註冊到Nacos Discover中的。 還有一個就是Spring初始化流程(這個初始化流程也包含了Nacos註冊的一個小問題:當服務不是web服務的時候,Nacos將不會註冊這個服務,不知道算不算問題? 下次在接着說)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"5. 本文爲個人分析,如果有問題歡迎指出, 謝謝啦!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章