1、MVC设计模式

MVC 设计不仅限于 Java Web 应用,还包括许多应用,比如前端、PHP、.NET 等语言。之所以那么做的根本原因在于解耦各个模块。

MVC 是 Model、View 和 Controller 的缩写,分别代表 Web 应用程序中的 3 种职责。
模型:用于存储数据以及处理用户请求的业务逻辑。
视图:向控制器提交数据,显示模型中的数据。
控制器:根据视图提出的请求判断将请求和数据交给哪个模型处理,将处理后的有关结果交给哪个视图更新显示。

基于 Servlet 的 MVC 模式的具体实现如下。
模型:一个或多个 JavaBean 对象,用于存储数据(实体模型,由 JavaBean 类创建)和处理业务逻辑(业务模型,由一般的 Java 类创建)。
视图:一个或多个 JSP 页面,向控制器提交数据和为模型提供数据显示,JSP 页面主要使用 HTML 标记和 JavaBean 标记来显示数据。
控制器:一个或多个 Servlet 对象,根据视图提交的请求进行控制,即将请求转发给处理业务逻辑的 JavaBean,并将处理结果存放到实体模型 JavaBean 中,输出给视图显示。

基于 Servlet 的 MVC 模式的流程如图 1 所示。

图 1 JSP 中的 MVC 模式 (https://www.csdn.net/)


2、Spring MVC处理用户请求的完整流程

Spring MVC 框架是高度可配置的,包含多种视图技术,例如 JSP 技术、Velocity、Tiles、iText 和 POI。

Spring MVC 框架并不关心使用的视图技术,也不会强迫开发者只使用 JSP 技术,但教程中使用的视图是 JSP,本节主要介绍 Spring MVC 框架处理用户请求的完整流程和处理中包含的 4 个接口。
Spring MVC 工作流程

Spring MVC 框架主要由 DispatcherServlet、处理器映射、控制器、视图解析器、视图组成,其工作原理如图 1 所示。

Spring MVC工作原理图
图 1 Spring MVC 工作原理图

从图 1 可总结出 Spring MVC 的工作流程如下:
客户端请求提交到 DispatcherServlet。
由 DispatcherServlet 控制器寻找一个或多个 HandlerMapping,找到处理请求的 Controller。
DispatcherServlet 将请求提交到 Controller。
Controller 调用业务逻辑处理后返回 ModelAndView。
DispatcherServlet 寻找一个或多个 ViewResolver 视图解析器,找到 ModelAndView 指定的视图。
视图负责将结果显示到客户端。

Spring MVC接口:
在图 1 中包含 4 个 Spring MVC 接口,即 DispatcherServlet、HandlerMapping、Controller 和 ViewResolver。

Spring MVC 所有的请求都经过 DispatcherServlet 来统一分发,在 DispatcherServlet 将请求分发给 Controller 之前需要借助 Spring MVC 提供的 HandlerMapping 定位到具体的 Controller。

HandlerMapping 接口负责完成客户请求到 Controller 映射。

Controller 接口将处理用户请求,这和 Java Servlet 扮演的角色是一致的。一旦 Controller 处理完用户请求,将返回 ModelAndView 对象给 DispatcherServlet 前端控制器,ModelAndView 中包含了模型(Model)和视图(View)。

从宏观角度考虑,DispatcherServlet 是整个 Web 应用的控制器;从微观考虑,Controller 是单个 Http 请求处理过程中的控制器,而 ModelAndView 是 Http 请求过程中返回的模型(Model)和视图(View)。

ViewResolver 接口(视图解析器)在 Web 应用中负责查找 View 对象,从而将相应结果渲染给客户。


3、第一个Spring MVC应用

本节通过一个简单的 Web 应用 springMVCDemo01 来演示 Spring MVC 入门程序的实现过程。
1)创建 Web 应用并引入 JAR 包
在 MyEclipse 中创建一个名为 springMVCDemo01 的 Web 应用,在 springMVCDemo01 的 lib 目录中添加 Spring MVC 程序所需要的 JAR 包,包括 Spring 的 4 个核心 JAR 包、commons-logging 的 JAR 包以及两个与 Web 相关的 JAR 包(spring-web-3.2.13.RELEASE.jar 和 spring-webmvc-3.2.13. RELEASE.jar)。

另外,在 Spring MVC 应用中使用注解时不要忘记添加 spring-aop-3.2.13.RELEASE.jar 包,添加后的 JAR 包如图 1 所示。

2)在 web.xml 文件中部署 DispatcherServlet
在开发 Spring MVC 应用时需要在 web.xml 中部署 DispatcherServlet,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"version="3.0"><display-name>springMVC</display-name><!-- 部署 DispatcherServlet --><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 表示容器再启动时立即加载servlet --><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><!-- 处理所有URL --><url-pattern>/</url-pattern></servlet-mapping>
</web-app>

上述 DispatcherServlet 的 servlet 对象 springmvc 初始化时将在应用程序的 WEB-INF 目录下查找一个配置文件,该配置文件的命名规则是“servletName-servlet.xml”,例如 springmvc-servlet.xml。

另外,也可以将 Spring MVC 配置文件存放在应用程序目录中的任何地方,但需要使用 servlet 的 init-param 元素加载配置文件。示例代码如下:

<!-- 部署 DispatcherServlet -->
<servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc-servlet.xml</param-value></init-param><!-- 表示容器再启动时立即加载servlet --><load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>

3)创建 Web 应用首页
在 springMVCDemo01 应用的 WebContent 目录下有个应用首页 index.jsp。index.jsp 的代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>未注册的用户,请<a href="${pageContext.request.contextPath }/register"> 注册</a>!<br/>已注册的用户,去<a href="${pageContext.request.contextPath }/login"> 登录</a>!
</body>
</html>

4)创建 Controller 类
在 src 目录下创建 controller 包,并在该包中创建 RegisterController 和 LoginController 两个传统风格的控制器类(实现了 Controller 接口),分别处理首页中“注册”和“登录”超链接的请求。

RegisterController 的具体代码如下:

package controller;
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 LoginController implements Controller {public ModelAndView handleRequest(HttpServletRequest arg0,HttpServletResponse arg1) throws Exception {return new ModelAndView("/WEB-INF/jsp/register.jsp");}
}

