SpringMVC從入門到放棄之第二章SpringMVC入門

額(⊙o⊙)…
繼續(xù)記筆記。。。看看
SpringMVC從入門到放棄之第一章Web MVC簡介

一個在實驗室的時候就是這樣

原文地址

2.1、Spring Web MVC是什么

Spring Web MVC是一種基于Java的實現(xiàn)了Web MVC設(shè)計模式的請求驅(qū)動類型的輕量級Web框架,即使用了MVC架構(gòu)模式的思想,將Web進行職責解耦,基于請求驅(qū)動指的就是使用請求—響應(yīng)模型,框架的目的就是幫助我們簡化開發(fā),Spring Web MVC也是要簡化我們?nèi)粘eb開發(fā)的。
Spring Web MVC也是服務(wù)到工作者模式的體現(xiàn),但進行可優(yōu)化。前端控制器是DispatcherServlet;應(yīng)用控制器其實拆為處理器映射器(Handler Mapping)進行處理器管理和視圖解析器(View Resolver)進行視圖管理;頁面控制器/動作/處理器為Controller接口(僅包含ModelAndView handleRequest(request,response)方法)的實現(xiàn)(也可以是任何POJO類);支持本地化(Locale)解析、主題(Theme)解析及文件上傳等;提供了非常靈活的數(shù)據(jù)驗證、格式化和數(shù)據(jù)綁定機制;提供了強大的約定大于配置(慣例優(yōu)先原則)的契約式編程支持。

2.2、Spring Web MVC能幫助我們做什么

1、讓我們能非常簡單的設(shè)計出干凈的Web層和薄薄的Web層;
2、進行更簡潔的Web層開發(fā);
3、天生與Spring框架集成(如IOC容器、AOP等);
4、提供強大的約定大于配置的契約式編程支持;
5、能簡單的進行Web層的單元測試;
6、支持靈活的URL到頁面控制器的映射;
7、非常容易與其他視圖技術(shù)集成;
8、非常靈活的數(shù)據(jù)驗證、格式化和數(shù)據(jù)綁定機制,能使用任何對象進行數(shù)據(jù)綁定,不必實現(xiàn)特定框架的API;
9、提供一套強大的JSP標簽庫,簡化JSP開發(fā);
10、支持靈活的本地化、主題等解析;
11、更加簡單的異常處理;
12、對靜態(tài)資源的支持;
13、支持Restful風格;

2.3、Spring Web MVC架構(gòu)

Spring Web MVC框架也是一個基于請求驅(qū)動的Web框架,并且也是用了前端控制器模式來進行設(shè)計,再根據(jù)請求映射規(guī)則分發(fā)給相應(yīng)的頁面控制器(動作/處理器)進行處理。首先讓我們來整體看一下Spring Web MVC請求的處理流程。

2.3.1、Spring Web MVC處理請求過程

Spring Web MVC請求處理過程

具體執(zhí)行步驟如下:
1、首先客戶發(fā)送請求——>前端控制器,前端控制器根據(jù)請求信息(如URL)來決定選擇哪一個頁面控制器進行處理并把請求委托給它,即以前控制器的控制邏輯部分;如圖中的1、2步驟;
2、頁面控制器接收到請求后,進行功能處理,首先需要收集和綁定請求參數(shù)到一個對象,這個對象在Spring Web MVC中叫命令對象,并進行驗證,然后將命令對象委托給業(yè)務(wù)對象進行處理;處理完畢后返回一個ModelAndView(模型數(shù)據(jù)和邏輯視圖名);如圖中的3、4、5步驟;
3、前端控制器收回控制權(quán),然后根據(jù)返回的邏輯視圖名,選擇相應(yīng)的視圖進行渲染,并把數(shù)據(jù)模型傳入以便視圖渲染;如圖中的6、7步驟;
4、前端控制器再次收回控制權(quán),將響應(yīng)返回給用戶;如圖中的8步驟;至此整個響應(yīng)到此結(jié)束。
問題:
1、請求如何給前端控制器?
2、前端控制器如何根據(jù)請求信息選擇頁面控制器進行功能處理?
3、如何支持多種頁面控制器呢?
4、頁面控制器如何使用業(yè)務(wù)對象?
5、頁面控制器如何返回模型數(shù)據(jù)?
6、前端控制器如何根據(jù)頁面控制器返回的邏輯視圖名選擇具體的視圖進行渲染?
7、不同的視圖技術(shù)如何使用相應(yīng)的模型數(shù)據(jù)?
首先我們知道有如上問題,那么這些問題我們?nèi)绾芜M行解決呢?在后面依次進行回答。

