標籤:

Spring核心技術原理-(1)為什麼要有Spring?

一、知史可以明鑒

我們學習技術的時代趕上了最好的時代,跳過了很多前人經常踩的坑,前人在踩坑的過程中總結了很多經驗和教訓,而新時代的我們只是繼承了前人的經驗和教訓,而忽略了這些采坑的過程,以至於我們面對很多新技術都不知道他是什麼?他為什麼存在?他為什麼可以解決這個問題?更不知道如何掌握其原理!雲里霧裡一頭霧水!

交流群的很多小夥伴,常常私聊我讓我推薦一下學習SSM框架的視頻和資料,我首先會打開他的資料卡看一下他的年齡,如果超過了他這個年齡應有的水平,我就會問他JSP+Servlet學了嗎?很多小夥伴的回答是簡單的學了一下,然後,我會給他一個關於JSP+Servlet的實戰項目,順便給他們找一些SSM的項目,並且建議他們首先看這個JSP+Servlet的實戰項目。

更有甚者,學了基礎之後就開始學習Spring Boot的,當問他們Spring Boot是什麼的時候,大致也可以回答出來「約定大於配置」,「用起來很簡單」,但是在細究其原理,也是吱吱嗚嗚,一知半解!如果我們沒有經歷過Spring最開始繁瑣的配置、然後一步步精簡,根本體會不到為什麼會有Spring Boot這個東西!

不先學習常見的設計模式直接看Spring、MyBatis等源碼,簡直就是一個找虐的過程!不掌握Servlet原理、基本的Tomcat容器技術上來就看Spring MVC源碼同樣也是一個打擊自信心的好地方!

學習是一個循序漸進的過程,不能急於求成,但也不能過分鑽牛角尖!不能再一個技術上停滯不前,也不能如」蜻蜓點水」一般寥寥掠過!同樣,如果你還沒有掌握好Servlet和簡單的設計模式我建議你先去查閱相關的資料進行系統的學習。

我也相信很多圖書或視頻等資料都忽略了講述為什麼會有Spring的過程,要麼是簡單概括並且痛斥EJB的各種弊端,要麼就是隻字不提,這是一種對讀者很不負責任的表現,知史可以明鑒!因此,在進一步學習Spring核心原理之前,我們有必要介紹一下整個Web發展的簡單歷史,一步步引出為什麼會有Spring!

二、Web發展簡史

老一輩的軟體開發人員一般經歷了從Model1到Model2,然後到後來的三層模型,最後到現在的Spring Boot。如果從Model1到Model2說起到我們現在使用的Spring Boot為整個時間軸的話,大致可以分為4個階段:

(1)初級階段:使用Model1/Model2/三層模模型進行開發;

(2)中級階段:使用EJB進行分散式應用開發,忍受重量級框架帶來的種種麻煩;

(3)高級階段:使用Spring春天帶給我們的美好,但是還要忍受很多繁瑣的配置;

(4)骨灰級階段:使用Spring Boot,暢享「預定大於配置」帶給我們的種種樂趣!

三、Web發展初級階段

1、Model1開發模式:

Model1的開發模式是:JSP+JavaBean的模式,它的核心是Jsp頁面,在這個頁面中,Jsp頁面負責整合頁面和JavaBean(業務邏輯),而且渲染頁面,它的基本流程如下:

相信很多小夥伴在剛學習Web的時候,肯定使用到了Model1開發模式,也就是我們的業務代碼、持久化代碼直接寫在Jsp頁面裡邊,使用Jsp直接處理Web瀏覽器的請求,並使用JavaBean處理業務邏輯。

利用我們現在熟悉的MVC模型的思想去看,雖然編寫代碼十分容易,但Jsp混淆了MVC模型中的視圖層和控制層,高度耦合的結果是Jsp代碼十分複雜,後期維護困難!

2、Model2開發模式:

Model1雖然在一定程度上解耦了,但JSP依舊即要負責頁面控制,又要負責邏輯處理,職責不單一!此時Model2應運而生,使得各個部分各司其職,Model2是基於MVC模式的。

