【JavaEE】Spring MVC入門(mén):工作原理及入門(mén)程序

最近開(kāi)始學(xué)習(xí)Spring MVC框架,在傳智博客的課程資源中找到了Spring MVC的教學(xué)視頻,本文也作為課程筆記與小結(jié)。

Spring MVC

Spring MVC屬于SpringFrameWork的后續(xù)產(chǎn)品,已經(jīng)融合在Spring Web Flow里面。Spring 框架提供了構(gòu)建 Web 應(yīng)用程序的全功能 MVC 模塊。
在開(kāi)始入門(mén)程序之前,先了解下Spring MVC的工作原理。

Spring MVC工作流程

Spring MVC的工作流程可用下面這張圖表示:


Spring MVC工作流程

Spring MVC主要由前端控制器 DispatcherServlet、處理器映射器 HandlerMapping、處理器適配器 HandlerAdapter、處理器 Handler、視圖解析器 ViewResolver 以及 視圖 View 組成。
從命名上看,這里的DispatcherServlet就是一個(gè)Servlet,所有請(qǐng)求正是從這個(gè)Servlet開(kāi)始,而Spring MVC正是采用了前端控制器模式(Front Controller Pattern)。所謂前端控制器模式,是用來(lái)提供一個(gè)集中的請(qǐng)求處理機(jī)制,所有的請(qǐng)求都將由一個(gè)單一的處理程序處理。該處理程序可以做認(rèn)證/授權(quán)/記錄日志,或者跟蹤請(qǐng)求,然后把請(qǐng)求傳給相應(yīng)的處理程序。
對(duì)照上圖中的數(shù)字標(biāo)號(hào),Spring MVC的工作流程大致如下:

  1. 用戶(hù)發(fā)送請(qǐng)求至DispatcherServlet
  2. DispatcherServlet請(qǐng)求HandlerMapping查找Handler
  3. HandlerMapping向DispatcherServlet返回Handler
  4. DispatcherServlet請(qǐng)求HandlerAdapter執(zhí)行Handler
  5. HandlerAdapter執(zhí)行Handler
  6. Handler返回一個(gè)ModelAndView對(duì)象給HandlerAdapter,ModelAndView中包含模型數(shù)據(jù)和邏輯視圖名
  7. HandlerAdapter返回ModelAndView給DispatcherServlet
  8. DispatcherServlet請(qǐng)求ViewResolver將邏輯視圖名解析為具體的View
  9. ViewResolver將解析出的具體的View返回給DispatcherServlet
  10. View根據(jù)傳過(guò)來(lái)的Model數(shù)據(jù)進(jìn)行渲染,Model實(shí)際上是一個(gè)Map
  11. 視圖渲染完畢后最終由DispatcherServlet響應(yīng)給用戶(hù)

在實(shí)際開(kāi)發(fā)中,以上幾個(gè)模塊Handler和View需要我們編寫(xiě),其它模塊均需要進(jìn)行配置,后面的入門(mén)程序中會(huì)講到。

Spring MVC 入門(mén)程序

首先介紹我所使用的開(kāi)發(fā)環(huán)境

  • JDK 1.6.0_23
  • Tomcat 7.0.69
  • Spring Framework 4.3.3
  • IDE: IntelliJ IDEA 15.0.6

入門(mén)程序是實(shí)現(xiàn)一個(gè)簡(jiǎn)單的商品查詢(xún)功能,相關(guān)數(shù)據(jù)將采用靜態(tài)數(shù)據(jù)的方式。

1.建立工程,搭建開(kāi)發(fā)環(huán)境

項(xiàng)目目錄

項(xiàng)目目錄如上所示,其中config文件夾作為存放一些xml配置文件的目錄,建立文件夾后需要右鍵Make Directory As Sources Root才能作為classpath使用。web/WEB-INF/lib文件夾用于存放項(xiàng)目所用到的jar包,將Spring和其它需要的jar包放入。
lib

其中spring-webmvc-4.3.3.RELEASE.jar便是Spring MVC模塊,另外mybatis是為后續(xù)開(kāi)發(fā)準(zhǔn)備,入門(mén)項(xiàng)目暫時(shí)用不到。其它項(xiàng)目相關(guān)配置(如Tomcat)在這不多提及。

2.配置DispatcherServlet

前面提及DispatcherServlet實(shí)際就是一個(gè)Servlet,所以應(yīng)在web.xml中配置這個(gè)Servlet。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    
    <servlet>
        <servlet-name>DispatchServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>DispatchServlet</servlet-name>
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>
</web-app>

參數(shù)contentConfigLocation用于指定Spring配置文件的位置,這里是項(xiàng)目目錄中的config下的spring-mvc.xml。

3.配置其它模塊/編寫(xiě)Handler