2.3.2、Spring Web MVC架構(gòu)

1、Spring Web MVC核心架構(gòu)圖


Spring Web MVC核心架構(gòu)圖

架構(gòu)圖對應(yīng)的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、處理器適配,即將我們的處理器包裝成相應(yīng)的適配器(從而支持多種類型的處理器)
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
            // 304 Not Modified緩存支持
            //此處省略具體代碼
            // 執(zhí)行處理器相關(guān)的攔截器的預(yù)處理(HandlerInterceptor.preHandle)
            //此處省略具體代碼
            // 步驟4、由適配器執(zhí)行處理器(調(diào)用處理器相應(yīng)功能處理方法)
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 
            // Do we need view name translation?
            if (mv != null && !mv.hasView()) {
                mv.setViewName(getDefaultViewName(request));
            }
            // 執(zhí)行處理器相關(guān)的攔截器的后處理(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");
            }
        }
        // 執(zhí)行處理器相關(guān)的攔截器的完成后處理(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);
        // Trigger after-completion for thrown exception.
        triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
        throw ex;
        }
        finally {
        // Clean up any resources used by a multipart request.
            if (processedRequest != request) {
                  cleanupMultipart(processedRequest);
            }
       }
}

核心架構(gòu)的具體步驟流程如下:
1、首先用戶發(fā)送請求———>DispatcherServlet,前端控制器收到請求后自己不進行處理,而是委托給其他的解析器進行處理,作為統(tǒng)一的訪問點,進行全局的流程控制;
2、DispatcherServlet———>HandlerMapping,HandlerMapping將會把請求映射為HandlerExecutionChain對象(包含一個Handler處理器(頁面控制器)對象、多個HandlerInterceptor攔截器)對象,通過這種策略模式,很容易添加新的映射策略;
3、DispatcherServlet———>HandlerAdapter,HandlerAdapter將會把處理器包裝為適配器,從而支持多種類型的處理器,即適配器設(shè)計模式的應(yīng)用,從而很容易支持很多類型的處理器;
4、HandlerAdapter———>處理器功能處理方法的調(diào)用,HandlerAdapter將會根據(jù)適配的結(jié)果調(diào)用真正的處理器的功能處理方法,完成功能處理;并返回一個ModelAndView對象(包含模型數(shù)據(jù)、邏輯視圖名);
5、ModelAndView的邏輯視圖名———>ViewRsolver,ViewResolver將把邏輯視圖名解析為具體的View,通過這種策略模式,很容易更換其他視圖技術(shù);
6、View———>渲染,View會根據(jù)傳進來的Model模型數(shù)據(jù)進行渲染,此處的Model實際是一個Map數(shù)據(jù)結(jié)構(gòu),因此很容易支持其他視圖技術(shù);
7、返回控制權(quán)給Dispatcher,由DispatcherServlet返回響應(yīng)給用戶,到此一個流程結(jié)束。
到此,再來看我們前邊提出的問題:
1、請求如何給前端控制器?這個應(yīng)該在web.xml中進行部署描述,在HelloWorld中詳細講解。
2、前端控制器如何根據(jù)請求控制信息選擇頁面控制器進行功能處理?我們需要配置HandlerMapping進行映射
3、如何支持多種頁面控制器呢?配置HandlerAdapter從而支持多種類型的頁面控制器
4、頁面控制器如何使用業(yè)務(wù)對象?可以預(yù)料到,肯定利用SpringIoc容器的依賴注入功能
5、頁面控制器如何返回模型數(shù)據(jù)?使用ModelAndView返回
6、前端控制器如何根據(jù)頁面控制器返回的邏輯視圖名選擇具體的視圖進行渲染?使用ViewResolver進行解析
7、不同的視圖技術(shù)如何使用相應(yīng)的模型數(shù)據(jù)?因為Model是一個Map數(shù)據(jù)結(jié)構(gòu),很容易支持其他視圖技術(shù)
在此我們看出具體的核心開發(fā)步驟:
1、DispatcherServlet在web.xml中的部署描述,從而攔截請求到Spring Web MVC
2、HandlerMapping的配置,從而將請求映射到處理器
3、HandlerAdapter的配置,從而支持多種類型的處理器
4、ViewResolver的配置,從而將邏輯視圖名解析為具體視圖技術(shù)
5、處理器(頁面控制器)的配置,從而進行功能處理

