作者:小傅哥 博客:https://
? 沉淀、分享、成長,讓自己和他人都能有所收獲!??
? 目錄 一、前言
二、目標
三、方案
四、實現
1. 工程結構
2. 定義和實現事件
3. 事件廣播器
4. 事件發(fā)布者的定義和實現
五、測試
六、總結
七、系列推薦
一、前言 能解耦,是多么重要的一件事情!
摔杯為號、看我眼色行事、見南面火起,這是在嘎哈么?這其實是在通過事物傳播進行解耦引線和炸彈,僅僅是這樣的一個解耦,它放到了多少村夫莽漢,劫了法場,篡了兵權!
這樣的解耦場景在互聯網開發(fā)的設計中使用的也是非常頻繁,如:這里需要一個注冊完成事件推送消息
、用戶下單我會發(fā)送一個MQ
、收到我的支付消息就可以發(fā)貨了
等等,都是依靠事件訂閱和發(fā)布以及MQ消息這樣的組件,來處理系統(tǒng)之間的調用解耦,最終通過解耦的方式來提升整體系統(tǒng)架構的負載能力。
其實解耦思路可以理解為設計模式中觀察者模式的具體使用效果,在觀察者模式中當對象間存在一對多關系時,則使用觀察者模式,它是一種定義對象間的一種一對多的依賴關系,當一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新。這讓我想起了我每個月的車牌搖號,都會推送給我一條本月沒中簽的消息!??!
二、目標 在 Spring 中有一個 Event 事件功能,它可以提供事件的定義、發(fā)布以及監(jiān)聽事件來完成一些自定義的動作。比如你可以定義一個新用戶注冊的事件,當有用戶執(zhí)行注冊完成后,在事件監(jiān)聽中給用戶發(fā)送一些優(yōu)惠券和短信提醒,這樣的操作就可以把屬于基本功能的注冊和對應的策略服務分開,降低系統(tǒng)的耦合。以后在擴展注冊服務,比如需要添加風控策略、添加實名認證、判斷用戶屬性等都不會影響到依賴注冊成功后執(zhí)行的動作。
那么在本章節(jié)我們需要以觀察者模式的方式,設計和實現 Spring Event 的容器事件和事件監(jiān)聽器功能,最終可以讓我們在現有實現的 Spring 框架中可以定義、監(jiān)聽和發(fā)布自己的事件信息。
三、方案 其實事件的設計本身就是一種觀察者模式的實現,它所要解決的就是一個對象狀態(tài)改變給其他對象通知的問題,而且要考慮到易用和低耦合,保證高度的協(xié)作。
在功能實現上我們需要定義出事件類、事件監(jiān)聽、事件發(fā)布,而這些類的功能需要結合到 Spring 的 AbstractApplicationContext#refresh(),以便于處理事件初始化和注冊事件監(jiān)聽器的操作。整體設計結構如下圖:
在整個功能實現過程中,仍然需要在面向用戶的應用上下文 AbstractApplicationContext
中添加相關事件內容,包括:初始化事件發(fā)布者、注冊事件監(jiān)聽器、發(fā)布容器刷新完成事件。 使用觀察者模式定義事件類、監(jiān)聽類、發(fā)布類,同時還需要完成一個廣播器的功能,接收到事件推送時進行分析處理符合監(jiān)聽事件接受者感興趣的事件,也就是使用 isAssignableFrom 進行判斷。 isAssignableFrom 和 instanceof 相似,不過 isAssignableFrom 是用來判斷子類和父類的關系的,或者接口的實現類和接口的關系的,默認所有的類的終極父類都是Object。如果A.isAssignableFrom(B)結果是true,證明B可以轉換成為A,也就是A可以由B轉換而來。 四、實現 1. 工程結構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
工程源碼 :公眾號「bugstack蟲洞?!梗貜停篠pring 專欄,獲取完整源碼
容器事件和事件監(jiān)聽器實現類關系,如圖 11-2
圖 10-2 以上整個類關系圖以圍繞實現 event 事件定義、發(fā)布、監(jiān)聽功能實現和把事件的相關內容使用 AbstractApplicationContext#refresh 進行注冊和處理操作。 在實現的過程中主要以擴展 spring context 包為主,事件的實現也是在這個包下進行擴展的,當然也可以看出來目前所有的實現內容,仍然是以IOC為主。 ApplicationContext 容器繼承事件發(fā)布功能接口 ApplicationEventPublisher,并在實現類中提供事件監(jiān)聽功能。 ApplicationEventMulticaster 接口是注冊監(jiān)聽器和發(fā)布事件的廣播器,提供添加、移除和發(fā)布事件方法。 最后是發(fā)布容器關閉事件,這個仍然需要擴展到 AbstractApplicationContext#close 方法中,由注冊到虛擬機的鉤子實現。 2. 定義和實現事件cn.bugstack.springframework.context.ApplicationEvent
public abstract class ApplicationEvent extends EventObject { /** * Constructs a prototypical Event. * * @param source The object on which the Event initially occurred. * @throws IllegalArgumentException if source is null. */ public ApplicationEvent (Object source) { super (source); } }
以繼承 java.util.EventObject 定義出具備事件功能的抽象類 ApplicationEvent,后續(xù)所有事件的類都需要繼承這個類。 cn.bugstack.springframework.context.event.ApplicationContextEvent
public class ApplicationContextEvent extends ApplicationEvent { /** * Constructs a prototypical Event. * * @param source The object on which the Event initially occurred. * @throws IllegalArgumentException if source is null. */ public ApplicationContextEvent (Object source) { super (source); } /** * Get the <code>ApplicationContext</code> that the event was raised for. */ public final ApplicationContext getApplicationContext () { return (ApplicationContext) getSource(); } }
cn.bugstack.springframework.context.event.ContextClosedEvent
public class ContextClosedEvent extends ApplicationContextEvent { /** * Constructs a prototypical Event. * * @param source The object on which the Event initially occurred. * @throws IllegalArgumentException if source is null. */ public ContextClosedEvent (Object source) { super (source); } }
cn.bugstack.springframework.context.event.ContextRefreshedEvent
public class ContextRefreshedEvent extends ApplicationContextEvent { /** * Constructs a prototypical Event. * * @param source The object on which the Event initially occurred. * @throws IllegalArgumentException if source is null. */ public ContextRefreshedEvent (Object source) { super (source); } }
ApplicationContextEvent 是定義事件的抽象類,所有的事件包括關閉、刷新,以及用戶自己實現的事件,都需要繼承這個類。 ContextClosedEvent、ContextRefreshedEvent,分別是 Spring 框架自己實現的兩個事件類,可以用于監(jiān)聽刷新和關閉動作。 3. 事件廣播器cn.bugstack.springframework.context.event.ApplicationEventMulticaster
public interface ApplicationEventMulticaster { /** * Add a listener to be notified of all events. * @param listener the listener to add */ void addApplicationListener (ApplicationListener<?> listener) ; /** * Remove a listener from the notification list. * @param listener the listener to remove */ void removeApplicationListener (ApplicationListener<?> listener) ; /** * Multicast the given application event to appropriate listeners. * @param event the event to multicast */ void multicastEvent (ApplicationEvent event) ; }
在事件廣播器中定義了添加監(jiān)聽和刪除監(jiān)聽的方法以及一個廣播事件的方法 multicastEvent
最終推送時間消息也會經過這個接口方法來處理誰該接收事件。 cn.bugstack.springframework.context.event.AbstractApplicationEventMulticaster
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; } /** * 監(jiān)聽器是否對該事件感興趣 */ protected boolean supportsEvent (ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) { Class<? extends ApplicationListener> listenerClass = applicationListener.getClass(); // 按照 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 不同的實例化類型,需要判斷后獲取目標 class 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); } // 判定此 eventClassName 對象所表示的類或接口與指定的 event.getClass() 參數所表示的類或接口是否相同,或是否是其超類或超接口。 // isAssignableFrom是用來判斷子類和父類的關系的,或者接口的實現類和接口的關系的,默認所有的類的終極父類都是Object。如果A.isAssignableFrom(B)結果是true,證明B可以轉換成為A,也就是A可以由B轉換而來。 return eventClassName.isAssignableFrom(event.getClass()); } }
AbstractApplicationEventMulticaster 是對事件廣播器的公用方法提取,在這個類中可以實現一些基本功能,避免所有直接實現接口放還需要處理細節(jié)。 除了像 addApplicationListener、removeApplicationListener,這樣的通用方法,這里這個類中主要是對 getApplicationListeners 和 supportsEvent 的處理。 getApplicationListeners 方法主要是摘取符合廣播事件中的監(jiān)聽處理器,具體過濾動作在 supportsEvent 方法中。 在 supportsEvent 方法中,主要包括對Cglib、Simple不同實例化需要獲取目標Class,Cglib代理類需要獲取父類的Class,普通實例化的不需要。接下來就是通過提取接口和對應的 ParameterizedType 和 eventClassName,方便最后確認是否為子類和父類的關系,以此證明此事件歸這個符合的類處理。可以參考代碼中的注釋 supportsEvent 方法運行截圖
在代碼調試中可以看到,最終 eventClassName 和 event.getClass() 在 isAssignableFrom 判斷下為 true 關于 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 可以嘗試在 AbstractApplicationContext 類中更換驗證。 4. 事件發(fā)布者的定義和實現cn.bugstack.springframework.context.ApplicationEventPublisher
public interface ApplicationEventPublisher { /** * Notify all listeners registered with this application of an application * event. Events may be framework events (such as RequestHandledEvent) * or application-specific events. * @param event the event to publish */ void publishEvent (ApplicationEvent event) ; }
ApplicationEventPublisher 是整個一個事件的發(fā)布接口,所有的事件都需要從這個接口發(fā)布出去。 cn.bugstack.springframework.context.support.AbstractApplicationContext
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 { // 6. 初始化事件發(fā)布者 initApplicationEventMulticaster(); // 7. 注冊事件監(jiān)聽器 registerListeners(); // 9. 發(fā)布容器刷新完成事件 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 () { // 發(fā)布容器關閉事件 publishEvent(new ContextClosedEvent(this )); // 執(zhí)行銷毀單例bean的銷毀方法 getBeanFactory().destroySingletons(); } }
在抽象應用上下文 AbstractApplicationContext#refresh 中,主要新增了 初始化事件發(fā)布者
、注冊事件監(jiān)聽器
、發(fā)布容器刷新完成事件
,三個方法用于處理事件操作。 初始化事件發(fā)布者(initApplicationEventMulticaster),主要用于實例化一個 SimpleApplicationEventMulticaster,這是一個事件廣播器。 注冊事件監(jiān)聽器(registerListeners),通過 getBeansOfType 方法獲取到所有從 spring.xml 中加載到的事件配置 Bean 對象。 發(fā)布容器刷新完成事件(finishRefresh),發(fā)布了第一個服務器啟動完成后的事件,這個事件通過 publishEvent 發(fā)布出去,其實也就是調用了 applicationEventMulticaster.multicastEvent(event); 方法。 最后是一個 close 方法中,新增加了發(fā)布一個容器關閉事件。publishEvent(new ContextClosedEvent(this));
五、測試 1. 創(chuàng)建一個事件和監(jiān)聽器cn.bugstack.springframework.test.event.CustomEvent
public class CustomEvent extends ApplicationContextEvent { private Long id; private String message; /** * Constructs a prototypical Event. * * @param source The object on which the Event initially occurred. * @throws IllegalArgumentException if source is null. */ public CustomEvent (Object source, Long id, String message) { super (source); this .id = id; this .message = message; } // ...get/set }
創(chuàng)建一個自定義事件,在事件類的構造函數中可以添加自己的想要的入參信息。這個事件類最終會被完成的拿到監(jiān)聽里,所以你添加的屬性都會被獲得到。 cn.bugstack.springframework.test.event.CustomEventListener
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()); } }
這個是一個用于監(jiān)聽 CustomEvent 事件的監(jiān)聽器,這里你可以處理自己想要的操作,比如一些用戶注冊后發(fā)送優(yōu)惠券和短信通知等。 另外是關于 ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent>
、ContextClosedEventListener implements ApplicationListener<ContextClosedEvent>
監(jiān)聽器,這里就不演示了,可以參考下源碼。 2. 配置文件<?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 中配置了三個事件監(jiān)聽器,監(jiān)聽刷新、監(jiān)控自定義事件、監(jiān)聽關閉事件。 3. 單元測試public class ApiTest { @Test public void test_event () { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml" ); applicationContext.publishEvent(new CustomEvent(applicationContext, 1019129009086763L , "成功了!" )); applicationContext.registerShutdownHook(); } }
通過使用 applicationContext 新增加的發(fā)布事件接口方法,發(fā)布一個自定義事件 CustomEvent,并透傳了相應的參數信息。 測試結果
刷新事件:cn.bugstack.springframework.test.event.ContextRefreshedEventListener$$EnhancerByCGLIB$$440 a36f5 收到:cn.bugstack.springframework.context.support.ClassPathXmlApplicationContext@71 c7db30消息;時間:22 :32 :50 消息:1019129009086763 :成功了! 關閉事件:cn.bugstack.springframework.test.event.ContextClosedEventListener$$EnhancerByCGLIB$$f4d4b18d Process finished with exit code 0
從測試結果可以看到,我們自己定義的事件和監(jiān)聽,以及監(jiān)聽系統(tǒng)的事件信息,都可以在控制臺完整的輸出出來了。你也可以嘗試增加一些其他事件行為,并調試代碼學習觀察者模式。 六、總結 在整個手寫 Spring 框架的學習過程中,可以逐步看到很多設計模式的使用,比如:簡單工廠BeanFactory、工廠方法FactoryBean、策略模式訪問資源,現在又實現了一個觀察者模式的具體使用。所以學習 Spring 的過程中,要更加注意關于設計模式的運用,這是你能讀懂代碼的核心也是學習的重點。 那么本章節(jié)關于觀察者模式的實現過程,主要包括了事件的定義、事件的監(jiān)聽和發(fā)布事件,發(fā)布完成后根據匹配策略,監(jiān)聽器就會收到屬于自己的事件內容,并做相應的處理動作,這樣的觀察者模式其實日常我們也經常使用,不過在結合 Spring 以后,除了設計模式的學習,還可以學到如何把相應觀察者的實現和應用上下文結合。 所有在 Spring 學習到的技術、設計、思路都是可以和實際的業(yè)務開發(fā)結合起來的,而這些看似比較多的代碼模塊,其實也是按照各自職責一點點的擴充進去的。在自己的學習過程中,可以先動手嘗試完成這些框架功能,在一點點通過調試的方式與 Spring 源碼進行對照參考,最終也就慢慢掌握這些設計和編碼能力了。 七、系列推薦