吳雨桐
(四川大學錦城學院 計算機與軟件學院,四川 成都 611731)
設計模式是一套可以被反復使用、程序員熟知的、經過分類編目的代碼設計經驗的一種總結。使用設計模式主要是為了可重用代碼、讓代碼更容易被他人理解、保證代碼的可靠性。毫無疑問,設計模式無論是于人于己還是對于程序來講都是多贏的,設計模式使代碼編制真正工程化,就像軟件工程的基石,和大廈的一塊塊磚石一樣。設計模式分為創建模式、結構模式和行為模式三大類。其中創建模式一般對于對象的實例創建比較有用。結構模式通過處理對象和類之間的組合來作用于企業級開發應用的設計結構,以達到降低項目開發的復雜度,提高其系統的可復用性和性能。行為模式是作用域一組對象之間的交互作用。
Spring 是編程領域的一個輕量級開源框架,被廣大java 工程師所熟知。該框架在 2002 年最早提出并隨后創建,是為了解決企業級編程開發的復雜性,實現快捷開發的應用型框架 。Spring 是一個開源容器框架,集成各類型的工具,通過核心的Bean Factory 實現了底層的類的實例化和生命周期的管理。在整個框架中,各類型的功能被抽象成一個個的 Bean,這樣就可以實現各種功能的管理,包括動態加載和切面編程。當然在這種開源框架中,也會用到萬能的設計模式。在Spring 中大概使用到了九種設計模式。這九種分別是簡單工廠(非二十三種設計模式中的一種)、工廠方法模式、單例模式、適配器模式、裝飾器模式、代理模式、觀察者模式、策略模式以及模塊方法模式。
簡單工廠模式又名靜態工廠方法,簡單工廠模式簡單來說就是由一個工廠類來接收參數并動態的決定創建哪一個產品類。
在Spring 中,Bean Factory 就是簡單工廠模式的應用。它作為IOC 最基本的容器,負責生產和管理Bean,并且它還為其他具體的IOC 容器提供了基本規范。如果說它是Spring 里面最低層的接口,提供了最簡單的容器功能,那么Application Context 就是更高級的容器,它繼承Bean Factory 接口,還增加了更多新功能,如國際化,可訪問資源以及消息發送、響應機制等。
第一步是創建一個Student 實體類。第二步是在配置文件中傳入參數,通過id 屬性指定參數,通過class 指定具體的類,通過scope 屬性來定義是否為單例。第三步是測試,創建一個main 函數,通過new Class Path Xml Application Context 來指定xml 文件,用get Bean 方法來傳入參數,從而獲取到xml 中配置的類。
在使用簡單工廠的時候通常不用創建簡單工廠類的實例,因此可以把簡單工廠類實現成一個工具類,直接使用靜態方法結果。也就是說簡單工廠的方法通常都是靜態的,所以也被稱為靜態工廠。如果放置客戶端隨意創造簡單工廠實例,也可以把簡單工廠的構造方法私有化。想要理解Spring 里面的簡單工廠模式其實很簡單,想想我們在學習java 設計模式的時候,做簡單工廠實例的流程,其必不可缺的流程就是工廠類,沒有工廠你就什么也拿不到。在Spring 中,Bean Factory 就是這么一個工廠,如果沒有這個工廠,就好比我們需要工廠里生產的商品,但是拿到手還得我們自己動手,不僅麻煩還不利于日后的維護。
這種模式涉及到的是一個單一的類。該類只負責創建自己的對象,同時確保只創建單個對象。在上述簡單工廠模式實例中其實已經設計到了單例模式的運用。
在Spring 中,依賴注入默認就是為單例,依賴注入是發生在Abstract Bean Factory 的get Bean 方法中,其中的do Get Bean方法調用get Singleton 進行Bean 的創建。單例模式的定義為:保證一個類有且僅有一個實例,并提供一個訪問它的全局訪問點。Spring 對單例的實現就是完成了后面半句話,即提供了一個全局的訪問點Bean Factory。而且在Spring 中實現單例模式只需要設置一個屬性或者一個注解。
Xml 配置:在上面簡單工廠模式中有提到xml 文件配置,在配置中有這么一個屬性scope,將代碼中的Prototype 改成Singleton 即可。
注解:@Scope
在我們的項目中,有一些對象確實只需要創建一個就夠了,比如線程池、緩存、日志。創建的太多反而會成為負擔,所以在Spring 中默認的就是單例。一般我們都說,最成功的單例并不是雙重檢驗鎖,而是枚舉,枚舉本身就是一種單例,并且無法做到反射攻擊,再一個最優雅的無非就是Spring 本身實現的單例:在Spring 中,在一些注解作用下的類默認都是單例的,目前通過我對Spring 的了解,我認為最優注解為@Component,主要使用場景有:數據庫的配置、Redis 的配置、權限的配置、Filter 的過濾、攔截器、swagger 及自定義的時間轉換器、類型轉換器、對接第三方硬件時,調用硬件的dll、so文件等。我個人比較常遇到的就是數據庫配置以及攔截器的配置。當然在Spring 中也不是所有注解在默認情況下都是單例的,因為@Component+@Bean 并不是單例,在調用過程中可能會創建多個實例,出現錯誤,所以要避免這種組合使用,另外,在控制器層中的常用注解@Restcontroller 也是多例的。
代理模式是給某一個對象提供一個代理對象,并由代理對象來控制對原對象的引用.代理模式還分靜態代理與動態代理。
AOP(面向切面編程)五大通知類型:

