概述

前面两篇介绍了模型2架构的优势以及如何构建一个模型2应用。但是Spring MVC框架可以帮助我们快速的开发MVC应用。


Spring MVC体系概述

若基于某个框架来开发一个模型2的应用程序,我们要负责编写一个Dispatcher servlet和控制类。 其中Dispatcher servlet必须能够做到如下事情:

  1. 根据URI调用对应的action
  2. 实例化正确的控制器类
  3. 根据请求参数来构造表单bean
  4. 调用控制器对象的相应方法
  5. 转向一个视图

Spring MVC框架围绕DispatcherServlet这个核心展开,DispatcherServlet负责截获请求并分派给相应的处理器处理。 SpringMVC框架包括注解驱动控制器、请求及响应的信息处理、视图解析、本地化解析、上传文件解析、异常处理及表单标签绑定等内容。


由于SpringMVC是基于Model2实现的框架,所以它底层的机制也是MVC,我们来看下SpringMVC的整体架构

从接收请求到返回相应,Spring MVC框架的众多组件有条不紊的完成内部的分工,在整个框架中,DispatcherServlet处于核心的位置,负责协调和组织不同组件以完成请求处理并返回响应的工作。

下面我们来分步解析下SpringMVC处理请求的整体过程

  1. 整个过程始于客户端发出的一个HTTP请求,Web应用服务器收到这个请求后,如果匹配DispatcherServlet的请求映射路径(web.xml中指定),则web容器将该请求交给DispatcherServlet处理

  2. DispatcherServlet接收到这请求后,将根据请求的信息(包括url,HTTP方法、请求报文头、请求参数、Cookie等)及HandlerMapping的配置找到处理请求的处理器(Handler)。 可将HandlerMapping看做是路由控制器,将Handler看做目标主机。值得注意的是,SpringMVC中并没有定义一个Handler接口,实际上任何一个Object都可以称为请求处理器。

  3. 当DispatcherServlet根据HandlerMapping得到对应请求的Handler后,通过HandlerAdpapter对Handler进行封装再以统一的适配器接口调用Handler.

  4. 处理器完成业务逻辑的处理后将返回一个ModelAndView给DispatcherServlet,ModelAndView包含了视图逻辑名和模型数据信息

  5. ModelAndView并非真正的视图对象,DispatcherServlet通过ViewResolver完成逻辑视图和真实视图对象的解析工作

  6. 当得到真实的视图对象View后,DispatcherServlet就使用这个View对ModelAndView中的模型数据进行视图渲染

  7. 最终客户端得到相应消息可能是一个普通的html页面,也可能是要给XML或者JSON串,甚至是一张图片或者PDF文档等不同的媒体形式。


Spring MVC的DispatcherServlet

我们在前面两篇博文的例子中,servlet需要我们自己编写,基于Spring MVC ,则无需如此。 SpringMVC中自带了一个开箱即用的DispatcherServlet,全限定名为org.springframework.web.servlet.DispatcherServlet

使用DispatcherServlet

要想使用这个servlet,同样的也需要把它配置在部署描述符(web.xml)、应用servlet和servlet-mapping。如下所示

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><!-- map all requests to DispatcherServlet --><url-pattern>/</url-pattern></servlet-mapping>
</web-app>
  • servlet元素内的on-startup是可选项,如果它存在,则它将在应用程序启动时装载servlet并调用他的init方法,若不存在,则该servlet的第一个请求时加载

  • DispatcherServlet将使用Spring MVC诸多默认的组件,此外,初始化的时候,它会寻找一个在应用程序的WEB-INF目录下的配置文件,该配置文件的命名规则 servletName-servlet.xml 。 其中servletName是在部署描述中的DispatcherServlet的名称,比如我们上述的配置文件 <servlet-name>springmvc</servlet-name>,则在WEB-INF下对应的文件为springmvc-servlet.xml.

  • 此外,也可以把SpringMVC的配置文件放在应用程序目录中的任何地方,用servlet定义的init-param元素,以便DispatcherServlet加载到该文件。 init-param元素拥有一个contextConfigLocation的param-name元素,其param-value元素则包含配置文件的路径。如下所示

  <servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/config/simple-config.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet>