LoginController 的具体代码如下:

package controller;
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 RegisterController implements Controller {public ModelAndView handleRequest(HttpServletRequest arg0,HttpServletResponse arg1) throws Exception {return new ModelAndView("/WEB-INF/jsp/login.jsp");}
}

5)创建 Spring MVC 配置文件并配置 Controller 映射信息
传统风格的控制器定义之后,需要在 Spring MVC 配置文件中部署它们(学习基于注解的控制器后不再需要部署控制器)。在 WEB-INF 目录下创建名为 springmvc-servlet.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:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- LoginController控制器类,映射到"/login" -->   <bean name="/login" class="controller.LoginController"/>   <!-- LoginController控制器类,映射到"/register" --><bean name="/register" class="controller.RegisterController"/>
</beans>

6)应用的其他页面
RegisterController 控制器处理成功后跳转到 /WEB-INF/jsp 下的 register.jsp 视图,LoginController 控制器处理成功后跳转到 /WEB-INF/jsp 下的 login.jsp 视图,因此在应用的 /WEB-INF/jsp 目录下应有 register.jsp 和 login.jsp 页面,这两个 JSP 页面的代码在此省略。

7)发布并运行 Spring MVC 应用
在 MyEclipse 中第一次运行 Spring MVC 应用时需要将应用发布到 Tomcat。例如在运行 springMVCDemo01 应用时可以选中应用名称 springMVCDemo01 并右击,然后选择 Run As→Run on Server 命令,打开如图 2 所示的对话框,在对话框中单击 Finish 按钮完成发布并运行。

通过地址“http://localhost:8080/springMVCDemo01”首先访问 index.jsp 页面,如图 2 所示。

在如图 2 所示的页面中,当用户单击“注册”超链接时,根据 springmvc-servlet.xml 文件中的映射将请求转发给 RegisterController 控制器处理,处理后跳转到 /WEB-INF/jsp 下的 register.jsp 视图。同理,当单击“登录”超链接时,控制器处理后转到 /WEB-INF/jsp下的login.jsp 视图。


4、Spring MVC视图解析器

Spring 视图解析器是 Spring MVC 中的重要组成部分,用户可以在配置文件中定义 Spring MVC 的一个视图解析器(ViewResolver),示例代码如下:

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

上述视图解析器配置了前缀和后缀两个属性,因此《第一个Spring MVC应用》教程中的 RegisterController 和 LoginController 控制器类的视图路径仅需提供 register 和 login,视图解析器将会自动添加前缀和后缀。

InternalResourceViewResolver 是 URLBasedViewResolver 的子类,所以 URLBasedViewResolver 支持的特性它都支持。

在实际应用中 InternalResourceViewResolver 也是使用的最广泛的一个视图解析器。那么InternalResourceViewResolver有什么自己独有的特性呢?

单从字面意思来看,我们可以把 InternalResourceViewResolver 解释为内部资源视图解析器,这就是 InternalResourceViewResolver 的一个特性。

InternalResourceViewResolver 会把返回的视图名称都解析为 InternalResourceView 对象,InternalResourceView 会把 Controller 处理器方法返回的模型属性都存放到对应的 request 属性中,然后通过 RequestDispatcher 在服务器端把请求 forword 重定向到目标 URL。

比如在 InternalResourceViewResolver 中定义了 prefix=/WEB-INF/,suffix=.jsp,然后请求的 Controller 处理器方法返回的视图名称为 login,那么这个时候 InternalResourceViewResolver 就会把 login 解析为一个 InternalResourceView 对象,先把返回的模型属性都存放到对应的 HttpServletRequest 属性中,然后利用 RequestDispatcher 在服务器端把请求 forword 到 /WEB-INF/test.jsp。

这就是 InternalResourceViewResolver 一个非常重要的特性,我们都知道存放在 /WEB-INF/ 下面的内容是不能直接通过 request 请求的方式请求到的,为了安全性考虑,我们通常会把 jsp 文件放在 WEB-INF 目录下,而 InternalResourceView 在服务器端跳转的方式可以很好的解决这个问题。


5、@Controller和@RequestMapping注解

在《第一个Spring MVC应用》教程中创建了两个传统风格的控制器,它们是实现 Controller 接口的类。传统风格的控制器不仅需要在配置文件中部署映射,而且只能编写一个处理方法,不够灵活。使用基于注解的控制器具有以下两个优点:
在基于注解的控制器类中可以编写多个处理方法,进而可以处理多个请求(动作),这就允许将相关的操作编写在同一个控制器类中,从而减少控制器类的数量,方便以后的维护。
基于注解的控制器不需要在配置文件中部署映射,仅需要使用 RequestMapping 注释类型注解一个方法进行请求处理。

在 Spring MVC 中最重要的两个注解类型是 Controller 和 RequestMapping,本节将重点介绍它们。在本节将创建一个 Spring MVC 应用 springMVCDemo02 来演示相关知识,springMVCDemo01 的 JAR 包、web.xml 与 springMVCDemo02 应用的 JAR 包、web.xml 完全一样。

Controller 注解类型
在 Spring MVC 中使用 org.springframework.stereotype.Controller 注解类型声明某类的实例是一个控制器。例如,在 springMVCDemo02 应用的 src 目录下创建 controller 包,并在该包中创建 Controller 注解的控制器类 IndexController,示例代码如下:

package controller;
import org.springframework.stereotype.Controller;
/**
* “@Controller”表示 IndexController 的实例是一个控制器
*
* @Controller相当于@Controller(@Controller) 或@Controller(value="@Controller")
*/
@Controller
public class IndexController {// 处理请求的方法
}

在 Spring MVC 中使用扫描机制找到应用中所有基于注解的控制器类,所以,为了让控制器类被 Spring MVC 框架扫描到,需要在配置文件中声明 spring-context,并使用 <context:component-scan/> 元素指定控制器类的基本包(请确保所有控制器类都在基本包及其子包下)。

例如,在 springMVCDemo02 应用的 /WEB-INF/ 目录下创建配置文件 springmvc-servlet.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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 使用扫描机制扫描控制器类,控制器类都在controller包及其子包下 --><context:component-scan base-package="controller" /><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/" /><property name="suffix" value=".jsp" /></bean>
</beans>