2.4Spring Web MVC優(yōu)勢

1、清晰的角色劃分:前端控制器(DispatcherServlet)、 請求到處理器映射(HandlerMapping)、處理器適配器(HandlerAdapter)、視圖解析器(ViewResolver)、處理器或頁面控制器(Controller)、驗證器(Validator)、命令對象(Command請求參數(shù)綁定到的對象就叫命令對象)、表單對象(From Object提供給表單展示和提交到的對象就叫表單對象);
2、分工明確,而且擴展點相當靈活,可以很容易擴展,雖然幾乎不需要;
3、由于命令對象就是一個POJO,無需繼承框架特定API,可以使用命令對象直接作為業(yè)務(wù)對象;
4、和Spring其他框架無縫集成,是其他Web框架所不具備的;
5、可適配,通過HandlerAdapter可以支持任意類作為處理器;
6、可定制性,HandlerMapping、ViewResolver等能夠非常簡單的定制;
7、功能強大的數(shù)據(jù)驗證、格式化、綁定機制;
8、利用Spring提供的Mock對象能夠非常簡單的進行Web層單元測試;
9、本地化、主題的解析的支持,使我們更容易進行國際化和主題的切換;
10、強大的JSP標簽庫,是JSP編寫更加容易。

2.5、HelloWorld入門

2.5.1、準備開發(fā)環(huán)境和運行環(huán)境

☆開發(fā)工具:eclipse
☆運行環(huán)境:tomcat7
☆工程:動態(tài)Web工程
spring所有框架下載
spring-framework-4.3.1.RELEASE-dist.zip
☆依賴jar包:
1、Spring依賴的jar包:
為了簡單,將spring-framework-4.3.1.RELEASE-dist.zip/dist/下所有的jar包拷貝到項目的WEB-INF/lib目錄下;
2、Spring框架依賴的jar包:
需要添加Apache commons logging日志,此處用的commons-logging-1.2.jar
需要添加jstl標簽庫支持,此處使用的是jstl-1.1.2.jar和standard-1.1.2.jar
3、項目目錄

項目結(jié)構(gòu)圖

4、效果
效果

2.5.2、前端控制器的配置

我們在web.xml中添加如下配置

  <servlet>
    <servlet-name>helloworld</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>helloworld</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

load-on-startup:表示啟動容器時初始化該Servlet;
url-pattern:表示哪些請求交給Spring Web MVC處理,“/”是用來定義默認Servlet映射的。也可以如“*.html”表示攔截所有以html為擴展名的請求。
自此請求已交給Spring Web MVC框架處理,因此我們需要配置Spring的配置文件,默認的DispatcherServlet會加載WEB-INF/[DispatcherServlet的Servlet名字]-servlet.xml配置文件。本例為WEB-INF/helloworld-servlet.xml。

2.5.3、在Spring配置文件中配置HandlerMapping、HandlerAdapter

具體配置在WEB-INF/helloworld.xml文件中:

    <!-- HandlerMapping -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    
    <!-- HandlerAdapter -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

