1. 程式人生 > >Spring——通過Web開發演進過程瞭解一下為什麼要有Spring?

Spring——通過Web開發演進過程瞭解一下為什麼要有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發展的初級階段。

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

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

這裡寫圖片描述

1、面向介面程式設計的例項化物件

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

這裡寫圖片描述

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

UserDao userDao = new UserDaoImpl();
  • 1

我們在每次使用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也被這個時代所選擇著!你我只有不停的進步,不停地學習才能跟上這個時代!