RequestMapping 注解类型
在基于注解的控制器类中可以为每个请求编写对应的处理方法。那么如何将请求与处理方法一一对应呢?

需要使用 org.springframework.web.bind.annotation.RequestMapping 注解类型将请求与处理方法一一对应。

1)方法级别注解
方法级别注解的示例代码如下:

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* “@Controller”表示 IndexController 的实例是一个控制器
*
* @Controller相当于@Controller(@Controller) 或@Controller(value="@Controller")
*/
@Controller
public class IndexController {@RequestMapping(value = "/index/login")public String login() {/*** login代表逻辑视图名称,需要根据Spring MVC配置* 文件中internalResourceViewResolver的前缀和后缀找到对应的物理视图*/return "login";}@RequestMapping(value = "/index/register")public String register() {return "register";}
}

上述示例中有两个 RequestMapping 注解语句,它们都作用在处理方法上。注解的 value 属性将请求 URI 映射到方法,value 属性是 RequestMapping 注解的默认属性,如果只有一个 value 属性,则可以省略该属性。

用户可以使用如下 URL 访问 login 方法(请求处理方法),在访问 login 方法之前需要事先在 /WEB-INF/jsp/ 目录下创建 login.jsp。
http://localhost:8080/springMVCDemo02/index/login

2)类级别注解
类级别注解的示例代码如下:

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class IndexController {@RequestMapping("/login")public String login() {return "login";}@RequestMapping("/register")public String register() {return "register";}
}

在类级别注解的情况下,控制器类中的所有方法都将映射为类级别的请求。用户可以使用如下 URL 访问 login 方法。

http://localhost:8080/springMVCDemo02/index/login

为了方便维护程序,建议开发者采用类级别注解,将相关处理放在同一个控制器类中。例如,对商品的增、删、改、查处理方法都可以放在 GoodsOperate 控制类中。

编写请求处理方法
在控制类中每个请求处理方法可以有多个不同类型的参数,以及一个多种类型的返回结果。

1)请求处理方法中常出现的参数类型
如果需要在请求处理方法中使用 Servlet API 类型,那么可以将这些类型作为请求处理方法的参数类型。Servlet API 参数类型的示例代码如下:

package controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class IndexController {@RequestMapping("/login")public String login(HttpSession session,HttpServletRequest request) {session.setAttribute("skey", "session范围的值");session.setAttribute("rkey", "request范围的值");return "login";}
}

除了 Servlet API 参数类型以外,还有输入输出流、表单实体类、注解类型、与 Spring 框架相关的类型等,这些类型在后续章节中使用时再详细介绍。

其中特别重要的类型是 org.springframework.ui.Model 类型,该类型是一个包含 Map 的 Spring 框架类型。在每次调用请求处理方法时 Spring MVC 都将创建 org.springframework.ui.Model 对象。Model 参数类型的示例代码如下:

package controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class IndexController {@RequestMapping("/register")public String register(Model model) {/*在视图中可以使用EL表达式${success}取出model中的值*/model.addAttribute("success", "注册成功");return "register";}
}

2)请求处理方法常见的返回类型
最常见的返回类型就是代表逻辑视图名称的 String 类型,例如前面教程中的请求处理方法。除了 String 类型以外,还有 ModelAndView、Model、View 以及其他任意的 Java 类型。


6、Spring MVC获取参数

Controller 接收请求参数的方式有很多种,有的适合 get 请求方式,有的适合 post 请求方式,有的两者都适合。下面分别介绍这些方式,读者可以根据实际情况选择合适的接收方式。

通过实体 Bean 接收请求参数
通过一个实体 Bean 来接收请求参数,适用于 get 和 post 提交请求方式。需要注意的是,Bean 的属性名称必须与请求参数名称相同。下面通过具体应用 springMVCDemo02 讲解“通过实体 Bean 接收请求参数”。

1)创建首页面
在 springMVCDemo02 应用的 WebContent 目录下修改 index.jsp 页面,代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>没注册的用户,请<a href="${pageContext.request.contextPath }/index/register"> 注册</a>!<br/>已注册的用户,去<a href="${pageContext.request.contextPath }/index/login"> 登录</a>!
</body>
</html>

2)完善配置文件
完善配置文件 springmvc-servlet.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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 使用扫描机制扫描控制器类,控制器类都在controller包及其子包下 --><context:component-scan base-package="controller" /><mvc:annotation-driven /><!-- annotation-driven用于简化开发的配置,注解DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter --><!-- 使用resources过滤掉不需要dispatcherservlet的资源(即静态资源,例如css、js、html、images)。在使用resources时必须使用annotation-driven,否则resources元素会阻止任意控制器被调用 --><!-- 允许css目录下的所有文件可见 --><mvc:resources location="/css/" mapping="/css/**" /><!-- 允许html目录下的所有文件可见 --><mvc:resources location="/html/" mapping="/html/**" /><!-- 允许css目录下的所有文件可见 --><mvc:resources location="/images/" mapping="/images/**" /><!-- 完成视图的对应 --><!-- 对转向页面的路径解析。prefix:前缀, suffix:后缀 --><beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/" /><property name="suffix" value=".jsp" /></bean>
</beans>

3)创建 POJO 实体类
在 springMVCDemo02 应用的 src 目录下创建 pojo 包,并在该包中创建实体类 UserForm,代码如下:

package pojo;
public class UserForm {private String uname; // 与请求参数名称相同private String upass;private String reupass;// 省略getter和setter方法
}

4)创建控制器类
在 springMVCDemo02 应用的 controller 包中创建控制器类 IndexController 和 UserController。