BeanNameUrlHandlerMapping:表示將在請求的URL和Bean名字映射,如URL為“上下文/hello”,則Spring配置文件必須有一個名字為“/hello”的bean,上下文默認忽略。
SimpleControllerHandlerAdapter:表示所有實現(xiàn)了org.springframework.web.servlet.mvc.Controller接口的Bean可以作為Spring Web MVC中的處理器。如果需要其他類型的處理器可以通過實現(xiàn)HandlerAdapter來解決。

2.5.4、在Spring配置文件中配置ViewResolver

具體配置在WEB-INF/ helloworld-servlet.xml文件中:

    <!-- ViewResolver -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

InternalResourceViewResolver:用于支持Servlet、JSP視圖解析;
viewClass:JstlView表示JSP模板頁面需要使用JSTL標簽庫,classpath中必須啊包含jstl的相關(guān)jar包;
prefix和suffix:查找視圖頁面的前綴和后綴(前綴[邏輯視圖名]后綴),比如傳過來的邏輯視圖名為hello,則該jsp視圖頁面應(yīng)該存放在“/WEB-INF/jsp/hello.jsp”;

2.5.5、開發(fā)處理器/頁面控制器

package com.helloworld;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class HelloWorldController implements Controller{

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //1、收集參數(shù)
        //2、綁定參數(shù)到命令對象
        //3、將命令對象傳如業(yè)務(wù)對象進行業(yè)務(wù)處理
        //4、選擇下一個頁面
        ModelAndView mv = new ModelAndView();
        mv.addObject("message", "HelloWorld");
        mv.setViewName("hello");
        return mv;
    }
}

org.springframework.web.servlet.mvc.Controller:頁面控制器/處理器必須是實現(xiàn)Controller接口,注意別選錯了;后邊我們會學(xué)習其他的處理器實現(xiàn)方式;
public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp):功能處理方法,實現(xiàn)相應(yīng)的功能處理,比如收集參數(shù)、驗證參數(shù)、綁定參數(shù)到命令對象、將命令對象傳入到業(yè)務(wù)對象進行業(yè)務(wù)處理,最后返回ModelAndView對象;
ModelAndView:包含了視圖要實現(xiàn)的模型數(shù)據(jù)和邏輯視圖名;“mv.addObject("message", "Hello World!");”表示添加模型數(shù)據(jù),此處可以是任意的POJO對象;“mv.setViewName("hello");”表示設(shè)置邏輯視圖名為“hello”,視圖解析器會將其解析為具體的視圖,如前邊的視圖解析器InternalResourceVi。wResolver 會將其解析為“WEB-INF/jsp/hello.jsp”。
我們需要將其添加到Spring配置文件(WEB-INF/helloworld-servlet.xml),讓其接受Spring IoC容器管理:

    <!-- 處理器 -->
    <bean name="/hello" class="com.helloworld.HelloWorldController"/>

name="/hello":前邊配置的BeanNameUrlHandlerMapping,表示如果請求的URL為“上下文/hello”,則將會交給該Bean處理。

2.5.6、開發(fā)視圖界面

創(chuàng)建 /WEB-INF/jsp/hello.jsp視圖頁面:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Hello World</title>
</head>
<body>
    ${message }
</body>
</html>

${message}:表示顯示由HelloWorldController處理器傳過來的模型數(shù)據(jù)。

2.5.7、啟動服務(wù)器運行測試

通過請求:http://localhost:8080/HelloWorld/hello如果頁面輸出“Hello World! ”就表明我們成功了!
localhost:服務(wù)器地址;
8080:端口;
HelloWorld:web應(yīng)用上下文;
/hello:映射到控制器部分

2.5.8、運行流程分析

運行流程分析