接下來(lái),需要對(duì)HandlerMapping、HandlerAdapter、ViewResolver進(jìn)行配置,首先介紹使用非注解方式的配置。

3.1使用非注解方式

在Spring MVC中,由多種HandlerMapping和HandlerAdapter可供我們使用,首先介紹HandlerAdapter的配置。
第一種:SimpleControllerHandlerAdapter
使用SimpleControllerHandlerAdapter要求所編寫(xiě)的Handler實(shí)現(xiàn)Controller接口。
在Spring配置文件spring-mvc.xml添加相應(yīng)的bean:

<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>

Handler類(lèi):

public class ItemsControllerA implements Controller
{
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception
    {
        List<Items> itemsList = new ArrayList<Items>();
        Items items1 = new Items();
        items1.setName("Microsoft Surface Pro 4");
        items1.setPrice(8900f);
        items1.setDetail("Surface");

        Items items2 = new Items();
        items2.setName("Microsoft Surface Studio");
        items2.setPrice(20000f);
        items2.setDetail("Surface");

        itemsList.add(items1);
        itemsList.add(items2);

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("itemsList", itemsList);
        modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp");
        return modelAndView;
    }
}

Items是一個(gè)po類(lèi),擁有一些基本屬性和相應(yīng)的getter和setter方法(如id、name、price等),這里作為入門(mén)示例使用了靜態(tài)數(shù)據(jù),itemsList.jsp位于/WEB-INF/jsp/items下,里面僅有一個(gè)表格用于數(shù)據(jù)的展示。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form>
    商品列表:
    <table>
        <tr>
            <td>商品名稱(chēng)</td>
            <td>商品價(jià)格</td>
            <td>商品描述</td>
        </tr>
        <c:forEach items="${itemsList}" var="item">
            <tr>
                <td>${item.name}</td>
                <td>${item.price}</td>
                <td>${item.detail}</td>
            </tr>
        </c:forEach>
    </table>
</form>
</body>
</html>

接下來(lái)還需要對(duì)HandlerMapping配置,首先介紹BeanNameUrlHandlerMapping:使用該映射器要求在配置Handler類(lèi)時(shí)指定bean的name屬性,且name作為url,配置如下:

    <!-- Handler -->
    <bean id="ItemsControllerA" name="/queryItems.action" class="com.dstudiow.ssm.controller.ItemsControllerA"/>

    <!-- BeanNameUrlHandlerMapping: 將bean的name作為url查找,需在配置Handler指定bean name,即url -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    </bean>

最后還有ViewResolver的配置,對(duì)于jsp頁(yè)面,使用InternalResourceViewResolver

    <!-- ViewResolver -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>

spring-mvc.xml完整內(nèi)容如下:

    <?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:mvc="http://www.springframework.org/schema/mvc"
           xmlns:content="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-4.1.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
    
    
        <!-- Handler -->
        <bean id="ItemsControllerA" name="/queryItems.action" class="com.dstudiow.ssm.controller.ItemsControllerA"/>

        <!-- BeanNameUrlHandlerMapping: 將bean的name作為url查找,需在配置Handler指定bean name,即url -->
        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

        <!-- SimpleControllerHandlerAdapter: 要求Handler實(shí)現(xiàn)Controller接口 -->
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
   
        <!-- ViewResolver -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
    </beans>

至此,相關(guān)的程序編碼和配置均已完成,啟動(dòng)Tomcat,將項(xiàng)目部署至Tomcat中,訪問(wèn)鏈接


頁(yè)面

另外一種映射器叫做SimpleUrlHandlerMapping,使用方式如下:

<!-- SimpleUrlHandlerMapping -->
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <!-- key為url,值為bean id -->
                <prop key="/queryItems1.action">ItemsControllerA</prop>
            </props>
        </property>
    </bean>
頁(yè)面

第二種 HttpRequestHandlerAdapter
使用HttpRequestHandlerAdapter要求Handler實(shí)現(xiàn)HttpRequestHandler接口,使用如下(直接在spring-mvc.xml繼續(xù)添加了bean):

Handler:

public class ItemsControllerB implements HttpRequestHandler
{
    @Override
    public void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException
    {
        List<Items> itemsList = new ArrayList<Items>();
        Items items1 = new Items();
        items1.setName("Microsoft Surface Pro 4");
        items1.setPrice(8900f);
        items1.setDetail("Surface");

        Items items2 = new Items();
        items2.setName("Microsoft Surface Studio");
        items2.setPrice(20000f);
        items2.setDetail("Surface");

        itemsList.add(items1);
        itemsList.add(items2);

        httpServletRequest.setAttribute("itemsList", itemsList);
        httpServletRequest.getRequestDispatcher("/WEB-INF/jsp/items/itemsList.jsp").forward(httpServletRequest, httpServletResponse);
    }
}