圖1 五大通知類型
代碼為切面類,注解使用@Aspect,將該類定義標識為一個切面供容器來讀取,用Logger Factory 來記錄日志,@Pointcut注解是植入Advice 的觸發條件,它定義的是表達式和方法簽名。方法簽名一定要是公開以及無返回值類型的,然后根據實際需求選擇圖3 中的五大通知類型。
結果:以登錄操作為例

圖2 日志
代理模式可以將那些與業務無關,但又可以將一些業務模塊共同調用的責任或邏輯封裝起來。為了增加其代碼的復用性,降低各個模塊間的耦合度,并且有利于項目未來的可拓展性和可維護性。最常見的一些實用場景如下面這些:日志的記錄、跟蹤、優化與監控和事務的處理、持久化、性能優化、資源池、連接池的管理、系統的權限管理以及緩存、錯誤處理、調試、加載等。上文便是通過AOP 實現了日志記錄。而且在Spring AOP 中實現的是一種動態代理,如果目標方法有接口的時候會自動選用JDK 動態代理目標,相反如果方法沒有接口時候則會選擇選擇 CGLib 動態代理。
適配器模式的目的是將一個類接口轉變為客戶端目標接口,從而使因不匹配而無法工作的兩個類能一起工作,因此又被稱為轉換器模式等。
前文提到,Spring AOP 是基于代理模式來實現的,在Spring AOP 中通過使用的 Advice(通知)來增強被代理類的功能。與之相關的接口為Advisor Adapter,通知的類型與圖1 中相似,分為方法前、方法后等等。每個類型的通知都有對應的攔截器,Spring 需要將每個通知都封裝成對應的攔截器類型,返回給容器,所以需要使用適配器模式對 Advice 進行轉換。
Spring 框架的子模塊Spring MVC,使用處理器分離模型、控制和視圖的開發模式,提高系統服用性、可維護性和靈活性的開發模式,達到不同技術層級間松耦合的效果。其中,Dispatcher Servlet 根據請求信息調用Handler Mapping,解析請求對應的Handler。解析到后,開始由Handler Adapter 適配器處理。它作為期望接口,具體的適配器實現類用于對目標類進行適配,控制器作為需要適配的類。
Spring AOP 在前文代理模式中已經提到,這里就說說Spring MVC。Spring MVC 之所以要采用適配器模式,是因為當在一個項目中,控制器種類繁多,每一個控制器都會有不同的請求來進行處理,如果不使用適配器模式,就需要自行if else語句去判斷。如果這樣的話,每加一個控制器都需要加一個判斷語句,不僅使得系統難以維護,還違反了設計模式中的開閉原則。
設計模式本身是作為一套眾人知曉,經過分類編目,且被人們反復使用的代碼編寫經驗的總結。但是,這套總結僅僅存在于代碼之上。我們在項目開發,軟件開發中遇到的一些問題,僅憑著使用設計模式還是遠遠不夠的,于是Spring 應運而生。它是由于開發的復雜性而創建的。Spring 與現有的技術不同,它更多強調的是面向對象。它的初衷便是為了讓軟件開發變得更加簡單,開發人員從以前的使用類轉向更好的使用接口,并且,它將使用接口的復雜度降到了最低,幾乎接近零。其實我們作為一個程序員,無論是前端還是后端,設計模式的重要性是其他所有框架所不能比擬的。現在大多數框架中都用到了很多的設計模式。正是因為運用到的這些設計模式,才使得我們在使用框架進行項目開發時能夠做到游刃有余。