運行步驟:
1、首先用戶發(fā)送請求http://localhost:8080/HelloWorld/hello——>web容器,web容器根據(jù)“/hello”路徑映射到DispatcherServlet(url-pattern為/)進行處理;
2、DispatcherServlet——>BeanNameUrlHandlerMapping進行請求到處理的映射,BeanNameUrlHandlerMapping將“/hello”路徑直接映射到名字為“/hello”的Bean進行處理,即HelloWorldController,BeanNameUrlHandlerMapping
將其包裝為HandlerExecutionChain(只包括HelloWorldController 處理器,沒有攔截器);
3、DispatcherServlet——>SimpleControllerHandlerAdapter,SimpleControllerHandlerAdapter 將HandlerExecutionChain中的處理器(HelloWorldController)適配為SimpleControllerHandlerAdapter;
4、SimpleControllerHandlerAdapter——> HelloWorldController 處理器功能處理方法的調(diào)用,
SimpleControllerHandlerAdapter 將會調(diào)用處理器的handleRequest 方法進行功能處理,該處理方法返回一個ModelAndView給DispatcherServlet;
5、hello(ModelAndView 的邏輯視圖名)——>InternalResourceViewResolver, InternalResourceViewResolver 使用JstlView,具體視圖頁面在/WEB-INF/jsp/hello.jsp;
6、JstlView(/WEB-INF/jsp/hello.jsp)——>渲染,將在處理器傳入的模型數(shù)據(jù)(message=HelloWorld!)在視圖中展示出來;
7、返回控制權(quán)給DispatcherServlet,由DispatcherServlet 返回響應(yīng)給用戶,到此一個流程結(jié)束。
到此HelloWorld就完成了,步驟是不是有點多?而且回憶下我們主要進行了如下配置:
1、前端控制器DispatcherServlet;
2、HandlerMapping;
3、HandlerAdapter;
4、ViewResolver;
5、處理器/頁面控制器;
6、視圖
因此,接下來幾章讓我們詳細看看這些配置,先從DispatcherServlet開始吧。

2.6、POST中文亂碼解決方案

Spring Web MVC框架提供了org.springframework.web.filter.CharacterEncodingFilter用于解決POST方式造成的中文亂碼問題,具體配置如下:

<filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
  </filter>
  <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
  </filter-mapping>

以后我們項目及所有頁面的編碼均為UTF-8。

2.7、Spring新特性

一、Spring2.5 之前,我們都是通過實現(xiàn)Controller 接口或其實現(xiàn)來定義我們的處理器類。
二、Spring2.5 引入注解式處理器支持,通過@Controller 和@RequestMapping注解定義我們的處理器類。并且提供了一組強大的注解:
需要通過處理器映射DefaultAnnotationHandlerMapping和處理器適配器AnnotationMethodHandlerAdapter來開啟支持@Controller 和 @RequestMapping 注解的處理器。

注解 作用
@Controller 用于標識是處理器類
@RequestMapping 請求到處理器功能方法的映射規(guī)則
@RequestParam 請求參數(shù)到處理器功能處理方法的方法參數(shù)上的綁定
@ModelAttribute 請求參數(shù)到命令對象的綁定
@InitBinder 自定義數(shù)據(jù)綁定注冊支持,用于將請求參數(shù)轉(zhuǎn)換到命令對象屬性的對應(yīng)類型
@SessionAttributes 用于聲明session 級別存儲的屬性,放置在處理器類上,通常列出模型屬性(如@ModelAttribute)對應(yīng)的名稱,則這些屬性會透明的保存到session 中

三、Spring3.0 引入RESTful 架構(gòu)風格支持(通過@PathVariable注解和一些其他特性支持),且又引入了
更多的注解支持:

注解 作用
@CookieValue cookie 數(shù)據(jù)到處理器功能處理方法的方法參數(shù)上的綁定
@RequestHeader 請求頭(header)數(shù)據(jù)到處理器功能處理方法的方法參數(shù)上的綁定
@RequestBody 請求的body體的綁定(通過HttpMessageConverter 進行類型轉(zhuǎn)換)
@ResponseBody 處理器功能處理方法的返回值作為響應(yīng)體(通過HttpMessageConverter進行類型轉(zhuǎn)換)
@ResponseStatus 定義處理器功能處理方法/異常處理器返回的狀態(tài)碼和原因
@ExceptionHandler 注解式聲明異常處理器
@PathVariable 請求URI 中的模板變量部分到處理器功能處理方法的方法參數(shù)上的綁定,從而支持RESTful 架構(gòu)風格的URI

四、Spring3.1 新特性:
對 Servlet 3.0 的全面支持。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容