Model2的開發模式是:Jsp+Servlet+JavaBean的模式,它和Model1不同的是,增加了Servlet,將調用頁面數據,調用業務邏輯等工作放到了Servlet中處理,從而減輕了Jsp的工作負擔!它的基本流程如下:

Model2開發模式將Servlet的概念引入架構體系中,使用它來分配視圖層Jsp的顯示頁面,同時調用模型層的JavaBean來控制業務邏輯。

3、Model1和Model2的區別:

Model1:簡單,適合小型項目的開發,但是Jsp的職責過於繁重,職責分工不明確。在後期的維護工作中,必將為此付出代價!

Model2:相對於Model1來說,職責分工更為明確,在Model1的基礎上,抽取了Servlet層,體現了一個分層的思想,適合大型的項目開發!(當時的評判標準是適合大型項目開發的,現在看起來已經過時了!)

Model2看起來已經盡善盡美了,儘管如此,他還不能稱之為一個比較完善的MVC設計模式!

4、Model1和Model2與三層的對比:

在Model2中,我們將Servlet抽取出單獨的一層,和Jsp協作完成用戶數據交互的工作,也就是表示層。那麼作為三層結構來說,又做了什麼樣的改進呢?三層則是在此基礎上,將JavaBean再一次進行分割:業務邏輯、數據持久化,三層如下:

(1)表示層,JSP/Servlet;

(2)業務邏輯層:業務規則;

(3)持久化層:主要包裝持久化的邏輯 ;

各個的耦合性如下圖:

Model1、Model2、三層是在解耦的基礎上一步步進化而來,通過解耦我們可以進行進一步的抽象,以應對現實需求的變動。

四、Web發展中級階段、高級階段和骨灰級階段

這一小節似乎有點應付,對於中級階段,因為我沒有用過EJB,在這裡不敢妄加評論,以免誤導大家。但是相信每一位接觸過Spring的小夥伴,都應該知道Rod Johnson在2002年編寫的《Expert One-to-One J2EE Design and Development》一書,Rod 在本書中對J2EE正統框架臃腫、低效、脫離現實的種種學院派做法提出了質疑,並以此書為指導思想,編寫了interface21框架,也就是後來的Spring。

對於高級階段和骨灰級階段是我們後期一系列文章的重點,本篇只作為一個階段劃分,不做過多的解釋,因此讓我們重新回到Web發展的初級階段。

對EJB有興趣的可以參考文章:uml.org.cn/j2ee/2009112

五、Web發展初級階段存在的問題

經歷過初級階段的小夥伴肯定看得懂下邊的一個項目結構,一個簡單的MVC三層結構,使用JSP+Servlet+MySQL+JDBC技術,面向介面編程:

1、面向介面編程的實例化對象

以用戶管理模塊為例,有一個UserDao介面,有一個介面的實現類UserDaoImpl,如下:

由於是面向介面編程,因此我們在每次使用UserDao的時候,都要進行實例化一次,實例化代碼如下:

UserDao userDao = new UserDaoImpl();

我們在每次使用UserDao的時候都需要進行實例化,當然不僅僅有UserDao需要進行實例化,還有很多需要進行實例化的,舉例如下:

可以看出,每一個方法中都需要進行實例化我們需要用到的介面的實現類,這就會存在大量的實例化對象,並且他們的生命周期可能就是從方法的調用開始到方法的調用結束為止,加大了GC回收的壓力!

2、使用單例模式的一次改進

了解設計模式的可能會想到使用單例模式的方式來解決這個問題,以此來避免大量重複的創建對象,但是我們還要考慮到眾多的這種對象的創建都需要改成單例模式的話,是一個耗時耗力的操作。

對於這個系統來說,如果都把這種面向介面的對象實現類轉換為單例模式的方式的話,大概也要寫十幾個或者上百個這種單例模式代碼,而對於一個單例模式的寫法來說,往往是模板式的代碼,以靜態內部類的方式實現單例模式如下:

可以看出,這種方式有兩個問題:

(1)業務代碼與單例模式的模板代碼放在一個類里,耦合性較高;