IndexController 的代码如下:

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class IndexController {@RequestMapping("/login")public String login() {return "login"; // 跳转到/WEB-INF/jsp下的login.jsp}@RequestMapping("/register")public String register() {return "register";}
}

UserController 的代码如下:

package controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.UserForm;
import com.sun.org.apache.commons.logging.Log;
import com.sun.org.apache.commons.logging.LogFactory;
@Controller
@RequestMapping("/user")
public class UserController {// 得到一个用来记录日志的对象,这样在打印信息的时候能够标记打印的是哪个类的信息private static final Log logger = LogFactory.getLog(UserController.class);/*** 处理登录 使用UserForm对象(实体Bean) user接收注册页面提交的请求参数*/@RequestMapping("/login")public String login(UserForm user, HttpSession session, Model model) {if ("zhangsan".equals(user.getUname())&& "123456".equals(user.getUpass())) {session.setAttribute("u", user);logger.info("成功");return "main"; // 登录成功,跳转到 main.jsp} else {logger.info("失败");model.addAttribute("messageError", "用户名或密码错误");return "login";}}/*** 处理注册 使用UserForm对象(实体Bean) user接收注册页面提交的请求参数*/@RequestMapping("/register")public String register(UserForm user, Model model) {if ("zhangsan".equals(user.getUname())&& "123456".equals(user.getUpass())) {logger.info("成功");return "login"; // 注册成功,跳转到 login.jsp} else {logger.info("失败");// 在register.jsp页面上可以使用EL表达式取出model的uname值model.addAttribute("uname", user.getUname());return "register"; // 返回 register.jsp}}
}

5)创建页面视图
在 springMVCDemo02 应用的 /WEB-INF/jsp 目录下创建 register.jsp 和 login.jsp。

register.jsp 的核心代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body><form action="${pageContext.request.contextPath }/user/register" method="post" name="registForm"><table border=1 bgcolor="lightblue" align="center"><tr><td>姓名:</td><td><input class="textSize" type="text" name="uname" value="${uname }" /></td></tr><tr><td>密码:</td><td><input class="textSize" type="password" maxlength="20" name="upass" /></td></tr><tr><td>确认密码:</td><td><input class="textSize" type="password" maxlength="20" name="reupass" /></td></tr><tr><td colspan="2" align="center"><input type="button" value="注册" onclick="allIsNull() " /></td></tr></tab1e></form>
</body>
</html>

在 register.jsp 的代码中使用了 EL 语句“$1{uname}”取出“model.addAttribute(“uname”,user.getUname())”中的值。对于 EL 和 JSTL 的相关知识,读者可参考《JSP教程》。

login.jsp 的核心代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body><form action="${pageContext.request.contextPath }/user/login" method="post"><table><tr><td colspan="2"><img src="${pageContext.request.contextPath }/images/login.gif"></td></tr><tr><td>姓名:</td><td><input type="text" name="uname" class="textSize"></td></tr><tr><td>密码:</td><td><input type="password" name="upass" class="textsize"></td></tr><tr><td colspan="2"><input type="image" src="${pageContext.request.contextPath }/images/ok.gif" onclick="gogo()"><input type="image" src="${pageContext.request.contextPath }/images/cancel.gif" onclick="cancel()"></td></tr></table>${messageError }</form>
</body>
</html>

6)测试应用
运行 springMVCDemo02 应用的首页面,进行程序测试。

通过处理方法的形参接收请求参数
通过处理方法的形参接收请求参数也就是直接把表单参数写在控制器类相应方法的形参中,即形参名称与请求参数名称完全相同。该接收参数方式适用于 get 和 post 提交请求方式。用户可以将“通过实体 Bean 接收请求参数”部分中控制器类 UserController 中 register 方法的代码修改如下:

