Spring web mvc 和 Struts2 都属于表现层的框架,它是 Spring 框架的一部分。Spring Web MVC 是一种基于Java的实现了 Web MVC 设计模式的请求驱动类型的轻量级 Web 框架,即使用了 MVC 架构模式的思想,将 web 层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring MVC 大大简化了我们日常Web开发。
优点
让我们能非常简单的设计出干净的 Web 层和薄薄的 Web 层;
进行更简洁的 Web 层的开发;
天生与 Spring 框架集成(如 IoC 容器、AOP 等);
提供强大的约定大于配置的契约式编程支持;
能简单的进行 Web 层的单元测试;
支持灵活的 URL 到页面控制器的映射;
非常容易与其他视图技术集成,如 Velocity、FreeMarker 等等,因为模型数据不放在特定的 API 里,而是放在一个 Model 里(Map 数据结构实现,因此很容易被其他框架使用);
非常灵活的数据验证、格式化和数据绑定机制,能使用任何对象进行数据绑定,不必实现特定框架的 API;
提供一套强大的 JSP 标签库,简化 JSP 开发;
支持灵活的本地化、主题等解析;
更加简单的异常处理;
对静态资源的支持;
支持Restful风格。
1、创建工程并创建 SpringMVC 的核心配置文件 springmvc.xml,导入相应 jar 包和约束
2、编写 springmvc.xml 文件
SpringMVC 本身就是 Spring 的子项目,对 Spring 兼容性很好,不需要做很多配置。
这里只配置一个 Controller 扫描就可以了,让 Spring 对页面控制层 Controller 进行管理。
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd ">
<context:component-scan base-package="com.pngyul.springmvc.controller" />
beans>
3、配置 SpringMVC 的前端控制器 DispatcherServlet 在 web.xml 中
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>springmvcdisplay-name>
<welcome-file-list>
<welcome-file>index.htmlwelcome-file>
<welcome-file>index.htmwelcome-file>
<welcome-file>index.jspwelcome-file>
<welcome-file>default.htmlwelcome-file>
<welcome-file>default.htmwelcome-file>
<welcome-file>default.jspwelcome-file>
welcome-file-list>
<servlet>
<servlet-name>springmvc-firstservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springmvc.xmlparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>springmvc-firstservlet-name>
<url-pattern>*.actionurl-pattern>
servlet-mapping>
web-app>
4、编写 pojo 与 jsp 页面
public class Item {
// 商品id
private int id;
// 商品名称
private String name;
// 商品价格
private double price;
// 商品创建时间
private Date createtime;
// 商品描述
private String detail;
set\get方法。。。
}
<body>
<form action="${pageContext.request.contextPath }/item/queryitem.action" method="post">
查询条件:
<table width="100%" border=1>
<tr>
<td><input type="submit" value="查询"/>td>
tr>
table>
商品列表:
<table width="100%" border=1>
<tr>
<td>商品名称td>
<td>商品价格td>
<td>生产日期td>
<td>商品描述td>
<td>操作td>
tr>
<c:forEach items="${itemList }" var="item">
<tr>
<td>${item.name }td>
<td>${item.price }td>
<td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>td>
<td>${item.detail }td>
<td><a href="${pageContext.request.contextPath }/itemEdit.action?id=${item.id}">修改a>td>
tr>
c:forEach>
table>
form>
body>
5、.创建 ItemController
ItemController 是一个普通的 java 类,不需要实现任何接口。需要在类上添加 @Controller 注解,把 Controller 交由 Spring 管理在方法上面添加 @RequestMapping 注解,里面指定请求的 url。其中 “.action” 可以加也可以不加。
@Controller
public class ItemController {
@RequestMapping("/itemList.action")
public ModelAndView queryItemList(){
List- itemList = new ArrayList<>();
itemList.add(new Item("小米 8 SE", 2290,new Date(),"性价比高!"));
itemList.add(new Item("小米 8", 2690,new Date(),"性价比高!大屏幕!"));
itemList.add(new Item("荣耀 10", 2899,new Date(),"性价比高!AI拍摄"));
ModelAndView mAV = new ModelAndView();
mAV.addObject("itemList", itemList);
mAV.setViewName("itemList");
return mAV;
}
}
6、最后测试访问 http://localhost:8080/springmvc/itemList.action,测试结果如下:
1、 用户发送请求至前端控制器DispatcherServlet,前端控制器收到请求后自己不进行处理,而是调用处理器映射器。
2、 DispatcherServlet收到请求后调用HandlerMapping处理器映射器。HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象。
3、 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、 DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
5、 执行处理器(Controller,也叫后端控制器)。返回执行结果,封装在ModelAndView中
6、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器, ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
7、 ViewReslover解析后返回具体View
8、 DispatcherServlet对View进行渲染视图(即将对象模型的数据填充至视图中)。此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;
9、 DispatcherServlet将结果响应给用户
以下组件通常使用框架提供实现:
Handler:处理器
Handler 是继 DispatcherServlet 前端控制器的后端控制器,在 DispatcherServlet 的控制下Handler 对具体的用户请求进行处理。由于 Handler 涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发 Handler。
HandlAdapter:处理器适配器
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
ViewResolver:视图解析器
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
说明:在 springmvc 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 springmvc 的三大组件。需要用户开发的组件有 handler、view
DispatcherServlet 是前端控制器设计模式的实现,提供 Spring Web MVC 的集中访问点,主要负责职责的分派(在 web.xml 中配置),具体配置已在上面入门教程中书写。
DispatcherServlet 的默认配置(和 DispatcherServlet 类 在一个包下,如下图最后一行)在 DispatcherServlet.properties 中,而且是当 Spring 配置文件中没有指定配置时使用的默认策略。
DispatcherServlet 在启动时会自动注册这些默认的 Bean,无需我们注册,如果我们注册了,默认的将不会注册。因此我们入门案例中就没有配置 BeanNameUrlHandlerMapping、SimpleControllerHandlerAdapterDispatcherServlet,默认会注册这两个 Bean。里面还有一些其他的默认 Bean,我们可以自行去了解。
Controller 控制器,其实现在大多数公司已经不推荐使用实现 Controller 接口的方法来实现Controller,基本都使用注解。但难免还会有一些比较老的系统使用,了解更多,可以访问 http://jinnianshilongnian.iteye.com/blog/1752171。
Spring 2.5 之前,我们都是通过实现 Controller 接口或其实现来定义我们的处理器类。Spring2.5 引入注解式处理器支持,通过 @Controller 和 @RequestMapping 注解定义我们的处理器类。并且提供了一组强大的注解。
Spring 2.5 需要通过处理器映射 DefaultAnnotationHandlerMapping 和处理器适配器 AnnotationMethodHandlerAdapter 来开启支持 @Controller 和 @RequestMapping 注解的处理器。一般不用指定,因为默认的有。
Spring 3.1 引入新的 @Contoller 和 @RequestMapping 注解支持类:处理器映射 RequestMappingHandlerMapping 和处理器适配器 RequestMappingHandlerAdapter 组合来代替 Spring 2.5 开始的处理器映射 DefaultAnnotationHandlerMapping 和处理器适配器 AnnotationMethodHandlerAdapter。但需要我们自己配置。
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> bean>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">bean>
我们还可以在 springmvc.xml 配置文件中使用
替代注解映射器 RequestMappingHandlerAdapter 和适配器 RequestMappingHandlerMapping 的配置。
视图解析器使用 SpringMVC 框架默认的 InternalResourceViewResolver,这个视图解析器支持 JSP 视图解析在 springmvc.xml 配置文件中配置如下:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/">property>
<property name="suffix" value=".jsp">property>
bean>
逻辑视图名需要在 controller 中返回值中指定,比如逻辑视图名为 ItemList,则最终返回的 jsp 视图地址:
“WEB-INF/jsp/itemList.jsp”
也就是最终jsp物理地址:前缀+逻辑视图名+后缀
其整合代码量有点多,所以笔者单独写了一篇博客关于 SpringMVC 整合 MyBatis 点击这里。
SpringMVC 为我们提供了很多默认支持的参数类型,只要我们在处理器方法的形参中添加这些参数类型,Spring MVC 会自动为这些参数赋值,我们来看一下都有哪些。
ServletRequest/HttpServletRequest 和 ServletResponse/HttpServletResponse
InputStream/OutputStream 和 Reader/Writer :requestBodyIn:获取请求的内容区字节流,等价于 request.getInputStream();responseBodyOut:获取相应的内容区字节流,等价于 response.getOutputStream()。
WebRequest/NativeWebRequest:WebRequest 是 Spring Web MVC 提供的统一请求访问接口,不仅仅可以访问请求相关数据(如参数区数据、请求头数据,但访问不到 Cookie 区数据),还可以访问会话和上下文中的数据;NativeWebRequest 继承了 WebRequest,并提供访问本地 Servlet API 的方法。
HttpSession
Model、Map、ModelMap:Spring Web MVC 提供 Model、Map 或 ModelMap 让我们能去暴露渲染视图需要的模型数据。除了 ModelAndView 以外,还可以使用 Model 来向页面传递数据,Model 是一个接口,在参数里直接声明 model 即可。如果使用 Model 可以不使用 ModelAndView 对象,Model 对象可以向页面传递数据,View 对象则可以使用 String 返回值替代。
HttpServletRequest 和 HttpServletResponse
@RequestMapping("/itemEdit0.action")
public ModelAndView queryItemById(HttpServletRequest request,HttpServletResponse response){
String idString = request.getParameter("id");
Integer id = Integer.valueOf(idString);
Items item = itemsService.queryItemById(id);
ModelAndView mAV = new ModelAndView();
mAV.addObject("item", item);
mAV.setViewName("editItem");
return mAV;
}
ModelMap Model
ModelMap是Model接口的实现类,也可以通过ModelMap向页面传递数据。
使用Model和ModelMap的效果一样,如果直接使用Model,springmvc会实例化ModelMap
@RequestMapping("/itemEdit.action")
public String queryItemById0(HttpServletRequest request,Model modle){
String idString = request.getParameter("id");
Integer id = Integer.valueOf(idString);
Items item = itemsService.queryItemById(id);
modle.addAttribute("item",item);
return "editItem";
}
参数类型推荐使用包装数据类型,因为基础数据类型不可以为null
整形:Integer、int
字符串:String
单精度:Float、float
双精度:Double、double
布尔型:Boolean、boolean
1、不使用注解情况下,如果参数名称和 jsp 页面中的表单项的 name 一样,那么可以直接接收参数。
@RequestMapping("/itemEdit.action")
public String queryItemById0(Integer id,Model modle){
Items item = itemsService.queryItemById(id);
modle.addAttribute("item",item);
return "editItem";
}
2、@RequestParam 用来绑定单个请求参数值,可以指定一些属性。
//value:参数名字,即入参的请求参数名字,这也可以解决表单name和我们的形参名字不一样的情况;
//required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报404错误码;
//defaultValue:默认值,表示如果请求中没有同名参数时的默认值。
@RequestMapping("/itemEdit.action")
//将表单项的id值赋给itemId。
public String queryItemById(@RequestParam(value="id",required=true,defaultValue="1")Integer itemId,Model modle){
Items item = itemsService.queryItemById(itemId);
modle.addAttribute("item",item);
return "editItem2";
}
3、@CookieValue 用于将请求的 Cookie 数据映射到功能处理方法的参数上。
public String test(@CookieValue(value="JSESSIONID", defaultValue="") String sessionId)
如上配置将自动将JSESSIONID值入参到sessionId参数上,defaultValue表示Cookie中没有JSESSIONID时默认为空。
4、@RequestHeader 用于将请求的头信息区数据映射到功能处理方法的参数上
@RequestMapping(value="/header")
public String test(
@RequestHeader("User-Agent") String userAgent,
@RequestHeader(value="Accept") String[] accepts)
如上配置将自动将请求头“User-Agent”值入参到userAgent参数上,并将“Accept”请求头值入参到accepts参数上。
1、不使用注解情况下,使用对象接收表单数据,请求的参数名称和 pojo 的属性名称一致,会自动将请求参数赋值给 pojo 的属性。
2、@ModelAttribute 绑定请求参数到对象
public String test1(@ModelAttribute("item") Item item)
它的作用是将该绑定的命令对象以“item”为名称添加到模型对象中供视图页面展示使用。
我们此时可以在视图页面使用${item.id}来获取绑定的命令对象的属性。
//编写一个QueryVo
public class QueryVo {
private Items items;
set/get方法。。。
}
@RequestMapping("/updateItem.action")
public String updateItem(QueryVo queryVo){
itemsService.updateItemById(queryVo.getItems());
return "success";
}
<tr>
<td>商品名称td>
<td><input type="text" name="items.name" value="${item.name }" />td>
tr>
<tr>
<td>商品价格td>
<td><input type="text" name="items.price" value="${item.price }" />td>
tr>
功能要求商品列表页面中的每个商品前有一个 checkbok,选中多个商品后点击删除按钮把商品 id 传递给 Controller,根据商品 id 删除商品信息。
我们演示可以获取 id 的数组即可
public class QueryVo {
private Items items;
private String[] ids;
set/get方法。。。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>查询商品列表title>
head>
<body>
<%-- method="post"> --%>
<form action="${pageContext.request.contextPath }/deleteItem.action" method="post">
查询条件:
<table width="100%" border=1>
<tr>
<td><input type="submit" value="查询"/>td>
tr>
table>
商品列表:
<table width="100%" border=1>
<tr>
<td>选择td>
<td>商品名称td>
<td>商品价格td>
<td>生产日期td>
<td>商品描述td>
<td>操作td>
tr>
<c:forEach items="${itemList }" var="item">
<tr>
<td><input type="checkbox" name="ids" value="${item.id }">td>
<td>${item.name }td>
<td>${item.price }td>
<td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>td>
<td>${item.detail }td>
<td><a href="${pageContext.request.contextPath }/itemEdit.action?id=${item.id}">修改a>td>
tr>
c:forEach>
table>
<input type="submit" value="删除">
form>
body>
html>
//这里就不操作数据库了,直接输出看数据
@RequestMapping("/deleteItem.action")
public String updateItem1(QueryVo queryVo){
System.out.println(queryVo.getIds());
System.out.println(queryVo.getIds().length);
return "success";
}
其实也可以通过直接数组接收
@RequestMapping("/deleteItem.action")
public String updateItem1(Integer[] ids){
System.out.println(ids.length);
return "success";
}
实现商品数据的批量修改。
//使用包装pojo对象接收
public class QueryVo {
private Items items;
private String[] ids;
private List itemList;
set/get方法。。。
}
<c:forEach items="${itemList }" var="item" varStatus="s">
<tr>
<td><input type="checkbox" name="ids" value="${item.id}"/>td>
<td>
<input type="hidden" name="itemList[${s.index}].id" value="${item.id }"/>
<input type="text" name="itemList[${s.index}].name" value="${item.name }"/>
td>
<td><input type="text" name="itemList[${s.index}].price" value="${item.price }"/>td>
<td><input type="text" name="itemList[${s.index}].createtime" value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/>td>
<td><input type="text" name="itemList[${s.index}].detail" value="${item.detail }"/>td>
<td><a href="${pageContext.request.contextPath }/itemEdit.action?id=${item.id}">修改a>td>
tr>
c:forEach>
@RequestMapping("/updateItem.action")
public String updateItem(QueryVo queryVo){
System.out.println(queryVo.getItemsList());
System.out.println(queryVo.getItemsList().size());
return "success";
}
PS:接收List类型的数据必须是 pojo 的属性,如果方法的形参为 ArrayList 类型无法正确接收到数据。
由于日期数据有很多种格式,springmvc 没办法把字符串转换成日期类型。所以需要自定义参数绑定。
前端控制器接收到请求后,找到注解形式的处理器适配器,对 RequestMapping 标记的方法进行适配,并对方法中的形参进行参数绑定。可以在 springmvc 处理器适配器上自定义转换器 Converter 进行参数绑定。
一般使用
注解驱动加载处理器适配器,可以在此标签上进行配置。
1、自定义 Converter
package converter;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.core.convert.converter.Converter;
public class DateConverter implements Converter<String, Date>{
@Override
public Date convert(String source) {
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = simpleDateFormat.parse(source);
return date;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
2、在 springmvc 中配置 converter
<mvc:annotation-driven conversion-service="conversionService">mvc:annotation-driven>
<bean name="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="converter.DateConverter" />
list>
property>
bean>