版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領
文檔簡介
1、85 | 開源實戰(zhàn)四(中):剖析Spring框架中用來支持擴展的兩種設計模式2020-05-18 王爭設計模式之美進入課程講述:馮永吉時長 10:44 大小 9.84M上一節(jié)課中,我們學習了 Spring 框架背后蘊藏的一些經(jīng)典設計思想,比如約定優(yōu)于配置、低侵入松耦合、模塊化輕量級等等。我們可以將這些設計思想借鑒到其他框架開發(fā)中,在大的設計層面提高框架的代碼質(zhì)量。這也是我們在專欄中講解這部分內(nèi)容的原因。除了上一節(jié)課中講到的設計思想,實際上,可擴展也是大部分框架應該具備的一個重要特性。所謂的框架可擴展,我們之前也提到過,意思就是,框架使用者在不修改框架源碼的情況下,基于擴展點定制擴展新的功能。前
2、面在理論部分,我們也講到,常用來實現(xiàn)擴展特性的設計模式有:觀察者模式、模板模式、職責鏈模式、策略模式等。今天,我們再剖析 Spring 框架為了支持可擴展特性用的 2加微信:642945106 發(fā)送“贈送”領取贈送精品課程發(fā)數(shù)字“2”獲取眾籌列表下載APP種設計模式:觀察者模式和模板模式。話不多說,讓我們正式開始今天的學習吧!觀察者模式在 Spring 中的應用在前面我們講到,Java、Google Guava 都提供了觀察者模式的實現(xiàn)框架。Java 提供的框架比較簡單,只包含 java.util.Observable 和 java.util.Observer 兩個類。Google Guava
3、提供的框架功能比較完善和強大:通過 EventBus總線來實現(xiàn)觀察者模式。實際上,Spring 也提供了觀察者模式的實現(xiàn)框架。今天,我們就再來講一講它。Spring 中實現(xiàn)的觀察者模式包含三部分:Event(相當于消息)、Listener者(相當于觀察者)、Publisher 發(fā)送者(相當于被觀察者)。我們通過一個例子來看下,Spring 提供的觀察者模式是怎么使用的。代碼如下所示:復制代碼12345678910111213141516171819202122232425262728/ Eventpublic class DemoEvent extends ApplicationEvent p
4、rivate String message;public DemoEvent(Object source, String message) super(source);public String getMessage() return this.message;/ Listener者Componentpublic class DemoListener implements ApplicationListener Overridepublic void onApplicationEvent(DemoEvent demoEvent) String message = demoEvent.getMe
5、ssage(); System.out.println(message);/ Publisher發(fā)送者Componentpublic class DemoPublisher Autowiredprivate ApplicationContext applicationContext;從代碼中,我們可以看出,框架使用起來并不復雜,主要包含三部分工作:定義一個繼承ApplicationEvent 的(DemoEvent);定義一個實現(xiàn)了 ApplicationListener 的器(DemoListener);定義一個發(fā)送者(DemoPublisher),發(fā)送者調(diào)用ApplicationConte
6、xt 來發(fā)送消息。其中,ApplicationEvent 和 ApplicationListener 的代碼實現(xiàn)都非常簡單,內(nèi)部并不包含太多屬性和方法。實際上,它們最大的作用是做類型標識之用(繼承自 ApplicationEvent的類是,實現(xiàn) ApplicationListener 的類是器)。復制代碼1 public abstract class ApplicationEvent extends EventObject2 private static final long serialVersionUID =37099057708183571937L;45 public Applicati
7、onEvent(Object source)6 super(source);789public final long getTimestamp()10 return this.timestamp;1112 1314 public class EventObject implements java.io.Serializable 15 private static final long serialVersionUID = 5516075349620653480L;16 protected transient Objectsource;1718 public EventObject(Object
8、 source) 19 if (source = null)20 throw new IllegalArgumentException(null source);21 this.source = source;222324 public Object getSource()25 return source;262728 public String toString() 29 return getClass().getName() + source= + source + ;3029 public void publishEvent(DemoEvent demoEvent)30 this.app
9、licationContext.publishEvent(demoEvent)31;32 333132333435public interface ApplicationListener extends Event void onApplicationEvent(E var1);在前面講到觀察者模式的時候,我們提到,觀察者需要事先注冊到被觀察者(JDK 的實現(xiàn)方式)或者總線(EventBus 的實現(xiàn)方式)中。那在 Spring 的實現(xiàn)中,觀察者注冊到了哪里呢?又是如何注冊的呢?我想你應該猜到了,我們把觀察者注冊到了 ApplicationContext 對象中。這里的ApplicationCo
10、ntext 就相當于 Google EventBus 框架中的“總線”。不過,稍微提醒一下,ApplicationContext 這個類并不只是為觀察者模式服務的。它底層依賴BeanFactory(IOC 的主要實現(xiàn)類),提供應用啟動、運行時的上下文信息,是訪問這些信息的最頂層接口。實際上,具體到源碼來說,ApplicationContext 只是一個接口,具體的代碼實現(xiàn)包含在它的實現(xiàn)類 AbstractApplicationContext 中。我把跟觀察者模式相關的代碼,摘抄到了下面。你只需要關注它是如何發(fā)送和注冊者就好,其他細節(jié)不需要細究。復制代碼123456789101112131415
11、16171819202122public abstract class AbstractApplicationContext extends . private final SetApplicationListener applicationListeners;public AbstractApplicationContext() this.applicationListeners = new LinkedHashSet();/.public void publishEvent(ApplicationEvent event) this.publishEvent(event, (Resolvab
12、leType)null);public void publishEvent(Object event) this.publishEvent(event, (ResolvableType)null);protected void publishEvent(Object event, ResolvableType eventType)/.Object applicationEvent;if (event instanceof ApplicationEvent) applicationEvent = (ApplicationEvent)event;23242526272829303132333435
13、363738394041424344454647484950515253545556575859606162636465666768697071727374applicationEvent = new PayloadApplicationEvent(this, event); if (eventType = null) eventType = (PayloadApplicationEvent)applicationEvent).getResolvableTif (this.earlyApplicationEvents != null) this.earlyApplicationEvents.a
14、dd(applicationEvent);else this.getApplicationEventMulticaster().multicastEvent (if (this.parent != null) if (this.parent instanceof AbstractApplicationContext) (AbstractApplicationContext)this.parent).publishEvent(event, eventTyp else this.parent.publishEvent(event);public void addApplicationListene
15、r(ApplicationListener listener) Assert.notNull(listener, ApplicationListener must not be null); if (this.applicationEventMulticaster != null)this.applicationEventMulticaster.addApplicationListener(listener); else this.applicationListeners.add(listener);public CollectionApplicationListener getApplica
16、tionListeners() return this.applicationListeners;protected void registerListeners() Iterator var1 = this.getApplicationListeners().iterator();while(var1.hasNext() ApplicationListener listener = (ApplicationListener)var1.next();String Stringint var3listenerBeanNames = this.getBeanNamesForType(Applica
17、tionListener. var7 = listenerBeanNames;= listenerBeanNames.length;for(int var4 = 0; var4 var3; +var4) String listenerBeanName = var7var4;this.getApplicationEventMulticaster().addApplicationListenerBean(listeneSet earlyEventsToProcess = this.earlyApplicationEvents;7576777879808182838485this.earlyAppl
18、icationEvents = null; if (earlyEventsToProcess != null) Iterator var9 = earlyEventsToProcess.iterator();while(var9.hasNext() ApplicationEvent earlyEvent = (ApplicationEvent)var9.next(); this.getApplicationEventMulticaster().multicastEvent(earlyEvent);從上面的代碼中,我們發(fā)現(xiàn),真正的消息發(fā)送,實際上是通過ApplicationEventMultic
19、aster這個類來完成的。這個類的源碼我只摘抄了最關鍵的一部分,也就是 multicastEvent() 這個消息發(fā)送函數(shù)。不過,它的代碼也并不復雜,我就不多解釋了。這里我稍微提示一下,它通過線程池,支持異步非阻塞、同步阻塞這兩種類型的觀察者模式。復制代碼1 public void multicastEvent(ApplicationEvent event) 2 this.multicastEvent(event, this.resolveDefaultEventType(event);3 45 public void multicastEvent(final ApplicationEvent
20、 event, ResolvableType eventT6 ResolvableType type = eventType != null ? eventType : this.resolveDefaultEve7 Iterator var4 = this.getApplicationListeners(event, type).iterator();89while(var4.hasNext() 10 final ApplicationListener listener = (ApplicationListener)var4.next();11 Executor executor = thi
21、s.getTaskExecutor();12 if (executor != null) 13 executor.execute(new Runnable() 14 public void run() 15 SimpleApplicationEventMulticaster.this.invokeListener(listener, even1617);18 else 19 this.invokeListener(listener, event);20212223 借助 Spring 提供的觀察者模式的骨架代碼,如果我們要在 Spring 下實現(xiàn)某個的發(fā)送展性,也就是在不需要修改任何代碼的情況
22、下,擴展新的和。模板模式在 Spring 中的應用剛剛講的是觀察者模式在 Spring 中的應用,現(xiàn)在我們再講下模板模式。我們來看下一下經(jīng)常在面試中被問到的一個問題:請你說下 Spring Bean 的創(chuàng)建過程包含哪些主要的步驟。這其中就涉及模板模式。它也體現(xiàn)了 Spring 的擴展性。利用模板模式,Spring 能讓用戶定制 Bean 的創(chuàng)建過程。Spring Bean 的創(chuàng)建過程,可以大致分為兩大步:對象的創(chuàng)建和對象的初始化。對象的創(chuàng)建是通過反射來動態(tài)生成對象,而不是 new 方法。不管是哪種方式,說白了,總歸還是調(diào)用構造函數(shù)來生成對象,沒有什么特殊的。對象的初始化有兩種實現(xiàn)方式。一種是在
23、類中自定義一個初始化函數(shù),并且通過配置文件,顯式地告知 Spring,哪個函數(shù)是初始化函數(shù)。我舉了一個例子解釋一下。如下所示,在配置文件中,我們通過 init-method 屬性來指定初始化函數(shù)。這種初始化方式有一個缺點,初始化函數(shù)并不固定,由用戶隨意定義,這就需要 Spring 通過反射,在運行時動態(tài)地調(diào)用這個初始化函數(shù)。而反射又會影響代碼執(zhí)行的性能,那有沒有替代方案呢?復制代碼1 public class DemoClass 2/.34public void initDemo() 5/.初始化.67 89 / 配置:需要通過init-method顯式地指定初始化方法10 /beanSpri
24、ng 提供了另外一個定義初始化函數(shù)的方法,那就是讓類實現(xiàn) Initializingbean 接口。這個接口包含一個固定的初始化函數(shù)定義(afterPropertiesSet() 函數(shù))。Spring 在初始化 Bean 的時候,可以直接通過 bean.afterPropertiesSet() 的方式,調(diào)用 Bean 對象上的這個函數(shù),而不需要使用反射來調(diào)用了。我舉個例子解釋一下,代碼如下所示。盡管這種實現(xiàn)方式不會用到反射,執(zhí)行效率提高了,但業(yè)務代碼(DemoClass)跟框架代碼(InitializingBean)耦合在了一起??蚣艽a侵入到了業(yè)務代碼中,替換框架的成本就變高了。所以,我并不是
25、太推薦這種寫法。實際上,在 Spring 對 Bean 整個生命周期的管理中,還有一個跟初始化相對應的過程,那就是 Bean 的銷毀過程。我們知道,在 Java 中,對象的回收是通過 JVM 來自動完成的。但是,我們可以在將 Bean 正式交給 JVM 垃圾回收前,執(zhí)行一些銷毀操作(比如關閉文件句柄等等)。銷毀過程跟初始化過程非常相似,也有兩種實現(xiàn)方式。一種是通過配置destroy-method 指定類中的銷毀函數(shù),另一種是讓類實現(xiàn) DisposableBean 接口。因為 destroy- method、DisposableBean 跟 init-method、InitializingBea
26、n 非常相似,所以,這部分我們就不詳細講解了,你可以自行研究下。實際上,Spring 針對對象的初始化過程,還做了進一步的細化,將它拆分成了三個小步驟:初始化前置操作、初始化、初始化后置操作。其中,中間的初始化操作就是我們剛剛講的那部分,初始化的前置和后置操作,定義在接口 BeanPostProcessor 中。BeanPostProcessor 的接口定義如下所示:復制代碼1 public interface BeanPostProcessor 復制代碼1 public class DemoClass implements InitializingBean2 Override3 public
27、 void afterPropertiesSet() throws Exception 4/.初始化.56 78 / 配置:不需要顯式地指定初始化方法9 2345Object postProcessBeforeInitialization(Object var1, String var2) throws BeanObject postProcessAfterInitialization(Object var1, String var2) throws Beans我們再來看下,如何通過 BeanPostProcessor 來定義初始化前置和后置操作?我們只需要定義一個實現(xiàn)了 BeanPostPr
28、ocessor 接口的處理器類,并在配置文件中像配置普通 Bean 一樣去配置就可以了。Spring 中的 ApplicationContext 會自動檢測在配置文件中實現(xiàn)了 BeanPostProcessor 接口的所有 Bean,并把它們注冊到 BeanPostProcessor 處理器列表中。在 Spring 容器創(chuàng)建 Bean 的過程中,Spring 會逐一去調(diào)用這些處理器。通過上面的分析,我們基本上弄清楚了 Spring Bean 的整個生命周期(創(chuàng)建加銷毀)。針對這個過程,我畫了一張圖,你可以結合著剛剛講解一塊看下。不過,你可能會說,這里哪里用到了模板模式啊?模板模式不是需要定義一
29、個包含模板方法的抽象模板類,以及定義子類實現(xiàn)模板方法嗎?實際上,這里的模板模式的實現(xiàn),并不是標準的抽象類的實現(xiàn)方式,而是有點類似我們前面講到的 Callback 回調(diào)的實現(xiàn)方式,也就是將要執(zhí)行的函數(shù)封裝成對象(比如,初始化方法封裝成 InitializingBean 對象),傳遞給模板(BeanFactory)來執(zhí)行。重點回顧好了,今天的內(nèi)容到此就講完了。我們一塊來總結回顧一下,你需要重點掌握的內(nèi)容。今天我講到了 Spring 中用到的兩種支持擴展的設計模式,觀察者模式和模板模式。其中,觀察者模式在 Java、Google Guava、Spring 中都有提供相應的實現(xiàn)代碼。在平時的項目開發(fā)中
30、,基于這些實現(xiàn)代碼,我們可以輕松地實現(xiàn)一個觀察者模式。Java 提供的框架比較簡單,只包含 java.util.Observable 和 java.util.Observer 兩個類。Google Guava 提供的框架功能比較完善和強大,可以通過 EventBus總線來實現(xiàn)觀察者模式。Spring 提供了觀察者模式包含 Event、Listener者、Publisher 發(fā)送者三部分。先注冊好的發(fā)送到 ApplicationContext 中,然后,ApplicationConext 將消息發(fā)送給事者。除此之外,我們還講到模板模式在 Spring 中的一個典型應用,那就是 Bean 的創(chuàng)建過程。Bean的創(chuàng)建包含兩個大的步驟,對象的創(chuàng)建和對象的初始化。其中,對象的初始化又可以分解為 3 個小的步驟:初始化前置操作、初始化、初始化后置操作。課堂討論在 Google Guava 的 EventBus 實現(xiàn)中,被觀察者發(fā)送消息到總線,總線根據(jù)消息的類型,將消息發(fā)送給可匹配的觀察者。那在 Spring 提供的觀察者模式
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 電影放映設備裝配調(diào)試工班組管理水平考核試卷含答案
- 工業(yè)氣體液化工崗前核心能力考核試卷含答案
- 因孩子拉肚子請假條
- 2025年節(jié)能技術服務項目發(fā)展計劃
- 2025年潛水及水下救撈裝備合作協(xié)議書
- 信息安全培訓課件博客
- 2025 小學一年級科學下冊莖干的繁殖方法課件
- 2026年1月20日內(nèi)蒙古國際蒙醫(yī)醫(yī)院面試真題及答案解析(下午卷)
- 2026年智能腕力球項目公司成立分析報告
- 建筑工程公司施工員崗位工作總結
- 公司兩權分離管理制度
- 車輛叉車日常檢查記錄表
- 廣東高校畢業(yè)生“三支一扶”計劃招募考試真題2024
- 膠帶機硫化工藝.課件
- 種雞免疫工作總結
- 河南省商丘市柘城縣2024-2025學年八年級上學期期末數(shù)學試題(含答案)
- 河南省信陽市2024-2025學年高二上學期1月期末英語試題(含答案無聽力原文及音頻)
- 給女朋友申請書
- 八下《桃花源記》《小石潭記》全文背誦(原文+譯文)
- 【8地RJ期末】安徽省蕪湖市2024-2025學年八年級上學期期末考試地理試卷+
- 智能法理學習通超星期末考試答案章節(jié)答案2024年
評論
0/150
提交評論