spring-mvc.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:mvc="http://www.springframework.org/schema/mvc"
           xmlns:content="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-4.1.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
    
    
        <!-- Handler -->
        <bean id="ItemsControllerA" name="/queryItems.action" class="com.dstudiow.ssm.controller.ItemsControllerA"/>
        <bean id="ItemsControllerB" class="com.dstudiow.ssm.controller.ItemsControllerB"/>
    
        <!-- BeanNameUrlHandlerMapping: 將bean的name作為url查找,需在配置Handler指定bean name,即url -->
        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    
        </bean>
    
        <!-- SimpleUrlHandlerMapping -->
        <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
            <property name="mappings">
                <props>
                    <!-- key為url,值為bean id -->
                    <prop key="/queryItems1.action">ItemsControllerA</prop>
                    <prop key="/queryItems2.action">ItemsControllerB</prop>
                </props>
            </property>
        </bean>
     
        <!-- SimpleControllerHandlerAdapter: 要求Handler實(shí)現(xiàn)Controller接口 -->
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    
        <!-- HttpRequestHandlerAdapter: 要求Handler實(shí)現(xiàn)HttpRequestHandler接口 -->
        <bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>

        <!-- ViewResolver -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>

    </beans>

使用HttpRequestHandlerAdapter時(shí)Handler中handleRequest()方法與Servlet中的doGet()或doPost()方法幾乎一樣,使用這種方法可以根據(jù)需求設(shè)置相應(yīng)數(shù)據(jù)格式(如不需要返回頁(yè)面而是Json數(shù)據(jù),使用httpServletResponse.getWriter().write()寫(xiě)入Json字符串)。

以上便是使用非注解配置方式的HandlerAdapter和HandlerMapping的內(nèi)容,通過(guò)學(xué)習(xí)也了解到了非注解方式存在明顯的缺點(diǎn),一個(gè)Handler只有一個(gè)方法實(shí)現(xiàn)我們的功能,在需求較多的情況下,如本案例中同樣是商品的相關(guān)請(qǐng)求,需要獲取全部商品/根據(jù)id查找商品/修改商品信息等都需要寫(xiě)不同的Handler,比較麻煩,使用注解的方式正能解決這一問(wèn)題。

3.2 使用非注解方式

首先需要在Spring配置文件中配置<content:component-scan>,開(kāi)啟Spring的組件掃描。

<content:component-scan base-package="com.dstudiow.ssm.controller"/>

接下來(lái)是注解方式所使用的處理器適配器以及處理器映射器,分別為RequestMappingHandlerAdapterRequestMappingHandlerMapping,在配置文件加入bean即可:

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

除此之外,可以使用<mvc:annotation-driven/>標(biāo)簽,該標(biāo)簽幫我們默認(rèn)注冊(cè)了RequestMappingHandlerAdapterRequestMappingHandlerMapping這兩個(gè)bean。而在Spring 3.2之前的版本中默認(rèn)注冊(cè)的為DefaultAnnotationHandlerMappingAnnotationMethodHandlerAdapter,這兩個(gè)類(lèi)目前已被廢棄。
接下來(lái)是Handler類(lèi):

@Controller
public class ItemsControllerC
{

    @RequestMapping("/queryItems3.action")
    public ModelAndView queryItems() throws Exception
    {
        List<Items> itemsList = new ArrayList<Items>();
        Items items1 = new Items();
        items1.setName("Microsoft Surface Pro 4");
        items1.setPrice(8900f);
        items1.setDetail("Surface");

        Items items2 = new Items();
        items2.setName("Microsoft Surface Studio");
        items2.setPrice(20000f);
        items2.setDetail("Surface");

        itemsList.add(items1);
        itemsList.add(items2);

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("itemsList", itemsList);
        modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp");
        return modelAndView;
    }
}

@Controller注解標(biāo)識(shí)此類(lèi)為一個(gè)Handler,@RequestMapping標(biāo)識(shí)所對(duì)應(yīng)的訪問(wèn)路徑。


ViewResolver

在前面的示例中,Handler會(huì)返回一個(gè)ModelAndView對(duì)象,通過(guò)setViewName()方法設(shè)置了對(duì)應(yīng)的視圖絕對(duì)路徑,根據(jù)我們項(xiàng)目目錄的劃分,這個(gè)完整路徑一般還是比較長(zhǎng)的,每次都在代碼中寫(xiě)下完整的路徑未免有些繁瑣,所以ViewResolver有關(guān)于url前后綴的屬性可以配置,如下:

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

Handler就不需要寫(xiě)下完整路徑了:

modelAndView.setViewName("items/itemsList");
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評(píng)論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,497評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,305評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,962評(píng)論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,727評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,193評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,411評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,945評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,777評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,978評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,216評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,642評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,878評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,657評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,960評(píng)論 2 373

推薦閱讀更多精彩內(nèi)容