一. 為什么要用SpringMVC
上次我們用Gradle創建了一個基于servlet的表單提交應用,今天我們使用SpringMVC框架來完成同樣的工作。
之前不是挺好的,我們為什么非要用一個框架啊?我們來看看上次應用中數據庫處理部分的代碼:
看了之后你肯定會想,存數據不就只是一個insert嗎,怎么搞出這么一堆東西出來?好吧,做準備工作,那后面那一堆try,catch是個啥,這個多異常處理,看著好煩啊,能不能自己寫一個處理類,把這些工作做完。
好,我們自己創建了一個DBprocess類,在這里我們只需創建一個DB的類,用完之后銷毀就行了。這個不錯啊,這就是oop的基本準則。但是要是有第三個類要調用我們現在這個類呢?要是我們的類多了,相互調用,相互創建,是不是很亂,反正我以前是被這種事搞崩潰過,但凡中間有一個類沒有創建或是資源因為類沒析構掉而一直被占用,然后就是可怕的調試,簡直就是噩夢。
這時候我們想,要是有這么一種東西,它知道工程中類和類之間的關系,在每個類有需求的時候自動的滿足,把該給的東西給它,用完之后再拿回來,而我們只要關注每個類內部是怎么工作,那就太好了!沒錯,這個就是框架!
這里有一個很專業的名詞叫做:控制反轉,或是依賴注入。
其實我們生活中早就有這樣的概念了,比如我們的電腦和外接硬盤,U盤之間的關系。我們本身就是中間那個框架,當我們需要拷貝文件到其他地方,電腦就需要一個U盤來存放需要向外輸出的數據,但是電腦不會親自來做,它只是告訴你我需要,然后你就把外部存儲設備接到USB插口上,其實電腦也不關心你用的是U盤還是硬盤,只要你的存儲設備遵循一定的規定,比如是USB標準等,電腦就向外輸出,等到文件拷貝進度條走完,電腦告訴你我用完了,你就把U盤拔出來,用在到其他電腦上。整個過程中,我們就擔任了框架的作用,控制權從電腦反轉到了框架,而電腦依賴的U盤是我們幫助插入USB接口的,所以叫"依賴注入"。
二. 這個應用發生了什么變化
現在你可以暢想一下你新的數據庫類的工作方式了,沒錯,你告訴框架什么時候要數據庫操作,并把操作定義好(比如一個模板),需要的數據庫操作的時候,框架會自動喂給你一個定義好的數據庫模板,你昨晚該做的操作之后框架再回收這個模板(類)。好,現在我們就來看看相對于上次的servlet,用SpringMVC之后,我們的表單提交應用從邏輯到代碼細節有什么變化。
1. 邏輯上變了什么
對了,為什么叫MVC呢?對應為Model,View, Controller,什么意思呢,相信看完這個例子你自己就有體會了。
我們自上而下,先從邏輯層面開始,再來看代碼是怎么根據邏輯一步步跑起來的。
如圖2所示,和以前一樣,用戶請求一個URL,URL被已經在web.xml文件中注冊的servlet截獲,不同的是,這次是SpringMVC中的特定的servlet接獲,然后查詢目標控制器看看是誰來管這個請求,接著根據控制器的要求,參數被封裝成相應的命令對象傳給目標控制器,控制器在昨晚自己的工作之后,通過調用spring 容器把自己的成果用視圖的形式反饋個用戶,這樣就完成了一次請求操作。
2. 讓我們來扣扣代碼細節吧
怎么樣,邏輯很簡單吧,我們先來看看web.xml文件:
我們發現servlet-class發生的變化,對應的url-pattern也發生了變化。因為SpringMVC提供了一個DispatcherServlet的servlet類,并且這個類可以處理所有的請求,其實是用它截獲請求。
上一篇我們講到web.xml文件是注冊servlet用的,他是一個list,告訴servlet容器在注冊的servlet有多少,叫什么,干什么用,怎么用。上面又說到框架要給我們服務,需要知道我們類之間的關系,以及要注入什么樣的"依賴",這樣的"說明文件"都是由xml文件完成的,spring也會預先掃描xml文件,得到你工程的信息之后才能工作的。所以我們再來看一個springmvc_form-servlet.xml文件。
開始的一行告訴spring容器要掃描的基礎包名是什么,也就是管轄范圍。之后有一個bean注冊,這個bean是spring提供一個class,用來處理視圖的,一起就是說我在需要視圖的時候,這個class就會起作用,具體是怎么起作用的,大概就是給一個參數,spring就會把接下來的工作交給一個視圖來完成,也就是注入一個視圖。那么后面的兩個參數是什么,一個是前綴,一個是后綴,后面會詳細的解釋,這里可以猜到肯定是和參數拼接之類的。
然后我們再看看Beans.xml文件,這個文件注冊了帶注入的bean的詳細信息
這里注冊了兩個bean,都有id和對應的class,還有一些參數值,上面一個是數據庫連接的,可以看到里面有連接數據庫的一些基本參數。下面的是一個我們自己定義的類的bean,我們來看下這個類:
這個類其實就是增加用戶的數據庫操作類,那么圖4第二個bean的意思就是把這個類變成了一個bean,再適當的時候注入到需要他的地方,而圖4第一個bean被第一個引用,兩個一起合作完成注入工作。
我們自己定義了一個student類,里面有一些待填寫的屬性。
接著我們到最應用最開始的地方,注冊頁面,也就是student.jsp
這里截取了最重要的一部分,之后都是些表單的內容。上面一行也有解釋,與上一篇文章相似,這里也有一個action,只是這個action要提交給控制器。我們來看看控制器是怎么獲取到數據的,也就是參數是怎么按照控制器的要求打包的。
在springMVC中很重要的一個概念就是注解類,RequestMapping就是一個注解類,在這個類之前還有一個Controller注解,如圖8所示,表示這個類是一個控制器,控制器中的方法是具體的控制器實現。
RequestMapping注解填寫了兩個參數:value表示請求的路徑,method表示這個控制方法處理的請求類型。一旦jsp頁面請求了value中的值,請求的method值能匹配,那么請求就由這個方法來完成。而數據是怎么打包的呢,有什么數據進行了傳遞,是怎么傳遞的呢?圖7中有詳細的解釋:
//ModelMap是一個和模板文件進行溝通的參數,可以理解成是一個封裝好的數據包
ModelMap是通信的媒介,它把數據封裝好,從控制器傳給視圖。
//輸入:轉換成command對象(command對象是從視圖傳過來的)的student對象,帶著被spring打包的網頁數據
可以看到jsp頁面用了form:form標簽,這個標簽會自動綁定一個command屬性,這是一個student對象,student.jsp頁面把數據封裝成student類然后發送過來,這里其實就是注入了一個student對象,也就是spring中的構造器注入,你不用自行創建,只需按照原來類的使用方法使用就行,如這里的get方法得到私有成員的值。
而返回的值是什么呢?是一個字符串?what?還記得之前的那個視圖bean嗎,它有前綴和后綴,對了,這里是返回的字符串和前綴后綴拼接后,就組成了一個帶路徑的jsp頁面,也就是反悔了視圖,而視圖只是模板啊,數據呢,注意看我們的addStudent類還有一個參數,我之前說了,這個參數是一個對象,你向其中加入一些鍵值對,如:
model.addAttribute("username",student.getUsername());
那么在返回的視圖中就可以通過鍵的到響應的值了。
//輸出,將這屬性值處理之后加入到模型和視圖之間傳遞ModelMap,讓它返回給視圖,這里的視圖就是最后返回的reslut.jsp,前綴和后綴都已經配置好
我們來看看返回的視圖result.jsp
在視圖中我們用${model鍵},就能的到controller處理后的值了。
最后我我們來看看數據庫是怎么注入的
你看,我們在這里先讀取必要的Beans.xml的配置文件,然后獲取bean,這個bean就被注入到這個控制器類里了,你調用類中的創建方法,完成了創建空戶的操作,其他就什么都不用管了,springMVC框架都會幫你完成的。
怎么樣?SpringMVC是不是很方便,很科學呢!
最后的結果如圖:
提交界面:
反饋界面:
數據庫查詢:
本文的詳細代碼可以在我的github上獲取:
https://github.com/intwzt/TW_CODE/tree/master/12-9