@RequestMapping("/register")
/**
* 通过形参接收请求参数,形参名称与请求参数名称完全相同
*/
public String register(String uname,String upass,Model model) {if ("zhangsan".equals(uname)&& "123456".equals(upass)) {logger.info("成功");return "login"; // 注册成功,跳转到 login.jsp} else {logger.info("失败");// 在register.jsp页面上可以使用EL表达式取出model的uname值model.addAttribute("uname", uname);return "register"; // 返回 register.jsp}
}

通过 HttpServletRequest 接收请求参数
通过 HttpServletRequest 接收请求参数适用于 get 和 post 提交请求方式,可以将“通过实体 Bean 接收请求参数”部分中控制器类 UserController 中 register 方法的代码修改如下:

@RequestMapping("/register")
/**
* 通过HttpServletRequest接收请求参数
*/
public String register(HttpServletRequest request,Model model) {String uname = request.getParameter("uname");String upass = request.getParameter("upass");if ("zhangsan".equals(uname)&& "123456".equals(upass)) {logger.info("成功");return "login"; // 注册成功,跳转到 login.jsp} else {logger.info("失败");// 在register.jsp页面上可以使用EL表达式取出model的uname值model.addAttribute("uname", uname);return "register"; // 返回 register.jsp}
}

通过 @PathVariable 接收 URL 中的请求参数
通过 @PathVariable 获取 URL 中的参数,控制器类示例代码如下:

@Controller
@RequestMapping("/user")
public class UserController {@RequestMapping("/user")// 必须节method属性/*** 通过@PathVariable获取URL的参数*/public String register(@PathVariable String uname,@PathVariable String upass,Model model) {if ("zhangsan".equals(uname)&& "123456".equals(upass)) {logger.info("成功");return "login"; // 注册成功,跳转到 login.jsp} else {// 在register.jsp页面上可以使用EL表达式取出model的uname值model.addAttribute("uname", uname);return "register"; // 返回 register.jsp}}
}

在访问“http://localhost:8080/springMVCDemo02/user/register/zhangsan/123456”路径时,上述代码自动将 URL 中的模板变量 {uname} 和 {upass} 绑定到通过 @PathVariable 注解的同名参数上,即 uname=zhangsan、upass=123456。

通过 @RequestParam 接收请求参数
通过 @RequestParam 接收请求参数适用于 get 和 post 提交请求方式,可以将“通过实体 Bean 接收请求参数”部分控制器类 UserController 中 register 方法的代码修改如下:

@RequestMapping("/register")
/**
* 通过@RequestParam接收请求参数
*/
public String register(@RequestParam String uname,@RequestParam String upass, Model model) {if ("zhangsan".equals(uname) && "123456".equals(upass)) {logger.info("成功");return "login"; // 注册成功,跳转到 login.jsp} else {// 在register.jsp页面上可以使用EL表达式取出model的uname值model.addAttribute("uname", uname);return "register"; // 返回 register.jsp}
}
通过 @RequestParam 接收请求参数与“通过处理方法的形参接收请求参数”部分的区别如下:当请求参数与接收参数名不一致时,“通过处理方法的形参接收请求参数”不会报 404 错误,而“通过 @RequestParam 接收请求参数”会报 404 错误。
通过 @ModelAttribute 接收请求参数当 @ModelAttribute 注解放在处理方法的形参上时,用于将多个请求参数封装到一个实体对象,从而简化数据绑定流程,而且自动暴露为模型数据,在视图页面展示时使用。而“通过实体 Bean 接收请求参数”中只是将多个请求参数封装到一个实体对象,并不能暴露为模型数据(需要使用 model.addAttribute 语句才能暴露为模型数据,数据绑定与模型数据展示后面教程中会讲解)。通过 @ModelAttribute 注解接收请求参数适用于 get 和 post 提交请求方式,可以将“通过实体 Bean 接收请求参数”中控制器类 UserController 中 register 方法的代码修改如下:
@RequestMapping("/register")
public String register(@ModelAttribute("user") UserForm user) {if ("zhangsan".equals(uname) && "123456".equals(upass)) {logger.info("成功");return "login"; // 注册成功,跳转到 login.jsp} else {logger.info("失败");// 使用@ModelAttribute("user")与model.addAttribute("user",user)的功能相同//register.jsp页面上可以使用EL表达式${user.uname}取出ModelAttribute的uname值return "register"; // 返回 register.jsp}
}

7、Spring MVC的转发与重定向

重定向是将用户从当前处理请求定向到另一个视图(例如 JSP)或处理请求,以前的请求(request)中存放的信息全部失效,并进入一个新的 request 作用域;转发是将用户对当前处理的请求转发给另一个视图或处理请求,以前的 request 中存放的信息不会失效。

转发是服务器行为,重定向是客户端行为。

1)转发过程
客户浏览器发送 http 请求,Web 服务器接受此请求,调用内部的一个方法在容器内部完成请求处理和转发动作,将目标资源发送给客户;在这里转发的路径必须是同一个 Web 容器下的 URL,其不能转向到其他的 Web 路径上,中间传递的是自己的容器内的 request。

在客户浏览器的地址栏中显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。

2)重定向过程
客户浏览器发送 http 请求,Web 服务器接受后发送 302 状态码响应及对应新的 location 给客户浏览器,客户浏览器发现是 302 响应,则自动再发送一个新的 http 请求,请求 URL 是新的 location 地址,服务器根据此请求寻找资源并发送给客户。

在这里 location 可以重定向到任意 URL,既然是浏览器重新发出了请求,那么就没有什么 request 传递的概念了。在客户浏览器的地址栏中显示的是其重定向的路径,客户可以观察到地址的变化。重定向行为是浏览器做了至少两次的访问请求。

在 Spring MVC 框架中,控制器类中处理方法的 return 语句默认就是转发实现,只不过实现的是转发到视图。示例代码如下:

@RequestMapping("/register")
public String register() {return "register";  //转发到register.jsp
}

在 Spring MVC 框架中,重定向与转发的示例代码如下:

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class IndexController {@RequestMapping("/login")public String login() {//转发到一个请求方法(同一个控制器类可以省略/index/)return "forward:/index/isLogin";}@RequestMapping("/isLogin")public String isLogin() {//重定向到一个请求方法return "redirect:/index/isRegister";}@RequestMapping("/isRegister")public String isRegister() {//转发到一个视图return "register";}
}

在 Spring MVC 框架中,不管是重定向或转发,都需要符合视图解析器的配置,如果直接转发到一个不需要 DispatcherServlet 的资源,例如:

return “forward:/html/my.html”;

则需要使用 mvc:resources 配置:

<mvc:resources location="/html/" mapping="/html/**" />


8、@Autowired和@Service依赖注入

在前面学习的控制器中并没有体现 MVC 的 M 层,这是因为控制器既充当 C 层又充当 M 层。这样设计程序的系统结构很不合理,应该将 M 层从控制器中分离出来。

Spring MVC 框架本身就是一个非常优秀的 MVC 框架,它具有依赖注入的优点,可以通过 org.springframework.beans.factory. annotation.Autowired 注解类型将依赖注入到一个属性(成员变量)或方法,例如:

@Autowired
public UserService userService;

在 Spring MVC 中,为了能被作为依赖注入,类必须使用 org.springframework.stereotype.Service 注解类型注明为 @Service(一个服务)。另外,还需要在配置文件中使用 <context:component-scan base-package=“基本包”/> 元素来扫描依赖基本包。

下面《Spring MVC获取参数的几种常见方式》中“登录”和“注册”的业务逻辑处理分离出来,使用 Service 层实现。

首先创建 service 包,在该包中创建 UserService 接口和 UserServiceImpl 实现类。

UserService 接口的具体代码如下:

package service;
import pojo.UserForm;
public interface UserService {boolean login(UserForm user);boolean register(UserForm user);
}

UserServiceImpl 实现类的具体代码如下:

package service;
import org.springframework.stereotype.Service;
import pojo.UserForm;
//注解为一个服务
@Service
public class UserServiceImpl implements UserService {public boolean login(UserForm user) {if ("zhangsan".equals(user.getUname())&& "123456".equals(user.getUpass())) {return true;}return false;}public boolean register(UserForm user) {if ("zhangsan".equals(user.getUname())&& "123456".equals(user.getUpass())) {return true;}return false;}
}

然后在配置文件中添加一个 <context:component-scan base-package=“基本包”/>元素,具体代码如下:

<context:component-scan base-package=“service” />

最后修改控制器类 UserController,具体代码如下:

package controller;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.UserForm;
import service.UserService;
import com.sun.org.apache.commons.logging.Log;
import com.sun.org.apache.commons.logging.LogFactory;
@Controller
@RequestMapping("/user")
public class UserController {// 得到一个用来记录日志的对象,这样在打印信息的时候能够标记打印的是哪个类的信息private static final Log logger = LogFactory.getLog(UserController.class);// 将服务依赖注入到属性userService@Autowiredpublic UserService userService;/*** 处理登录*/@RequestMapping("/login")public String login(UserForm user, HttpSession session, Model model) {if (userService.login(user)) {session.setAttribute("u", user);logger.info("成功");return "main"; // 登录成功,跳转到 main.jsp} else {logger.info("失败");model.addAttribute("messageError", "用户名或密码错误");return "login";}}/*** 处理注册*/@RequestMapping("/register")public String register(@ModelAttribute("user") UserForm user) {if (userService.register(user)) {logger.info("成功");return "login"; // 注册成功,跳转到 login.jsp} else {logger.info("失败");// 使用@ModelAttribute("user")与model.addAttribute("user",user)的功能相同// 在register.jsp页面上可以使用EL表达式${user.uname}取出ModelAttribute的uname值return "register"; // 返回register.jsp}}
}

9、@ModelAttribute的使用

通过 org.springframework.web.bind.annotation.ModelAttribute 注解类型可经常实现以下两个功能:

1)绑定请求参数到实体对象(表单的命令对象)

@RequestMapping("/register")
public String register(@ModelAttribute("user") UserForm user) {if ("zhangsan".equals(uname) && "123456".equals(upass)) {logger.info("成功");return "login";} else {logger.info("失败");return "register";
}

在上述代码中“@ModelAttribute(“user”)UserForm user”语句的功能有两个:

  • 将请求参数的输入封装到 user 对象中。
  • 创建 UserForm 实例。

以“user”为键值存储在 Model 对象中,和“model.addAttribute(“user”,user)”语句的功能一样。如果没有指定键值,即“@ModelAttribute UserForm user”,那么在创建 UserForm 实例时以“userForm”为键值存储在 Model 对象中,和“model.addAtttribute(“userForm”, user)”语句的功能一样。

2)注解一个非请求处理方法
被 @ModelAttribute 注解的方法将在每次调用该控制器类的请求处理方法前被调用。这种特性可以用来控制登录权限,当然控制登录权限的方法有很多,例如拦截器、过滤器等。

使用该特性控制登录权限,创建 BaseController,代码如下所示:

package controller;
import javax.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.ModelAttribute;
public class BaseController {@ModelAttributepublic void isLogin(HttpSession session) throws Exception {if (session.getAttribute("user") == null) {throw new Exception("没有权限");}}
}

创建 ModelAttributeController ,代码如下所示:

package controller;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("/admin")
public class ModelAttributeController {@RequestMapping("/add")public String add() {return "addSuccess";}@RequestMapping("/update")public String update() {return "updateSuccess";}@RequestMapping("/delete")public String delete() {return "deleteSuccess";}
}

在上述 ModelAttributeController 类中的 add、update、delete 请求处理方法执行时,首先执行父类 BaseController 中的 isLogin 方法判断登录权限,可以通过地址“http://localhost:8080/springMVCDemo02/admin/add”测试登录权限。


10、类型转换的意义

本节以一个简单应用(JSP+Servlet)为示例来介绍类型转换的意义。如图 1 所示的添加商品页面用于收集用户输入的商品信息,商品信息包括商品名称(字符串类型 String)、商品价格(双精度浮点类型 double)、商品数量(整数类型 int)。

图 1 添加商品信息的收集页面

addGoods.jsp页面的代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>添加商品信息</title>
</head>
<body><form action="addGoods" method="post"><table border=1 bgcolor="lightblue" align="center"><tr><td>商品名称:</td><td><input class="textSize" type="text" name="goodsname" /></td></tr><tr><td>商品价格:</td><td><input class="textSize" type="text" name="goodsprice" /></td></tr><tr><td>商品数量:</td><td><input class="textSize" type="text" name="goodsnumber" /></td></tr><tr><td colspan="2" align="center"><input type="submit" value="提交" /></td></tr></tab1e></form>
</body>
</html>

希望页面收集到的数据提交到 addGoods 的 Servlet(AddGoodsServlet 类),该 Servlet 将这些请求信息封装成一个 Goods 类的值对象。

Goods 类的代码如下:

package pojo;
public class Goods {private String goodsname;private double goodsprice;private int goodsnumber;// 无参数的构造方法public Goods() {}// 有参数的构造方法public Goods(String goodsname, double goodsprice, int goodsnumber) {super();this.goodsname = goodsname;this.goodsprice = goodsprice;this.goodsnumber = goodsnumber;}// 此处省略了setter和getter方法
}

AddGoodsServlet 类的代码如下:

package servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import pojo.Goods;
public class AddGoodsServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doPost(request, response);}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.setContentType("text/html;charset=utf-8");// 设置编码,防止乱码request.setCharacterEncoding("utf-8");// 获取参数值String goodsname = request.getParameter("goodsname");String goodsprice = request.getParameter("goodsprice");String goodsnumber = request.getParameter("goodsnumber");// 下面进行类型转换double newgoodsprice = Double.parseDouble(goodsprice);int newgoodsnumber = Integer.parseInt(goodsnumber);// 将转换后的数据封装成goods值对象Goods goods = new Goods(goodsname, newgoodsprice, newgoodsnumber);// 将goods值对象传递给数据访问层,进行添加操作,代码省略...}
}