Controller接口

Spring2.5之前,开发一个控制器的唯一方法是实现org.springframework.web.servlet.mvc.Controller接口,该接口中有一个方法

ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;

其实现类可以访问对应请求的HttpServletRequest 和HttpServletResponse ,同时还必须返回包含视图路径或者视图路径和模的ModelAndView 对象。

Controller接口实现类只能处理一个单一动作,而一个基于注解的控制器可以同时支持多个请求处理动作,并且无需实现任何接口(后续介绍这种主流的开发方式,这里先演示下实现接口的方式


简单示例(实现接口的方式)

在这里我们只是简单的演示一下这种方式的用法,实际开发中并不推荐这样做。基于注解的方式后续介绍。

maven工程结构如下:

pom.xml添加依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.artisan</groupId><artifactId>chapter03a</artifactId><packaging>war</packaging><version>0.0.1-SNAPSHOT</version><name>chapter03a Maven Webapp</name><url>http://maven.apache.org</url><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope><!-- provided 依赖只有在当JDK 或者一个容器已提供该依赖之后才使用。 例如, 如果你开发了一个web 应用,你可能在编译 classpath 中需要可用的Servlet API 来编译一个servlet,但是你不会想要在打包好的WAR 中包含这个Servlet API;这个Servlet API JAR 由你的应用服务器或者servlet 容器提供。已提供范围的依赖在编译classpath (不是运行时)可用。它们不是传递性的,也不会被打包。 --></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>4.3.9.RELEASE</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency></dependencies><build><finalName>chapter03a</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>2.5.1</version><configuration><source>1.7</source><target>1.7</target><compilerArgument>-Xlint:all</compilerArgument><showWarnings>true</showWarnings><showDeprecation>true</showDeprecation></configuration></plugin></plugins></build>
</project>

部署描述文件和Spring MVC 的配置文件

部署描述文件web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- load servlet file by contextConfigLocation --><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/config/artisan-servlet.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><!-- map *.action requests to DispatcherServlet --><url-pattern>*.action</url-pattern></servlet-mapping>
</web-app>

这里告诉Servlet/JSP容器,我们将使用SpringMVC的DispatcherServlet,并通过配置url-pattern原始来匹配.action结尾的URL映射到该servlet。 并通过init-param元素,加载特定目录下的Spring MVC 配置文件人,如果不配置的话,则SpringMVC的配置文件将在默认的/WEB-INF目录下,并且按照约定的命名规范。

Spring MVC配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean name="/product_input.action" class="com.artisan.springmvc.controller.InputProductController"/><bean name="/product_save.action" class="com.artisan.springmvc.controller.SaveProductController"/></beans>

这里声明两个控制器类InputProductController和SaveProductController,并分别映射到/product_input.action和/product_save.action。


Controller

下面来编写“传统”风格的控制器,分别实现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 实现Spring自身的Controller**/
public class InputProductController implements Controller{private static final Logger logger = Logger.getLogger(InputProductController.class);@Overridepublic ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {logger.info("InputProductController called");return new ModelAndView("/WEB-INF/jsp/ProductForm.jsp");}}

InputProductController 类的handleRequest方法只返回一个ModelAndView对象,包含一个视图,并没有模型。因此,该请求将被转发到/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 实现Spring自身的Controller**/
public class SaveProductController implements Controller{private static final Logger logger = Logger.getLogger(SaveProductController.class);@Overridepublic ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {logger.info("SaveProductController called");ProductForm productForm = new ProductForm();// populate action propertiesproductForm.setName(request.getParameter("name"));productForm.setDescription(request.getParameter("description"));productForm.setPrice(request.getParameter("price"));// create modelProduct 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 Productreturn new ModelAndView("/WEB-INF/jsp/ProductDetails.jsp", "product",product);}}

SaveProductController类的handleRequest方法中,首先用请求创建了一个ProductForm对象,然后它根据ProductForm对象创建Product对象 。 由于ProductForm的price是一个字符串,而在Product类中是一个float类型,因此需要转型(后面会介绍通过数据绑定省去ProductForm,这里暂不讲解)

SaveProductController的handleRequest方法返回最后的ModelAndView模型包含了视图的路径、模型名称和模型(Product对象),该模型将提供给目标视图,用于界面显示。


视图

两个JSP,绑定了CSS样式。
我们在web.xml配置url-pattern来匹配.action ,没有配置 / (所有请求)是因为如果配置了/,而没有配置静态资源过滤,这个CSS也会被拦截,因此这里暂时配置了拦截所有action结尾的请求。

ProductForm.jsp

<!DOCTYPE HTML>
<html>
<head>
<title>Add Product Form</title>
<style type="text/css">@import url(css/main.css);</style>
</head>
<body><div id="global"><form action="product_save.action" method="post"><fieldset><legend>Add a product</legend><p><label for="name">Product Name: </label><input type="text" id="name" name="name" tabindex="1"></p><p><label for="description">Description: </label><input type="text" id="description" name="description" tabindex="2"></p><p><label for="price">Price: </label><input type="text" id="price" name="price" tabindex="3"></p><p id="buttons"><input id="reset" type="reset" tabindex="4"><input id="submit" type="submit" tabindex="5" value="Add Product"></p></fieldset>
</form>
</div>
</body>
</html>

ProductDetails.jsp

<!DOCTYPE HTML>
<html>
<head>
<title>Save Product</title>
<style type="text/css">@import url(css/main.css);</style>
</head>
<body>
<div id="global"><h4>The product has been saved.</h4><p><h5>Details:</h5>Product Name: ${product.name}<br/>Description: ${product.description}<br/>Price: ${product.price}</p>
</div>
</body>
</html>

ProductDetail.jsp页面通过模型属性名“product”来访问由SaveProductController传入的Product对象。这里用JSP表达式来显示Product对象的各种属性,后续会详解JSP 的EL表达式。


测试应用

输入URL:
http://localhost:8080/chapter03a/product_input.action

输入相应的数据

提交数据后,url跳转到
http://localhost:8080/chapter03a/product_save.action


View Resolver

上个案例中,页面的跳转通过指定页面的路径来完成的,比如

new ModelAndView("/WEB-INF/jsp/ProductDetails.jsp", "product",product);

其实,Spring MVC为我们提供了视图解析器,负责解析视图,现在我们来改造下。

在SpringMVC的配置文件中配置视图解析器

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

如上视图解析器前缀和后缀两个属性,这样一来,view的路径就缩短了。 比如仅需要ProductDetails,而无需设置/WEB-INF/jsp/ProductDetails.jsp,视图解析器就会自动增加前缀和后缀。

紧接着我们调整下控制器中的页面跳转

InputProductController 修改为

  return new ModelAndView("ProductForm");

SaveProductController修改为

 return new ModelAndView("ProductDetails", "product",product);

重新运行测试,结果同上。


总结

本博文是Spring MVC的入门介绍,我们知道了使用SpringMVC,我们无需编写自己的DispatcherServlet,其传统风格的控制器开发方式是实现控制器接口。 从Spring2.5版本开始,Spring提供了基于注解的方式开发控制器,下篇博文介绍。


源码

代码已提交到github

https://github.com/yangshangwei/SpringMvcTutorialArtisan

Spring MVC-03循序渐进之Spring MVC相关推荐

  1. spring mvc 小结-51cto学院Spring MVC

    一.Spring MVC 基础 Spring mvc 框架 是一个MVC框架,通过实现MVC很好地将数据.业务.展现进行分离,其底层仍然是servlet 要在web.xml 中配置servlet Sp ...

  2. ASP.NET MVC入门到精通——Spring.net-业务层仓储

    本系列目录:ASP.NET MVC4入门到精通系列目录汇总 上一节,我们已经把项目框架的雏形搭建好了,那么现在我来开始业务实现,在业务实现的过程当中,不断的来完善我们现有的框架. 1.假设我们来做一个 ...

  3. Spring MVC-使用Spring Tool Suite IDE搭建Spring MVC开发环境

    Spring MVC 概述 新建Spring MVC Project 分析IDE建立的工程 Maven dependencies configuration Spring MVC configurat ...

  4. Spring Boot——2分钟构建spring web mvc REST风格HelloWorld

    Spring Boot--2分钟构建spring web mvc REST风格HelloWorld 之前有一篇<5分钟构建spring web mvc REST风格HelloWorld>介 ...

  5. spring框架mvc框架_5篇Spring框架书籍,通过MVC学习Spring

    spring框架mvc框架 Spring Framework is one of the most widely used Java EE Frameworks. It's an open sourc ...

  6. Java Spring MVC框架 VIII 之 Spring MVC拦截器

    Java Spring MVC框架 VIII 之 Spring MVC拦截器 Spring MVC拦截器 1.拦截器简介 拦截器是SpringMvc框架提供的功能 它可以在控制器方法运行之前或运行之后 ...

  7. Spring系列(九)- Spring Web MVC 框架

    文章目录 MVC设计模式简介 Spring MVC 工作流程 Spring MVC接口 需求的配置 Spring MVC视图解析器 Controller 注解类型 Spring MVC的转发与重定向 ...

  8. spring struts2 mybatis框架学习总结(mvc三层架构)

    spring struts2 mybatis框架学习总结(mvc三层架构) ssi的框架主要是由struts2,spring以及ibatis组成,他们负责各层之间的交互与协作,从而实现整个web端的功 ...

  9. 【Spring】第三课 Spring框架搭建MVC三层架构

    概念 本文对Spring框架在项目的实际应用和搭建项目框架中的作用和应用的介绍. 1.搭建项目开发环境 本文连接的数据库是mydb,采用的表是account,银行账户信息的数据表,如果读者对该表的结构 ...

  10. 简单的Spring MVC入门程序,对于Spring mvc工作流程的理解,servlet标签和servlet-mapping 理解,视图解析器

    javaweb SpringMvc的组成:jsp,JavaBean,servlet 可以使用Spring所提供的功能 提供了前端控制器DispatcherServlet,不需要细化Servlet 执行 ...

最新文章

  1. 专访周志华、宋继强:高端AI人才要具备哪些素质?深度学习的局限性和未来?...
  2. 知乎网解决HTML5 Placeholder的方案
  3. 解读Java8中ConcurrentHashMap是如何保证线程安全的
  4. HTML+CSS+JavaScript复习笔记持更(八)——CSS3常用属性之列表
  5. centos6.x 搭建K8S环境准备
  6. 面试必会系列 - 5.3 LVS负载均衡
  7. vant input框禁止调用手机键盘_【案例分享】适应网银等密码键盘的解决方案
  8. h5弹框滑动 ios_微信 iOS 版更新:细节大更新,你值得拥有
  9. ltrim函数php,php ltrim函数怎么用?
  10. python多线程threading之阻塞线程(join)线程同步和守护线程(setDaemon(True))实例详解
  11. python3编程入门_python3编程基础之一:操作
  12. mysql中字符串和数字的互转函数
  13. 为什么大家都能听懂普通话
  14. 最简单的又炫的快捷菜单....喜欢的拿起就跑
  15. 易语言编写影视大全的整体思路及ACF浏览器和cheni纯组件列表灵活运用
  16. 【面试题】单链表的操作1
  17. (一)使用 Sliced Sprite 制作 UI 图像
  18. win10装系统输入用户名就卡死
  19. java word 文档合并_Java 合并Word文档
  20. 电脑右键“打开方式”不见了怎样恢复

热门文章

  1. android gridview显示本地图片大小,在Android上的GridView中调整图像大小
  2. pie hist plot boxplot
  3. vmware linux系统 ip,修改虚拟机上Linux系统的IP地址
  4. python字符串替换空格_python - 用pandas中的NaN替换空白值(空格)
  5. 电脑电池修复_笔记本电脑不充电是怎么回事?
  6. 听说你想去大厂看学妹,带你看看作业帮产品经理岗面经
  7. doc es 中type_Elasticsearch(024):es常见的字段映射类型之 连接类型(join type)
  8. tf.where() 详解
  9. Typora操作指南
  10. Mysql无法添加环境变量解决办法