【JavaEE】Spring MVC入门:工作原理及入门程序

最近开始学习Spring MVC框架,在传智博客的课程资源中找到了Spring MVC的教学视频,本文也作为课程笔记与小结。

Spring MVC

Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。
在开始入门程序之前,先了解下Spring MVC的工作原理。

Spring MVC工作流程

Spring MVC的工作流程可用下面这张图表示:


Spring MVC工作流程

Spring MVC主要由前端控制器 DispatcherServlet、处理器映射器 HandlerMapping、处理器适配器 HandlerAdapter、处理器 Handler、视图解析器 ViewResolver 以及 视图 View 组成。
从命名上看,这里的DispatcherServlet就是一个Servlet,所有请求正是从这个Servlet开始,而Spring MVC正是采用了前端控制器模式(Front Controller Pattern)。所谓前端控制器模式,是用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理。该处理程序可以做认证/授权/记录日志,或者跟踪请求,然后把请求传给相应的处理程序。
对照上图中的数字标号,Spring MVC的工作流程大致如下:

  1. 用户发送请求至DispatcherServlet
  2. DispatcherServlet请求HandlerMapping查找Handler
  3. HandlerMapping向DispatcherServlet返回Handler
  4. DispatcherServlet请求HandlerAdapter执行Handler
  5. HandlerAdapter执行Handler
  6. Handler返回一个ModelAndView对象给HandlerAdapter,ModelAndView中包含模型数据和逻辑视图名
  7. HandlerAdapter返回ModelAndView给DispatcherServlet
  8. DispatcherServlet请求ViewResolver将逻辑视图名解析为具体的View
  9. ViewResolver将解析出的具体的View返回给DispatcherServlet
  10. View根据传过来的Model数据进行渲染,Model实际上是一个Map
  11. 视图渲染完毕后最终由DispatcherServlet响应给用户

在实际开发中,以上几个模块Handler和View需要我们编写,其它模块均需要进行配置,后面的入门程序中会讲到。

Spring MVC 入门程序

首先介绍我所使用的开发环境

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

入门程序是实现一个简单的商品查询功能,相关数据将采用静态数据的方式。

1.建立工程,搭建开发环境

项目目录

项目目录如上所示,其中config文件夹作为存放一些xml配置文件的目录,建立文件夹后需要右键Make Directory As Sources Root才能作为classpath使用。web/WEB-INF/lib文件夹用于存放项目所用到的jar包,将Spring和其它需要的jar包放入。
lib

其中spring-webmvc-4.3.3.RELEASE.jar便是Spring MVC模块,另外mybatis是为后续开发准备,入门项目暂时用不到。其它项目相关配置(如Tomcat)在这不多提及。

2.配置DispatcherServlet

前面提及DispatcherServlet实际就是一个Servlet,所以应在web.xml中配置这个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>

参数contentConfigLocation用于指定Spring配置文件的位置,这里是项目目录中的config下的spring-mvc.xml

3.配置其它模块/编写Handler

接下来,需要对HandlerMapping、HandlerAdapter、ViewResolver进行配置,首先介绍使用非注解方式的配置。

3.1使用非注解方式

在Spring MVC中,由多种HandlerMapping和HandlerAdapter可供我们使用,首先介绍HandlerAdapter的配置。
第一种:SimpleControllerHandlerAdapter
使用SimpleControllerHandlerAdapter要求所编写的Handler实现Controller接口。
在Spring配置文件spring-mvc.xml添加相应的bean:

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

Handler类:

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是一个po类,拥有一些基本属性和相应的getter和setter方法(如id、name、price等),这里作为入门示例使用了静态数据,itemsList.jsp位于/WEB-INF/jsp/items下,里面仅有一个表格用于数据的展示。

<%@ 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>商品名称</td>
            <td>商品价格</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>

接下来还需要对HandlerMapping配置,首先介绍BeanNameUrlHandlerMapping:使用该映射器要求在配置Handler类时指定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的配置,对于jsp页面,使用InternalResourceViewResolver

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

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"/>

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

        <!-- SimpleControllerHandlerAdapter: 要求Handler实现Controller接口 -->
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
   
        <!-- ViewResolver -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
    </beans>

至此,相关的程序编码和配置均已完成,启动Tomcat,将项目部署至Tomcat中,访问链接


页面

另外一种映射器叫做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>
页面

第二种 HttpRequestHandlerAdapter
使用HttpRequestHandlerAdapter要求Handler实现HttpRequestHandler接口,使用如下(直接在spring-mvc.xml继续添加了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实现Controller接口 -->
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    
        <!-- HttpRequestHandlerAdapter: 要求Handler实现HttpRequestHandler接口 -->
        <bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>

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

    </beans>

使用HttpRequestHandlerAdapter时Handler中handleRequest()方法与Servlet中的doGet()或doPost()方法几乎一样,使用这种方法可以根据需求设置相应数据格式(如不需要返回页面而是Json数据,使用httpServletResponse.getWriter().write()写入Json字符串)。

以上便是使用非注解配置方式的HandlerAdapter和HandlerMapping的内容,通过学习也了解到了非注解方式存在明显的缺点,一个Handler只有一个方法实现我们的功能,在需求较多的情况下,如本案例中同样是商品的相关请求,需要获取全部商品/根据id查找商品/修改商品信息等都需要写不同的Handler,比较麻烦,使用注解的方式正能解决这一问题。

3.2 使用非注解方式

首先需要在Spring配置文件中配置<content:component-scan>,开启Spring的组件扫描。

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

接下来是注解方式所使用的处理器适配器以及处理器映射器,分别为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/>标签,该标签帮我们默认注册了RequestMappingHandlerAdapterRequestMappingHandlerMapping这两个bean。而在Spring 3.2之前的版本中默认注册的为DefaultAnnotationHandlerMappingAnnotationMethodHandlerAdapter,这两个类目前已被废弃。
接下来是Handler类:

@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注解标识此类为一个Handler,@RequestMapping标识所对应的访问路径。


ViewResolver

在前面的示例中,Handler会返回一个ModelAndView对象,通过setViewName()方法设置了对应的视图绝对路径,根据我们项目目录的划分,这个完整路径一般还是比较长的,每次都在代码中写下完整的路径未免有些繁琐,所以ViewResolver有关于url前后缀的属性可以配置,如下:

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

Handler就不需要写下完整路径了:

modelAndView.setViewName("items/itemsList");
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容