对于上面这个应用而言,开发者需要自己在 Servlet 中进行类型转换,并将其封装成值对象。这些类型转换操作全部手工完成,异常烦琐。

对于 Spring MVC 框架而言,它必须将请求参数转换成值对象类中各属性对应的数据类型——这就是类型转换的意义。


11、类型转换器详解

Spring MVC 框架的 Converter<S,T> 是一个可以将一种数据类型转换成另一种数据类型的接口,这里 S 表示源类型,T 表示目标类型。开发者在实际应用中使用框架内置的类型转换器基本上就够了,但有时需要编写具有特定功能的类型转换器。

内置的类型转换器
在 Spring MVC 框架中,对于常用的数据类型,开发者无须创建自己的类型转换器,因为 Spring MVC 框架有许多内置的类型转换器用于完成常用的类型转换。Spring MVC 框架提供的内置类型转换包括以下几种类型。
1)标量转换器

名称 作用
StringToBooleanConverter String 到 boolean 类型转换
ObjectToStringConverter Object 到 String 转换,调用 toString 方法转换
StringToNumberConverterFactory String 到数字转换(例如 Integer、Long 等)
NumberToNumberConverterFactory 数字子类型(基本类型)到数字类型(包装类型)转换
StringToCharacterConverter String 到 Character 转换,取字符串中的第一个字符
NumberToCharacterConverter 数字子类型到 Character 转换
CharacterToNumberFactory Character 到数字子类型转换
StringToEnumConverterFactory String 到枚举类型转换,通过 Enum.valueOf 将字符串转换为需要的枚举类型
EnumToStringConverter 枚举类型到 String 转换,返回枚举对象的 name 值
StringToLocaleConverter String 到 java.util.Locale 转换
PropertiesToStringConverter java.util.Properties 到 String 转换,默认通过 ISO-8859-1 解码
StringToPropertiesConverter String 到 java.util.Properties 转换,默认使用 ISO-8859-1 编码

