生活随笔
收集整理的這篇文章主要介紹了
手写简版spring --10--容器事件和事件监听器
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、降低耦合
解耦場景在互聯網開發的設計中使用的也是非常頻繁,如:這里需要一個注冊完成事件推送消息、用戶下單我會發送一個MQ、收到我的支付消息就可以發貨了等等,都是依靠事件訂閱和發布以及MQ消息這樣的組件,來處理系統之間的調用解耦,最終通過解耦的方式來提升整體系統架構的負載能力。其實解耦思路可以理解為設計模式中觀察者模式的具體使用效果,在觀察者模式中當對象間存在一對多關系時,則使用觀察者模式,它是一種定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都得到通知并被自動更新。事件傳播的一個典型應用是,當Bean中的操作發生異常(如數據庫連接失敗),則通過事件傳播機制通知異常監聽器進行處理
二、目標
在 Spring 中有一個 Event 事件功能,它可以提供事件的定義、發布以及監聽事件來完成一些自定義的動作。比如你可以定義一個新用戶注冊的事件,當有用戶執行注冊完成后,在事件監聽中給用戶發送一些優惠券和短信提醒,這樣的操作就可以把屬于基本功能的注冊和對應的策略服務分開,降低系統的耦合。以后在擴展注冊服務,比如需要添加風控策略、添加實名認證、判斷用戶屬性等都不會影響到依賴注冊成功后執行的動作。那么在本章節我們需要以觀察者模式的方式,設計和實現 Spring Event 的容器事件和事件監聽器功能,最終可以讓我們在現有實現的 Spring 框架中可以定義、監聽和發布自己的事件信息。
三、方案
其實事件的設計本身就是一種觀察者模式的實現,它所要解決的就是一個對象狀態改變給其他對象通知的問題,而且要考慮到易用和低耦合,保證高度的協作。在功能實現上我們需要定義出事件類、事件監聽、事件發布,而這些類的功能需要結合到 Spring 的 AbstractApplicationContext#refresh(),以便于處理事件初始化和注冊事件監聽器的操作。整體設計結構如下圖:
在整個功能實現過程中,仍然需要在面向用戶的應用上下文 AbstractApplicationContext中添加相關事件內容,包括:初始化事件發布者、注冊事件監聽器、發布容器刷新完成事件。 使用觀察者模式定義事件類、監聽類、發布類,同時還需要完成一個廣播器的功能,接收到事件推送時進行分析處理符合監聽事件接受者感興趣的事件,也就是使用isAssignableFrom 進行判斷。 isAssignableFrom 和 instanceof 相似,不過isAssignableFrom 是用來判斷子類和父類的關系的,或者接口的實現類和接口的關系的,默認所有的類的終極父類都是Object。如果A.isAssignableFrom(B)結果是true,證明B可以轉換成為A,也就是A可以由B轉換而來。
四、實現
工程結構
small
- spring
- step
- 10
└── src├── main│ └── java│ └── cn
. bugstack
. springframework│ ├── beans│ │ ├── factory│ │ │ ├── config│ │ │ │ ├──
AutowireCapableBeanFactory . java│ │ │ │ ├──
BeanDefinition . java│ │ │ │ ├──
BeanFactoryPostProcessor . java│ │ │ │ ├──
BeanPostProcessor . java│ │ │ │ ├──
BeanReference . java│ │ │ │ ├──
ConfigurableBeanFactory . java│ │ │ │ └──
SingletonBeanRegistry . java│ │ │ ├── support│ │ │ │ ├──
AbstractAutowireCapableBeanFactory . java│ │ │ │ ├──
AbstractBeanDefinitionReader . java│ │ │ │ ├──
AbstractBeanFactory . java│ │ │ │ ├──
BeanDefinitionReader . java│ │ │ │ ├──
BeanDefinitionRegistry . java│ │ │ │ ├──
CglibSubclassingInstantiationStrategy . java│ │ │ │ ├──
DefaultListableBeanFactory . java│ │ │ │ ├──
DefaultSingletonBeanRegistry . java│ │ │ │ ├──
DisposableBeanAdapter . java│ │ │ │ ├──
FactoryBeanRegistrySupport . java│ │ │ │ ├──
InstantiationStrategy . java│ │ │ │ └──
SimpleInstantiationStrategy . java │ │ │ ├── support│ │ │ │ └──
XmlBeanDefinitionReader . java│ │ │ ├──
Aware . java│ │ │ ├──
BeanClassLoaderAware . java│ │ │ ├──
BeanFactory . java│ │ │ ├──
BeanFactoryAware . java│ │ │ ├──
BeanNameAware . java│ │ │ ├──
ConfigurableListableBeanFactory . java│ │ │ ├──
DisposableBean . java│ │ │ ├──
FactoryBean . java│ │ │ ├──
HierarchicalBeanFactory . java│ │ │ ├──
InitializingBean . java│ │ │ └──
ListableBeanFactory . java│ │ ├──
BeansException . java│ │ ├──
PropertyValue . java│ │ └──
PropertyValues . java │ ├── context │ │ ├── event│ │ │ ├──
AbstractApplicationEventMulticaster . java │ │ │ ├──
ApplicationContextEvent . java │ │ │ ├──
ApplicationEventMulticaster . java │ │ │ ├──
ContextClosedEvent . java │ │ │ ├──
ContextRefreshedEvent . java │ │ │ └──
SimpleApplicationEventMulticaster . java │ │ ├── support│ │ │ ├──
AbstractApplicationContext . java │ │ │ ├──
AbstractRefreshableApplicationContext . java │ │ │ ├──
AbstractXmlApplicationContext . java │ │ │ ├──
ApplicationContextAwareProcessor . java │ │ │ └──
ClassPathXmlApplicationContext . java │ │ ├──
ApplicationContext . java │ │ ├──
ApplicationContextAware . java │ │ ├──
ApplicationEvent . java │ │ ├──
ApplicationEventPublisher . java │ │ ├──
ApplicationListener . java │ │ └──
ConfigurableApplicationContext . java│ ├── core
. io│ │ ├──
ClassPathResource . java │ │ ├──
DefaultResourceLoader . java │ │ ├──
FileSystemResource . java │ │ ├──
Resource . java │ │ ├──
ResourceLoader . java │ │ └──
UrlResource . java│ └── utils│ └──
ClassUtils . java└── test└── java└── cn
. bugstack
. springframework
. test├── event│ ├──
ContextClosedEventListener . java│ ├──
ContextRefreshedEventListener . java│ ├──
CustomEvent . java│ └──
CustomEventListener . java└──
ApiTest . java
容器事件和事件監聽器實現類關系,如圖:
以上整個類關系圖以圍繞實現 event 事件定義、發布、監聽功能實現和把事件的相關內容使用AbstractApplicationContext#refresh 進行注冊和處理操作。 在實現的過程中主要以擴展 spring context 包為主,事件的實現也是在這個包下進行擴展的,當然也可以看出來目前所有的實現內容,仍然是以IOC為主。 ApplicationContext 容器繼承事件發布功能接口ApplicationEventPublisher,并在實現類中提供事件監聽功能。 ApplicationEventMulticaster接口是注冊監聽器和發布事件的廣播器,提供添加、移除和發布事件方法。 最后是發布容器關閉事件,這個仍然需要擴展到AbstractApplicationContext#close 方法中,由注冊到虛擬機的鉤子實現。
定義和實現事件
public abstract class ApplicationEvent extends EventObject { public ApplicationEvent ( Object source
) { super ( source
) ; }
}
以繼承 java.util.EventObject 定義出具備事件功能的抽象類ApplicationEvent,后續所有事件的類都需要繼承這個類。
public class ApplicationContextEvent extends ApplicationEvent { public ApplicationContextEvent ( Object source
) { super ( source
) ; } public final ApplicationContext getApplicationContext ( ) { return ( ApplicationContext ) getSource ( ) ; }
}
public class ContextClosedEvent extends ApplicationContextEvent { public ContextClosedEvent ( Object source
) { super ( source
) ; }
}
public class ContextRefreshedEvent extends ApplicationContextEvent { public ContextRefreshedEvent ( Object source
) { super ( source
) ; }
}
ApplicationContextEvent 是定義事件的抽象類,所有的事件包括關閉、刷新,以及用戶自己實現的事件,都需要繼承這個類。 ContextClosedEvent、ContextRefreshedEvent,分別是 Spring 框架自己實現的兩個事件類,可以用于監聽刷新和關閉動作。
事件廣播器
public interface ApplicationEventMulticaster { void addApplicationListener ( ApplicationListener < ? > listener
) ; void removeApplicationListener ( ApplicationListener < ? > listener
) ; void multicastEvent ( ApplicationEvent event
) ;
}
在事件廣播器中定義了添加監聽和刪除監聽的方法以及一個廣播事件的方法 multicastEvent 最終推送時間消息也會經過這個接口方法來處理誰該接收事件。
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster , BeanFactoryAware { public final Set < ApplicationListener < ApplicationEvent > > applicationListeners
= new LinkedHashSet < > ( ) ; private BeanFactory beanFactory
; @Override public void addApplicationListener ( ApplicationListener < ? > listener
) { applicationListeners
. add ( ( ApplicationListener < ApplicationEvent > ) listener
) ; } @Override public void removeApplicationListener ( ApplicationListener < ? > listener
) { applicationListeners
. remove ( listener
) ; } @Override public final void setBeanFactory ( BeanFactory beanFactory
) { this . beanFactory
= beanFactory
; } protected Collection < ApplicationListener > getApplicationListeners ( ApplicationEvent event
) { LinkedList < ApplicationListener > allListeners
= new LinkedList < ApplicationListener > ( ) ; for ( ApplicationListener < ApplicationEvent > listener
: applicationListeners
) { if ( supportsEvent ( listener
, event
) ) allListeners
. add ( listener
) ; } return allListeners
; } protected boolean supportsEvent ( ApplicationListener < ApplicationEvent > applicationListener
, ApplicationEvent event
) { Class < ? extends ApplicationListener > listenerClass
= applicationListener
. getClass ( ) ; Class < ? > targetClass
= ClassUtils . isCglibProxyClass ( listenerClass
) ? listenerClass
. getSuperclass ( ) : listenerClass
; Type genericInterface
= targetClass
. getGenericInterfaces ( ) [ 0 ] ; Type actualTypeArgument
= ( ( ParameterizedType ) genericInterface
) . getActualTypeArguments ( ) [ 0 ] ; String className
= actualTypeArgument
. getTypeName ( ) ; Class < ? > eventClassName
; try { eventClassName
= Class . forName ( className
) ; } catch ( ClassNotFoundException e
) { throw new BeansException ( "wrong event class name: " + className
) ; } return eventClassName
. isAssignableFrom ( event
. getClass ( ) ) ; }
AbstractApplicationEventMulticaster 是對事件廣播器的公用方法提取,在這個類中可以實現一些基本功能,避免所有直接實現接口放還需要處理細節。 除了像 addApplicationListener、removeApplicationListener,這樣的通用方法,這里這個類中主要是對 getApplicationListeners 和 supportsEvent 的處理。 getApplicationListeners方法主要是摘取符合廣播事件中的監聽處理器,具體過濾動作在 supportsEvent 方法中。 在 supportsEvent方法中,主要包括對Cglib、Simple不同實例化需要獲取目標Class,Cglib代理類需要獲取父類的Class,普通實例化的不需要。接下來就是通過提取接口和對應的ParameterizedType 和 eventClassName,方便最后確認是否為子類和父類的關系,以此證明此事件歸這個符合的類處理。可以參考代碼中的注釋
supportsEvent 方法運行截圖
在代碼調試中可以看到,最終 eventClassName 和 event.getClass() 在 isAssignableFrom 判斷下為 true 關于 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 可以嘗試在 AbstractApplicationContext 類中更換驗證。
事件發布者的定義和實現
public interface ApplicationEventPublisher { void publishEvent ( ApplicationEvent event
) ;
}
ApplicationEventPublisher 是整個一個事件的發布接口,所有的事件都需要從這個接口發布出去。
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME
= "applicationEventMulticaster" ; private ApplicationEventMulticaster applicationEventMulticaster
; @Override public void refresh ( ) throws BeansException { initApplicationEventMulticaster ( ) ; registerListeners ( ) ; finishRefresh ( ) ; } private void initApplicationEventMulticaster ( ) { ConfigurableListableBeanFactory beanFactory
= getBeanFactory ( ) ; applicationEventMulticaster
= new SimpleApplicationEventMulticaster ( beanFactory
) ; beanFactory
. registerSingleton ( APPLICATION_EVENT_MULTICASTER_BEAN_NAME
, applicationEventMulticaster
) ; } private void registerListeners ( ) { Collection < ApplicationListener > applicationListeners
= getBeansOfType ( ApplicationListener . class ) . values ( ) ; for ( ApplicationListener listener
: applicationListeners
) { applicationEventMulticaster
. addApplicationListener ( listener
) ; } } private void finishRefresh ( ) { publishEvent ( new ContextRefreshedEvent ( this ) ) ; } @Override public void publishEvent ( ApplicationEvent event
) { applicationEventMulticaster
. multicastEvent ( event
) ; } @Override public void close ( ) { publishEvent ( new ContextClosedEvent ( this ) ) ; getBeanFactory ( ) . destroySingletons ( ) ; }
}
在抽象應用上下文 AbstractApplicationContext#refresh 中,主要新增了初始化事件發布者、注冊事件監聽器、發布容器刷新完成事件,三個方法用于處理事件操作。 初始化事件發布者(initApplicationEventMulticaster),主要用于實例化一個SimpleApplicationEventMulticaster,這是一個事件廣播器。 注冊事件監聽器(registerListeners),通過 getBeansOfType 方法獲取到所有從 spring.xml中加載到的事件配置 Bean 對象。 發布容器刷新完成事件(finishRefresh),發布了第一個服務器啟動完成后的事件,這個事件通過 publishEvent 發布出去,其實也就是調用了applicationEventMulticaster.multicastEvent(event); 方法。 最后是一個 close方法中,新增加了發布一個容器關閉事件。publishEvent(new ContextClosedEvent(this));
五、測試
創建一個事件和監聽器
public class CustomEvent extends ApplicationContextEvent { private Long id
; private String message
; public CustomEvent ( Object source
, Long id
, String message
) { super ( source
) ; this . id
= id
; this . message
= message
; }
}
創建一個自定義事件,在事件類的構造函數中可以添加自己的想要的入參信息。這個事件類最終會被完成的拿到監聽里,所以你添加的屬性都會被獲得到。
public class CustomEventListener implements ApplicationListener < CustomEvent > { @Override public void onApplicationEvent ( CustomEvent event
) { System . out
. println ( "收到:" + event
. getSource ( ) + "消息;時間:" + new Date ( ) ) ; System . out
. println ( "消息:" + event
. getId ( ) + ":" + event
. getMessage ( ) ) ; }
}
這個是一個用于監聽 CustomEvent 事件的監聽器,這里你可以處理自己想要的操作,比如一些用戶注冊后發送優惠券和短信通知等。 另外是關于 ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent>、ContextClosedEventListener implements ApplicationListener<ContextClosedEvent> 監聽器,這里就不演示了,可以參考下源碼。
配置文件
<?xml version="1.0" encoding="UTF-8"?>
< beans> < bean class = " cn.bugstack.springframework.test.event.ContextRefreshedEventListener" /> < bean class = " cn.bugstack.springframework.test.event.CustomEventListener" /> < bean class = " cn.bugstack.springframework.test.event.ContextClosedEventListener" />
</ beans>
在 spring.xml 中配置了三個事件監聽器,監聽刷新、監控自定義事件、監聽關閉事件。
單元測試
public class ApiTest { @Test public void test_event ( ) { ClassPathXmlApplicationContext applicationContext
= new ClassPathXmlApplicationContext ( "classpath:spring.xml" ) ; applicationContext
. publishEvent ( new CustomEvent ( applicationContext
, 1019129009086763L , "成功了!" ) ) ; applicationContext
. registerShutdownHook ( ) ; }
}
通過使用 applicationContext 新增加的發布事件接口方法,發布一個自定義事件CustomEvent,并透傳了相應的參數信息。
測試結果
刷新事件:
cn. bugstack. springframework. test. event. ContextRefreshedEventListener$$
EnhancerByCGLIB $$
440 a36f5
收到:
cn. bugstack. springframework. context. support. ClassPathXmlApplicationContext@ea4a92b 消息
; 時間:
Mon Aug 16 14 : 27 : 49 CST
2021
消息:
1019129009086763 : 成功了!
關閉事件:
cn. bugstack. springframework. test. event. ContextClosedEventListener$$
EnhancerByCGLIB $$f4d4b18d
從測試結果可以看到,我們自己定義的事件和監聽,以及監聽系統的事件信息,都可以在控制臺完整的輸出出來了。你也可以嘗試增加一些其他事件行為,并調試代碼學習觀察者模式。
六、總結
在整個手寫 Spring框架的學習過程中,可以逐步看到很多設計模式的使用,比如:簡單工廠BeanFactory、工廠方法FactoryBean、策略模式訪問資源,現在又實現了一個觀察者模式的具體使用。所以學習Spring 的過程中,要更加注意關于設計模式的運用,這是你能讀懂代碼的核心也是學習的重點。 那么本章節關于觀察者模式的實現過程,主要包括了事件的定義、事件的監聽和發布事件,發布完成后根據匹配策略,監聽器就會收到屬于自己的事件內容,并做相應的處理動作,這樣的觀察者模式其實日常我們也經常使用,不過在結合Spring 以后,除了設計模式的學習,還可以學到如何把相應觀察者的實現和應用上下文結合。 所有在 Spring學習到的技術、設計、思路都是可以和實際的業務開發結合起來的,而這些看似比較多的代碼模塊,其實也是按照各自職責一點點的擴充進去的。在自己的學習過程中,可以先動手嘗試完成這些框架功能,在一點點通過調試的方式與Spring 源碼進行對照參考,最終也就慢慢掌握這些設計和編碼能力了。
與50位技術專家面對面 20年技術見證,附贈技術全景圖
總結
以上是生活随笔 為你收集整理的手写简版spring --10--容器事件和事件监听器 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。