一.Spring MVC 簡介
大部分Java應用都是Web應用,展現層是WEB應用不可忽略的重要環節.Spring爲了展現層提供了一個優秀的WEB框架-Spring MVC . 和衆多的其他WEB框架一樣,它基於MVC的設計理念. 此外,它採用了鬆散耦合,可插拔的組件結構,比其他的MVC框架更具有擴展性和靈活性,Spring MVC通過一套MVC註解,讓POJO成爲成爲處理請求的處理器,無須實現任何接口.同時,Spring MVC還支持REST風格的URL請求:註解驅動及REST風格的Spring MVC是Spring的出色功能之一.
此外,Spring MVC在數據綁定,視圖解析,本地化處理及靜態資源處理上都有許多不俗的表現,它在框架設計,可擴展,靈活性等方面全面超越了Struts,WebWork等MVC框架,從原來的追趕者一躍成爲了MVC的領跑者.
1.1 MVC模式簡介
MVC全名是Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫,一種軟件設計典範,用一種業務邏輯、數據、界面顯示分離的方法組織代碼,將業務邏輯聚集到一個部件裏面,在改進和個性化定製界面及用戶交互的同時,不需要重新編寫業務邏輯。MVC被獨特的發展起來用於映射傳統的輸入、處理和輸出功能在一個邏輯的圖形化用戶界面的結構中。
MVC 是一種使用 MVC(Model View Controller 模型-視圖-控制器)設計創建 Web 應用程序的模式:
- Model(模型)表示應用程序核心(比如數據庫記錄列表)。
- View(視圖)顯示數據(數據庫記錄)。
- Controller(控制器)處理輸入(寫入數據庫記錄)。
MVC 模式同時提供了對 HTML、CSS 和 JavaScript 的完全控制。
**Model(模型)**是應用程序中用於處理應用程序數據邏輯的部分。 通常模型對象負責在數據庫中存取數據。
**View(視圖)**是應用程序中處理數據顯示的部分。 通常視圖是依據模型數據創建的。
**Controller(控制器)**是應用程序中處理用戶交互的部分。 通常控制器負責從視圖讀取數據,控制用戶輸入,並向模型發送數據。
MVC 分層有助於管理複雜的應用程序,因爲您可以在一個時間內專門關注一個方面。例如,您可以在不依賴業務邏輯的情況下專注於視圖設計。同時也讓應用程序的測試更加容易。
MVC 分層同時也簡化了分組開發。不同的開發人員可同時開發視圖、控制器邏輯和業務邏輯。
1.2 Spring MVC 體系結構
Spring MVC是基於 Model 2實現的技術框架,Model 2是經典的MVC(model,view,control)模型在WEB應用中的變體.這個改變主要源於HTTP協議的無狀態性,Model 2 的目的和MVC一樣,也是利用處理器分離模型,視圖和控制,達到不同技術層級間鬆散層耦合的效果,提高系統靈活性,複用性和可維護性.大多情況下,可以將Model 2 與 MVC等同起來.
Spring MVC體系概述
Spring MVC框架圍繞DispatcherServlet這個核心展開,DispatcherServlet是Spring MVC的總導演,總策劃.它負責截獲請求並將其分派給相應的處理器處理.Spring MVC框架包括註解驅動控制器,請求及響應的信息處理,視圖解析,本地化解析,上傳文件解析,異常處理及表單標籤綁定內容等...
Spring核心組件
組件介紹
DispatcherServlet:作爲前端控制器,整個流程控制的中心,控制其它組件執行,統一調度,降低組件之間的耦合性,提高每個組件的擴展性。
HandlerMapping:通過擴展處理器映射器實現不同的映射方式,例如:配置文件方式,實現接口方式,註解方式等。
HandlAdapter:通過擴展處理器適配器,支持更多類型的處理器,調用處理器傳遞參數等工作!
ViewResolver:通過擴展視圖解析器,支持更多類型的視圖解析,例如:jsp、freemarker、pdf、excel等。
1.3 Spring MVC執行流程
從接收請求到響應,Spring MVC框架的衆多組件通力配合,各司其職,有條不紊的完成分內工作!在整個框架中,DispatchserServlet處於核心的位置,它負責協調和組織不同組件以完成請求處理以及返回影響工作.和大多數Web MVC框架一樣,Spring MVC 通過一個前段的Servlet接收所有請求,並將這些工作委託給其他組件進行處理,DispatcherServlet 就是Spring MVC的前段Servlet。下面對Spring MVC處理請求的整體過程進行詳解!
- 整個過程始於客戶端發出的一個HTTP請求,WEB應用服務器接收到這個請求,如果匹配DispatcherServlet的映請求映射路徑(web.xml中指定),則Web容器將該請求轉交給DispatcherServlet處理。
- 接收到這個請求後,將根據請求的信息(包括 URL,HTTP方法,請求頭,請求參數,Cookie等)及HandlerMapping的配置找到處理請求的處理器(Handler)。可將HandlerMapping看做路由控制器,將Handler看做目標主機.值得注意的是,在Spring MVC中並沒有定義一個Handler接口,實際上,任何一個Object都可以成爲請求處理器。
- 當DispatcherServlet根據HandlerMapping得到對應當前請求的Handler後,通過HandlerAdapter對Handler的封裝,再以統一的適配器接口調用Handler。HandlerAdapter是Spring MVC的框架級接口,顧名思義,HandlerAdapter是一個適配器,它用統一的接口對各種Handler的方法進行調用.
- 處理器完成業務邏輯的處理後將返回一個ModelAndView給DispatcherServlet,ModelAndView包含了視圖邏輯名和模型數據信息。
- ModelAndView中包含的是"邏輯視圖名"而並非真正的視圖對象,DispatcherServlet藉由ViewResolver完成邏輯視圖名到真實視圖對象的解析工作。
- 當得到真實的視圖對象View後,DispatcherServlet就使用這個View對象對ModelAndView中的模型數據進行視圖渲染。
- 最終客戶端得到的響應信息可能是一個普通的HTML頁面,也可能是一個XML或者JSON串,甚至是一張圖片或者一個PDF文檔等不同的媒體形式。
1.4 組件開發實現情況
1、前端控制器DispatcherServlet(不需要工程師開發),由框架提供 作用:接收請求,響應結果,相當於轉發器,中央處理器。有了dispatcherServlet減少了其它組件之間的耦合度。 用戶請求到達前端控制器,它就相當於mvc模式中的c,dispatcherServlet是整個流程控制的中心,由它調用其它組件處理用戶的請求,dispatcherServlet的存在降低了組件之間的耦合性。
2、處理器映射器HandlerMapping(不需要工程師開發),由框架提供 作用:根據請求的url查找Handler HandlerMapping負責根據用戶請求找到Handler即處理器,springmvc提供了不同的映射器實現不同的映射方式,例如:配置文件方式,實現接口方式,註解方式等。
3、處理器適配器HandlerAdapter 作用:按照特定規則(HandlerAdapter要求的規則)去執行Handler 通過HandlerAdapter對處理器進行執行,這是適配器模式的應用,通過擴展適配器可以對更多類型的處理器進行執行。
4、處理器Handler(需要工程師開發) 注意:編寫Handler時按照HandlerAdapter的要求去做,這樣適配器纔可以去正確執行Handler Handler 是繼DispatcherServlet前端控制器的後端控制器,在DispatcherServlet的控制下Handler對具體的用戶請求進行處理。 由於Handler涉及到具體的用戶業務請求,所以一般情況需要工程師根據業務需求開發Handler。
5、視圖解析器View resolver(不需要工程師開發),由框架提供 作用:進行視圖解析,根據邏輯視圖名解析成真正的視圖(view) ViewResolver負責將處理結果生成View視圖,View Resolver首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址,再生成View視圖對象,最後對View進行渲染將處理結果通過頁面展示給用戶。 springmvc框架提供了很多的View視圖類型,包括:jstlView、freemarkerView、pdfView等。 一般情況下需要通過頁面標籤或頁面模版技術將模型數據通過頁面展示給用戶,需要由工程師根據業務需求開發具體的頁面。
6、視圖View(需要工程師開發jsp...) View是一個接口,實現類支持不同的View類型(jsp、freemarker、pdf...)
1.5 核心分發器DispatcherServlet
1.5.1 DispatcherServlet
DispatcherServlet是Spring MVC的"靈魂"和"心臟",它負責接受HTTP請求並協調 Spring MVC的各個組件完成請求處理的工作。和任何Servlet一樣,用戶必須在web.xml中配置好DispatcherServlet。
1.5.2 DispatcherServlet介紹
DispatcherServlet是前端控制器設計模式的實現,提供spring Web MVC的集中訪問點,而且負責職責的分派,而且與Spring IoC容器無縫集成,從而可以獲得Spring的所有好處。
1.5.3 DispatcherServlet主要職責
DispatcherServlet主要用作職責調度工作,本身主要用於控制流程,主要職責如下:
- 文件上傳解析,如果請求類型是multipart將通過MultipartResolver進行文件上傳解析;
- 通過HandlerMapping,將請求映射到處理器(返回一個HandlerExecutionChain,它包括一個處理器、多個HandlerInterceptor攔截器);
- 通過HandlerAdapter支持多種類型的處理器(HandlerExecutionChain中的處理器);
- 通過ViewResolver解析邏輯視圖名到具體視圖實現;
- 本地化解析;
- 渲染具體的視圖等;
- 如果執行過程中遇到異常將交給HandlerExceptionResolver來解析。
1.5.4 DispatcherServlet核心代碼
//前端控制器分派方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;
try {
ModelAndView mv;
boolean errorView = false;
try {
//檢查是否是請求是否是multipart(如文件上傳),如果是將通過MultipartResolver解析
processedRequest = checkMultipart(request);
//步驟2、請求到處理器(頁面控制器)的映射,通過HandlerMapping進行映射
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
//步驟3、處理器適配,即將我們的處理器包裝成相應的適配器(從而支持多種類型的處理器)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 304 Not Modified緩存支持
//此處省略具體代碼
// 執行處理器相關的攔截器的預處理(HandlerInterceptor.preHandle)
//此處省略具體代碼
// 步驟4、由適配器執行處理器(調用處理器相應功能處理方法)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Do we need view name translation?
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
// 執行處理器相關的攔截器的後處理(HandlerInterceptor.postHandle)
//此處省略具體代碼
}
catch (ModelAndViewDefiningException ex) {
logger.debug("ModelAndViewDefiningException encountered", ex);
mv = ex.getModelAndView();
}
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(processedRequest, response, handler, ex);
errorView = (mv != null);
}
//步驟5 步驟6、解析視圖並進行視圖的渲染
//步驟5 由ViewResolver解析View(viewResolver.resolveViewName(viewName, locale))
//步驟6 視圖在渲染時會把Model傳入(view.render(mv.getModelInternal(), request, response);)
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
// 執行處理器相關的攔截器的完成後處理(HandlerInterceptor.afterCompletion)
//此處省略具體代碼
catch (Exception ex) {
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
catch (Error err) {
ServletException ex = new NestedServletException("Handler processing failed", err);
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
finally {
if (processedRequest != request) {
cleanupMultipart(processedRequest);
}
}
}
1.5.5 DispatcherServlet輔助類
spring中的DispatcherServlet使用一些特殊的bean來處理request請求和渲染合適的視圖。這些bean就是Spring MVC中的一部分。你能夠通過在WebApplicationContext中的一個或多個配置來使用這些特殊的bean。但是,你不需要在Spring MVC在維護這些默認要使用的bean時,去把那些沒有配置過的bean都去初始化一道。在下一部分中,首先讓我們看看在DispatcherServlet依賴的那些特殊bean類型
bean類型 | 說明 |
---|---|
Controller | 處理器/頁面控制器,做的是MVC中的C的事情,但控制邏輯轉移到前端控制器了,用於對請求進行處理 |
HandlerMapping | 請求到處理器的映射,如果映射成功返回一個HandlerExecutionChain對象(包含一個Handler處理器(頁面控制器)對象、多個HandlerInterceptor攔截器)對象;如BeanNameUrlHandlerMapping將URL與Bean名字映射,映射成功的Bean就是此處的處理器 |
HandlerAdapter | HandlerAdapter將會把處理器包裝爲適配器,從而支持多種類型的處理器,即適配器設計模式的應用,從而很容易支持很多類型的處理器;如SimpleControllerHandlerAdapter將對實現了Controller接口的Bean進行適配,並且掉處理器的handleRequest方法進行功能處理 |
HandlerExceptionResolver處理器異常解析器 | 處理器異常解析,可以將異常映射到相應的統一錯誤界面,從而顯示用戶友好的界面(而不是給用戶看到具體的錯誤信息) |
ViewResolver視圖解析器 | ViewResolver將把邏輯視圖名解析爲具體的View,通過這種策略模式,很容易更換其他視圖技術;如InternalResourceViewResolver將邏輯視圖名映射爲jsp視圖 |
LocaleResolver & LocaleContextResolver地區解析器和地區Context解析器 | 解析客戶端中使用的地區和時區,用來提供不同的國際化的view視圖。 |
ThemeResolver | 主題解析器,解析web應用中能夠使用的主題,比如提供個性化的網頁佈局。 |
MultipartResolver | 多部件解析器,主要處理multi-part(多部件)request請求,例如:在HTML表格中處理文件上傳。 |
FlashMapManager | FlashMap管理器儲存並檢索在"input"和"output"的FlashMap中可以在request請求間(通常是通過重定向)傳遞屬性的FlashMap, |
2.springMVC的使用
2.1 新建一個maven項目
2.2 導入jar包
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.18.Release</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.18.Release</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.18.Release</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.18.Release</version>
</dependency>
<!-- 配置日誌信息-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- 配置servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--配置jsp的依賴 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<!-- 配置jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- 導入SpringMvc 需要的jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
2.3 創建一個springmvc.xml文件並配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1. 配置 需要掃描的控制層在哪個包 -->
<context:component-scan base-package="com.test.controller" ></context:component-scan>
<!-- 2 配置 視圖解析器 中的 前綴和後綴 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 設置前綴 -->
<property name="prefix" value="/WEB-INF/" />
<!-- 設置後綴 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
2.4 配置web.xml文件
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 加入前端控制器 DispatcherServlet -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 設置 DispatcherServlet 的參數 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 1 啓動服務器的時候就加載 -->
<!-- 0 使用的時候再加載 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2.5 創建控制器類 和控制器中的方法
@Controller
@RequestMapping("/User")
public class UserController {
@RequestMapping("/reg")
public String reg(HttpServletRequest request)
{
System.out.println(request.getServerName());
System.out.println("註冊..");
String id=request.getParameter("id");
System.out.println(id);
return "reg"; // return "WEB-INF/reg.jsp"
}
@RequestMapping("/login")
public String login(String name)
{
System.out.println(name);
return "login";
}
}
2.6 創建jsp 訪問控制器中的方法
<a href="User/reg?id=1">註冊</a><br/>
<a href="User/login?name=aa">登錄</a><br/>
3.springmvc常用註解
1.@controller 註解是否是控制器類
2. @requestMapping 請求路徑的註解
在類和方法上都可以用
可以設置請求路徑 也可以設置請求方法
@RequestMapping("/User")
@RequestMapping(value = "/login5",method = RequestMethod.POST)
3.@RequestParam 用在請求參數上
如果傳遞的參數名和接收的參數名不一致 需要@RequestParam 註解
3.1 如果傳遞的參數名和接收的參數名一致
<a href="User/login2?name=aa&pwd=123">登錄2</a><br/>
@RequestMapping("/login2")
public String login2(String name,String pwd)
{
System.out.println(name);
System.out.println(pwd);
return "success";
}
3.1 如果傳遞的參數名和接收的參數名不一致
<a href="User/login4?uname=lisi&upwd=123">登錄4</a><br/>
@RequestMapping("/login4")
public String login4(@RequestParam(value="uname") String name, @RequestParam(value="upwd") String pwd)
{
System.out.println(name);
System.out.println(pwd);
return "success";
}
4.@PathVariable
在路徑使用rest風格的時候 需要用@PathVariable註解路徑上的參數
<a href="User/login3/zhangsan/123">登錄3</a><br/>
//rest風格
@RequestMapping("/login3/{aa}/{pwd}")
public String login3(@PathVariable(value="aa") String name,@PathVariable(value="pwd") String pwd)
{
System.out.println(name);
System.out.println(pwd);
return "success";
}
5.@RequestHeader
請求頭
<a href="User/testHeader2">testHeader2</a><br/>
@RequestMapping("/testHeader2")
public String testHeader2(@RequestHeader("host") String header)
{
System.out.print(header);
return "success";
}
6.@CookieValue
獲取cookie中的值
<a href="User/setCookie">存cookie</a><br/>
<a href="User/getCookie">取cookie</a>
@RequestMapping("/setCookie")
public String setCookie(HttpServletResponse response)
{
Cookie cookie=new Cookie("uname","zhangsan");
cookie.setMaxAge(3600);
cookie.setPath("/");
response.addCookie(cookie);
return "success";
}
@RequestMapping("/getCookie")
public String getCookie(@CookieValue("uname") String uname)
{
System.out.println(uname);
return "success";
}