(2)大量重複的單例模式的模板代碼;

從上述可以看出,使用的單例模式雖然從性能上有所提高,但是卻加重了我們的開發成本。因此只會小規模的使用,例如我們操作JDBC的Utils對象等。

3、我們開發中遇到的痛點

從上述代碼的演進過程我們可以看得出來,我們即需要一個單例的對象來避免系統中大量重複對象的創建和銷毀,又不想因為使用單例模式造成大量重複無用的模板代碼和代碼的耦合!

(突然想到一個段子,想和大家分享一下:產品經理在給甲方彙報方案的時候說了兩種方案:一種是實用的,一種是美觀的,問甲方希望選擇哪一種?甲方說:有沒有即實用又美觀的!)

4、我們還能怎麼做

作為學院派的書生來說,我們可能會聯想到「資料庫連接池」,我們在獲取資料庫連接的時候會從這個池子中拿到一個連接的,假設這個資料庫連接池很特殊,有且只能有N個資料庫連接,並且每一個連接對象都不同(假設),那麼這個不就相當於每一個連接都是單例的了嗎?既可以避免大量對象的創建,也可以實現不會出現大量重複性的模板代碼。

因此,這裡應該有一個大膽的想法,我們是否可以建立一個池子,將我們的介面實現類對象放入到這個池子中,我們在使用的時候直接從這個池子裡邊取就行了!

5、這個池子

如果我們要創建這個池子,首先要確定需要把哪些對象放進這個池子,通過怎樣的方式放進去,放進去之後如何進行管理,如何進行獲取,池子中的每一個對象的生命周期是怎麼樣的等等這些東西都是我們需要考慮到的!

6、恭喜你

如果你已經了解了上述Web演進的過程,以及我們想要創建的這個池子,那麼恭喜你!你已經打開了Spring核心原理的大門了!

上述我們想要創建的池子其實就是Spring容器的雛形,將介面實現類的對象放進池子進行管理的過程其實也是Spring IOC依賴注入、控制反轉的雛形!

Spring的依賴注入/控制反轉就是從我們的配置文件或註解中的得到我們需要進行注入到Spring容器的實現類的信息,Spring IOC通過這些配置信息創建一個個單例的對象並放入Spring容器中,Spring容器可以看做是一個集合保存著我們的這些對象。

7、小總結

上文中主要從一個切入點探討了一下為什麼有Spring,以及介紹了一下Spring IOC和Spring容器的基本雛形概念,當然還可以從其他方面進行切入。這裡沒有進一步探討AOP的概念,對於新入門的小夥伴來說,這個確實有必要討論一下,也決定在後續文章中由淺入深的探討一下,而對於老手來說,其實我上邊寫的基本上是浪費大家時間的!

六、總結

從歷史的角度來說,不同時期的大革命在爆發之前,都會有一個蓄謀已久的「導火線」!Spring的出現,同樣順應了歷史發展潮流,正是由於那個時期J2EE開發標準的種種弊端造就了Spring的出現!即使不是Spring,同樣也會有其他類似的產品出現,只不過歷史選擇了Spring,Spring順應了歷史!沒有切膚之痛,是不會體會到Spring帶給我們的樂趣與快感!

同樣的,每個時代都會有每一個時代的問題,Spring也是!正如十年前我們的計算機可能帶不動一款遊戲,今天我們的計算機也有可能帶不動一款如今的遊戲,同樣十年後的計算機也會有一款他帶不動的遊戲出現!以一種發展的眼光去看Spring,就可以很好的理解Spring Boot是以一種什麼樣的角色出現在我們的面前了!

時代選擇了Spring,同樣Spring也被這個時代所選擇著!你我只有不停的進步,不停地學習才能跟上這個時代!

推薦閱讀:

Spring(1):概念及ioc操作
為Spring Cloud Ribbon配置請求重試(Camden.SR2+)
誰看過李守宏的 spring mvc教學視頻?不覺得想死嗎?
史上最簡單的SpringCloud教程 | 第三篇: 服務消費者(Feign)
使用Spring特性實現介面多實現類的動態調用

TAG:Spring |