2)集合、数组相关转换器

名称 作用
ArrayToCollectionConverter 任意数组到任意集合(List、Set)转换
CollectionToArrayConverter 任意集合到任意数组转换
ArrayToArrayConverter 任意数组到任意数组转换
CollectionToCollectionConverter 集合之间的类型转换
MapToMapConverter Map之间的类型转换
ArrayToStringConverter 任意数组到 String 转换
StringToArrayConverter 字符串到数组的转换,默认通过“,”分割,且去除字符串两边的空格(trim)
ArrayToObjectConverter 任意数组到 Object 的转换,如果目标类型和源类型兼容,直接返回源对象;否则返回数组的第一个元素并进行类型转换
ObjectToArrayConverter Object 到单元素数组转换
CollectionToStringConverter 任意集合(List、Set)到 String 转换
StringToCollectionConverter String 到集合(List、Set)转换,默认通过“,”分割,且去除字符串两边的空格(trim)
CollectionToObjectConverter 任意集合到任意 Object 的转换,如果目标类型和源类型兼容,直接返回源对象;否则返回集合的第一个元素并进行类型转换
ObjectToCollectionConverter Object 到单元素集合的类型转换

类型转换是在视图与控制器相互传递数据时发生的。Spring MVC 框架对于基本类型(例如 int、long、float、double、boolean 以及 char 等)已经做好了基本类型转换。例如,对于 addGoods.jsp 的提交请求,可以由以下处理方法来接收请求参数并处理: ```java package controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class Goodsontroller { @RequestMapping("/addGoods") public String add(String goodsname, double goodsprice, int goodsnumber) { double total = goodsprice * goodsnumber; System.out.println(total); return "success"; } } ``` 注意:在使用内置类型转换器时,请求参数输入值与接收参数类型要兼容,否则会报 400 错误。请求参数类型与接收参数类型不兼容问题需要学习输入校验后才可解决。

自定义类型转换器
当 Spring MVC 框架内置的类型转换器不能满足需求时,开发者可以开发自己的类型转换器。

例如有一个应用 springMVCDemo03 希望用户在页面表单中输入信息来创建商品信息。当输入“apple,10.58,200”时表示在程序中自动创建一个 new Goods,并将“apple”值自动赋给 goodsname 属性,将“10.58”值自动赋给 goodsprice 属性,将“200”值自动赋给 goodsnumber 属性。

springMVCDemo03 应用与 springMVCDemo02 具有相同的 JAR 包、web.xml。

如果想实现上述应用,需要做以下 5 件事:

  • 创建实体类。
  • 创建控制器类。
  • 创建自定义类型转换器类。
  • 注册类型转换器。
  • 创建相关视图。

按照上述步骤采用自定义类型转换器完成需求。

1)创建实体类
在 springMVCDemo03 的 src 目录下创建 pojo 包,并在该包中创建名为 GoodsModel 的实体类,代码如下:

package pojo;
public class GoodsModel {private String goodsname;private double goodsprice;private int goodsnumber;// 省略setter和getter方法
}

2)创建控制器类
在 springMVCDemo03 的 src 目录下创建 controller 包,并在该包中创建名为 ConverterController 的控制器类,代码如下:

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import pojo.GoodsModel;
@Controller
@RequestMapping("/my")
public class ConverterController {@RequestMapping("/converter")/** 使用@RequestParam* ("goods")接收请求参数,然后调用自定义类型转换器GoodsConverter将字符串值转换为GoodsModel的对象gm*/public String myConverter(@RequestParam("goods") GoodsModel gm, Model model) {model.addAttribute("goods", gm);return "showGoods";}
}

3)创建自定义类型转换器类
自定义类型转换器类需要实现 Converter<S,T> 接口,重写 convert(S) 接口方法。

convert(S) 方法的功能是将源数据类型 S 转换成目标数据类型 T。在 springMVCDemo03 的 src 目录下创建 converter 包,并在该包中创建名为 GoodsConverter 的自定义类型转换器类,代码如下:

package converter;
import org.springframework.core.convert.converter.Converter;
import pojo.GoodsModel;
public class GoodsConverter implements Converter<String, GoodsModel> {public GoodsModel convert(String source) {// 创建一个Goods实例GoodsModel goods = new GoodsModel();// 以“,”分隔String stringvalues[] = source.split(",");if (stringvalues != null && stringvalues.length == 3) {// 为Goods实例赋值goods.setGoodsname(stringvalues[0]);goods.setGoodsprice(Double.parseDouble(stringvalues[1]));goods.setGoodsnumber(Integer.parseInt(stringvalues[2]));return goods;} else {throw new IllegalArgumentException(String.format("类型转换失败, 需要格式'apple, 10.58,200 ',但格式是[% s ] ", source));}}
}

4)注册类型转换器
在 springMVCDemo03 的 WEB-INF 目录下创建配置文件 springmvc-servlet.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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 使用扫描机制扫描控制器类,控制器类都在controller包及其子包下 --><context:component-scan base-package="controller" /><!--注册类型转换器GoodsConverter--><bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"><property name="converters"><list><bean class="converter.GoodsConverter"/></list></property></bean><beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/" /><property name="suffix" value=".jsp" /></bean>
</beans>

5)创建相关视图
在 springMVCDemo03 应用的 WebContent 目录下创建信息采集页面 input.jsp,核心代码如下:

<form action="${pageContext.request.contextPath}/my/converter" method= "post">请输入商品信息(格式为apple, 10.58,200):<input type="text" name="goods" /><br><input type="submit" value="提交" />
</form>

