應(yīng)用敏捷與安全如何兼存?
827
2025-04-01
概述
前面兩篇介紹了模型2架構(gòu)的優(yōu)勢(shì)以及如何構(gòu)建一個(gè)模型2應(yīng)用。但是Spring MVC框架可以幫助我們快速的開發(fā)MVC應(yīng)用。
Spring MVC體系概述
若基于某個(gè)框架來開發(fā)一個(gè)模型2的應(yīng)用程序,我們要負(fù)責(zé)編寫一個(gè)Dispatcher servlet和控制類。 其中Dispatcher servlet必須能夠做到如下事情:
根據(jù)URI調(diào)用對(duì)應(yīng)的action
實(shí)例化正確的控制器類
根據(jù)請(qǐng)求參數(shù)來構(gòu)造表單bean
調(diào)用控制器對(duì)象的相應(yīng)方法
轉(zhuǎn)向一個(gè)視圖
Spring MVC框架圍繞DispatcherServlet這個(gè)核心展開,DispatcherServlet負(fù)責(zé)截獲請(qǐng)求并分派給相應(yīng)的處理器處理。 SpringMVC框架包括注解驅(qū)動(dòng)控制器、請(qǐng)求及響應(yīng)的信息處理、視圖解析、本地化解析、上傳文件解析、異常處理及表單標(biāo)簽綁定等內(nèi)容。
由于SpringMVC是基于Model2實(shí)現(xiàn)的框架,所以它底層的機(jī)制也是MVC,我們來看下SpringMVC的整體架構(gòu)
從接收請(qǐng)求到返回相應(yīng),Spring MVC框架的眾多組件有條不紊的完成內(nèi)部的分工,在整個(gè)框架中,DispatcherServlet處于核心的位置,負(fù)責(zé)協(xié)調(diào)和組織不同組件以完成請(qǐng)求處理并返回響應(yīng)的工作。
下面我們來分步解析下SpringMVC處理請(qǐng)求的整體過程
整個(gè)過程始于客戶端發(fā)出的一個(gè)HTTP請(qǐng)求,Web應(yīng)用服務(wù)器收到這個(gè)請(qǐng)求后,如果匹配DispatcherServlet的請(qǐng)求映射路徑(web.xml中指定),則web容器將該請(qǐng)求交給DispatcherServlet處理
DispatcherServlet接收到這請(qǐng)求后,將根據(jù)請(qǐng)求的信息(包括url,HTTP方法、請(qǐng)求報(bào)文頭、請(qǐng)求參數(shù)、Cookie等)及HandlerMapping的配置找到處理請(qǐng)求的處理器(Handler)。 可將HandlerMapping看做是路由控制器,將Handler看做目標(biāo)主機(jī)。值得注意的是,SpringMVC中并沒有定義一個(gè)Handler接口,實(shí)際上任何一個(gè)Object都可以稱為請(qǐng)求處理器。
當(dāng)DispatcherServlet根據(jù)HandlerMapping得到對(duì)應(yīng)請(qǐng)求的Handler后,通過HandlerAdpapter對(duì)Handler進(jìn)行封裝再以統(tǒng)一的適配器接口調(diào)用Handler.
處理器完成業(yè)務(wù)邏輯的處理后將返回一個(gè)ModelAndView給DispatcherServlet,ModelAndView包含了視圖邏輯名和模型數(shù)據(jù)信息
ModelAndView并非真正的視圖對(duì)象,DispatcherServlet通過ViewResolver完成邏輯視圖和真實(shí)視圖對(duì)象的解析工作
當(dāng)?shù)玫秸鎸?shí)的視圖對(duì)象View后,DispatcherServlet就使用這個(gè)View對(duì)ModelAndView中的模型數(shù)據(jù)進(jìn)行視圖渲染
最終客戶端得到相應(yīng)消息可能是一個(gè)普通的html頁面,也可能是要給XML或者JSON串,甚至是一張圖片或者PDF文檔等不同的媒體形式。
Spring MVC的DispatcherServlet
我們?cè)谇懊鎯善┪牡睦又校瑂ervlet需要我們自己編寫,基于Spring MVC ,則無需如此。
SpringMVC中自帶了一個(gè)開箱即用的DispatcherServlet,全限定名為org.springframework.web.servlet.DispatcherServlet
使用DispatcherServlet
要想使用這個(gè)servlet,同樣的也需要把它配置在部署描述符(web.xml)、應(yīng)用servlet和servlet-mapping。如下所示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
servlet元素內(nèi)的on-startup是可選項(xiàng),
如果它存在,則它將在應(yīng)用程序啟動(dòng)時(shí)裝載servlet并調(diào)用他的init方法,若不存在,則該servlet的第一個(gè)請(qǐng)求時(shí)加載
。
DispatcherServlet將使用Spring MVC諸多默認(rèn)的組件,此外,
初始化的時(shí)候,它會(huì)尋找一個(gè)在應(yīng)用程序的WEB-INF目錄下的配置文件,該配置文件的命名規(guī)則 servletName-servlet.xml
。 其中servletName是在部署描述中的DispatcherServlet的名稱,比如我們上述的配置文件
此外,也可以把SpringMVC的配置文件放在應(yīng)用程序目錄中的任何地方,用servlet定義的init-param元素,以便DispatcherServlet加載到該文件。 init-param元素?fù)碛幸粋€(gè)contextConfigLocation的param-name元素,其param-value元素則包含配置文件的路徑。
如下所示
1
2
3
4
5
6
7
8
9
10
11
Controller接口
Spring2.5之前
,開發(fā)一個(gè)控制器的唯一方法是實(shí)現(xiàn)org.springframework.web.servlet.mvc.Controller接口,該接口中有一個(gè)方法
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
1
其實(shí)現(xiàn)類可以訪問對(duì)應(yīng)請(qǐng)求的HttpServletRequest 和HttpServletResponse ,同時(shí)還必須返回包含視圖路徑或者視圖路徑和模的ModelAndView 對(duì)象。
Controller接口實(shí)現(xiàn)類只能處理一個(gè)單一動(dòng)作,而一個(gè)基于注解的控制器可以同時(shí)支持多個(gè)請(qǐng)求處理動(dòng)作,并且無需實(shí)現(xiàn)任何接口(后續(xù)介紹這種主流的開發(fā)方式,這里先演示下實(shí)現(xiàn)接口的方式)
簡單示例(實(shí)現(xiàn)接口的方式)
在這里我們只是簡單的演示一下這種方式的用法,實(shí)際開發(fā)中并不推薦這樣做。基于注解的方式后續(xù)介紹。
maven工程結(jié)構(gòu)如下:
pom.xml添加依賴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
部署描述文件和Spring MVC 的配置文件
部署描述文件web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
這里告訴Servlet/JSP容器,我們將使用SpringMVC的DispatcherServlet,并通過配置url-pattern原始來匹配.action結(jié)尾的URL映射到該servlet。 并通過init-param元素,加載特定目錄下的Spring MVC 配置文件人,如果不配置的話,則SpringMVC的配置文件將在默認(rèn)的/WEB-INF目錄下,并且按照約定的命名規(guī)范。
Spring MVC配置文件
1
2
3
4
5
6
7
8
9
10
這里聲明兩個(gè)控制器類InputProductController和SaveProductController,并分別映射到/product_input.action和/product_save.action。
Controller
下面來編寫“傳統(tǒng)”風(fēng)格的控制器,分別實(shí)現(xiàn)Spring提供的Controller接口
package com.artisan.springmvc.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; /** * * @author Mr.Yang * @Desc 實(shí)現(xiàn)Spring自身的Controller * */ public class InputProductController implements Controller{ private static final Logger logger = Logger.getLogger(InputProductController.class); @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { logger.info("InputProductController called"); return new ModelAndView("/WEB-INF/jsp/ProductForm.jsp"); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
InputProductController 類的handleRequest方法只返回一個(gè)ModelAndView對(duì)象,包含一個(gè)視圖,并沒有模型。因此,該請(qǐng)求將被轉(zhuǎn)發(fā)到/WEB-INF/jsp/ProductForm.jsp頁面
package com.artisan.springmvc.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; import com.artisan.springmvc.form.ProductForm; import com.artisan.springmvc.model.Product; /** * * @author Mr.Yang * @Desc 實(shí)現(xiàn)Spring自身的Controller * */ public class SaveProductController implements Controller{ private static final Logger logger = Logger.getLogger(SaveProductController.class); @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { logger.info("SaveProductController called"); ProductForm productForm = new ProductForm(); // populate action properties productForm.setName(request.getParameter("name")); productForm.setDescription(request.getParameter("description")); productForm.setPrice(request.getParameter("price")); // create model Product product = new Product(); product.setName(productForm.getName()); product.setDescription(productForm.getDescription()); try { product.setPrice(Float.parseFloat(productForm.getPrice())); } catch (NumberFormatException e) { } // insert code to save Product return new ModelAndView("/WEB-INF/jsp/ProductDetails.jsp", "product", product); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
SaveProductController類的handleRequest方法中,首先用請(qǐng)求創(chuàng)建了一個(gè)ProductForm對(duì)象,然后它根據(jù)ProductForm對(duì)象創(chuàng)建Product對(duì)象 。 由于ProductForm的price是一個(gè)字符串,而在Product類中是一個(gè)float類型,因此需要轉(zhuǎn)型(
后面會(huì)介紹通過數(shù)據(jù)綁定省去ProductForm,這里暫不講解)
SaveProductController的handleRequest方法返回最后的ModelAndView模型包含了視圖的路徑、模型名稱和模型(Product對(duì)象),該模型將提供給目標(biāo)視圖,用于界面顯示。
視圖
兩個(gè)JSP,綁定了CSS樣式。
我們?cè)趙eb.xml配置url-pattern來匹配.action ,沒有配置 / (所有請(qǐng)求)是因?yàn)槿绻渲昧?,而沒有配置靜態(tài)資源過濾,這個(gè)CSS也會(huì)被攔截,因此這里暫時(shí)配置了攔截所有action結(jié)尾的請(qǐng)求。
ProductForm.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
ProductDetails.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ProductDetail.jsp頁面通過模型屬性名“product”來訪問由SaveProductController傳入的Product對(duì)象。這里用JSP表達(dá)式來顯示Product對(duì)象的各種屬性,后續(xù)會(huì)詳解JSP 的EL表達(dá)式。
測試應(yīng)用
輸入U(xiǎn)RL:
http://localhost:8080/chapter03a/product_input.action
輸入相應(yīng)的數(shù)據(jù)
提交數(shù)據(jù)后,url跳轉(zhuǎn)到
http://localhost:8080/chapter03a/product_save.action
View Resolver
上個(gè)案例中,頁面的跳轉(zhuǎn)通過指定頁面的路徑來完成的,比如
new ModelAndView("/WEB-INF/jsp/ProductDetails.jsp", "product", product);
1
2
其實(shí),Spring MVC為我們提供了視圖解析器,負(fù)責(zé)解析視圖,現(xiàn)在我們來改造下。
在SpringMVC的配置文件中配置視圖解析器
1
2
3
4
如上視圖解析器前綴和后綴兩個(gè)屬性,這樣一來,view的路徑就縮短了。 比如僅需要ProductDetails,而無需設(shè)置/WEB-INF/jsp/ProductDetails.jsp,視圖解析器就會(huì)自動(dòng)增加前綴和后綴。
緊接著我們調(diào)整下控制器中的頁面跳轉(zhuǎn)
InputProductController 修改為
return new ModelAndView("ProductForm");
1
SaveProductController修改為
return new ModelAndView("ProductDetails", "product", product);
1
2
重新運(yùn)行測試,結(jié)果同上。
總結(jié)
本博文是Spring MVC的入門介紹,我們知道了使用SpringMVC,我們無需編寫自己的DispatcherServlet,其傳統(tǒng)風(fēng)格的控制器開發(fā)方式是實(shí)現(xiàn)控制器接口。 從Spring2.5版本開始,Spring提供了基于注解的方式開發(fā)控制器,下篇博文介紹。
源碼
代碼已提交到github
https://github.com/yangshangwei/SpringMvcTutorialArtisan
MVC Servlet Spring
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。