在 springMVCDemo03 应用的 /WEB-INF/jsp 目录下创建信息显示页面 showGoods.jsp,核心代码如下:

<body>您创建的商品信息如下:<!-- 使用EL表达式取出model中的goods信息 -->商品名称为:${goods.goodsname }商品价格为:${goods.goodsprice }商品名称为:${goods.goodsnumber }
</body>

最后,使用地址“ http://localhost:8080/springMVCDemo03 /input.jsp ”测试应用。


12、数据格式化详解


13、表单标签库


14、数据绑定和表单标签的应用


15、JSON数据交互


16、拦截器的配置和使用


17、拦截器的执行流程


18、用户登录权限验证案例


19、Spring MVC数据验证


20、Spring MVC验证器


21、Spring MVC验证器应用实例


22、Hibernate-Validator数据验证


23、Hibernate-Validator数据验证实例


24、Java国际化的概念及使用


25、Spring MVC的国际化


26、用户自定义切换语言实例


27、Spring MVC统一异常处理


28、SimpleMappingExceptionResolver类


29、HandlerExceptionResolver接口


30、@ExceptionHandler注解


31、Spring MVC文件上传


32、Spring MVC单文件上传


33、Spring MVC多文件上传


34、Spring MVC文件下载


35、JSP EL表达式的基本语法


36、JSP EL表达式隐含对象


37、JSP标准标签库


38、JSP函数标签库

Spring MVC 详细信息讲解资料相关推荐

  1. Spring MVC 入门示例讲解

    在本例中,我们将使用Spring MVC框架构建一个入门级web应用程序.Spring MVC 是Spring框架最重要的的模块之一.它以强大的Spring IoC容器为基础,并充分利用容器的特性来简 ...

  2. Spring MVC 详细示例教程

    转载自 http://www.cnblogs.com/sunniest/p/4555801.html 一.SpringMVC基础入门,创建一个HelloWorld程序 1.首先,导入SpringMVC ...

  3. 搭建Spring MVC 4开发环境八步走

    Spring MVC作为SpringFrameWork的产品,自诞生之日,就受到广泛开发者的关注,如今Spring MVC在Java中的发展可谓是蒸蒸日上,如今如果再有开发者说,不了解Spring M ...

  4. 三层架构与MVC详细讲解

    一:MVC (开发模式) C : controller 控制层 V:视图成(html,jsp,vue) M:模型层(分两种:第一种service,dao业务模型,第二种entity实体类模型) 二:第 ...

  5. SSM框架超级详细整合记录:Spring+Spring MVC+MyBatis+Maven+MySQL

    1.前言 本文主要对SSM框架整合的过程进行记录,作为之后参考的依据. 1.1.参考文章 Spring代码实例系列-绪论 Spring MVC代码实例系列-绪论 MyBatis代码实例系列-绪论 1. ...

  6. Http请求中Content-Type讲解以及在Spring MVC注解中produce和consumes配置详解

    转载自https://blog.csdn.net/shinebar/article/details/54408020 引言: 在Http请求中,我们每天都在使用Content-type来指定不同格式的 ...

  7. spring mvc学习(24):配置maven环境和创建maven项目(建议收藏,超全超详细)

    1本次歌谣就对如何创建一个maven项目做一个详细的讲解,毕竟卡了我三天,久久不能入眠,也搜了网上很多的博客 都没有顺利的解决maven项目的创建.这篇建议大家收藏,总会用到的.不然大家看网上的博客也 ...

  8. idea junit 测试看不到控制台报错信息_高手都这么给 Spring MVC 做单元测试!

    本章节主要讲解以下两部分内容: 1.Mock 测试简介 2.测试用例演示 一.Mock 测试简介 1.什么是 mock 测试 在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个「虚拟的对象」 ...

  9. Http请求中Content-Type讲解以及在Spring MVC中的应用

    引言: 在Http请求中,我们每天都在使用Content-type来指定不同格式的请求信息,但是却很少有人去全面了解content-type中允许的值有多少,这里将讲解Content-Type的可用值 ...

  10. Spring MVC验证器应用实例(超详细)

    本节使用一个应用 springMVCDemo08 讲解 Spring 验证器的编写及使用.该应用中有一个数据输入页面 addGoods.jsp,效果如图 1 所示. 有一个数据显示页面 goodsLi ...

最新文章

  1. 零基础如何选择适合的Java培训课程
  2. directx修复工具win7_教你安装双系统,win7+win10
  3. JAVA变量path , classpth ,java_home设设置作用和作用
  4. Java 8 - CompletableFuture组合式异步编程
  5. 和华为hr电话面试的反思
  6. 验证二叉搜索树Python解法
  7. Sixpack —— 支持多语言的 A/B 测试框架
  8. ojective-C学习笔记(7)Foundation框架
  9. 发抖音上热门最佳时间 视频MD5修改器苹果手机
  10. lfw人脸识别数据集
  11. 四、python实现粒子群算法
  12. animals中文谐音_动物英语单词发音
  13. 静态库与动态库的区别和使用
  14. linux文件是否锁定,linux 文件锁定
  15. java用下划线分开字母和数字_数字文字中的Java 7下划线
  16. 基于Android+Springboot+Mybatis+Mysql的个人生活APP设计 说明书+项目源码
  17. OllyDebug破解第一个 CM 程序 《Acid burn.exe》
  18. java入门拼图小游戏_【java】JavaFX从零开始实现拼图小游戏
  19. CUDA----.cpp文件和.cu文件应用区别
  20. 嵌入式开发和c/c++编程经验总结

热门文章

  1. 高等数学学习笔记——第八十九讲——高斯公式
  2. 异度之刃2 任务打杂店的帮手去哪获取纯洁石
  3. 一行代码实现随意编辑网页内容
  4. 查看网页最后修改时间方法以及原理简介
  5. Maven(六)Maven传递性和依赖性
  6. 2021寒假MISC打卡DAY1
  7. iOS 13越狱:越狱后如何安装AppSync和afc2越狱补丁
  8. win10安装apache环境
  9. 【STM32知识点】STM32基础知识总结
  10. MySQL基础 - 带搜索条件的查询