SpringMVC 核心技术(一)

Spring MVC 简介

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风格。

Spring MVC 简单入门

1、创建工程并创建 SpringMVC 的核心配置文件 springmvc.xml,导入相应 jar 包和约束
SpringMVC 核心技术(一)_第1张图片
SpringMVC 核心技术(一)_第2张图片
SpringMVC 核心技术(一)_第3张图片
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,测试结果如下:
这里写图片描述

Springmvc 架构

SpringMVC 核心技术(一)_第4张图片

架构流程

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将结果响应给用户 

组件说明

以下组件通常使用框架提供实现:

  • DispatcherServlet:前端控制器
    用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。
  • <HandlerMapping:处理器映射器
    HandlerMapping 负责根据用户请求url找到 Handler 即处理器,springmvc 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
  • Handler:处理器
    Handler 是继 DispatcherServlet 前端控制器的后端控制器,在 DispatcherServlet 的控制下Handler 对具体的用户请求进行处理。由于 Handler 涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发 Handler。

  • HandlAdapter:处理器适配器
    通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

  • ViewResolver:视图解析器
    View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。

  • View:视图
    springmvc 框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView 等。我们最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。

说明:在 springmvc 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 springmvc 的三大组件。需要用户开发的组件有 handler、view

DispatcherServlet 详解

DispatcherServlet 是前端控制器设计模式的实现,提供 Spring Web MVC 的集中访问点,主要负责职责的分派(在 web.xml 中配置),具体配置已在上面入门教程中书写。

DispatcherServlet 的默认配置(和 DispatcherServlet 类 在一个包下,如下图最后一行)在 DispatcherServlet.properties 中,而且是当 Spring 配置文件中没有指定配置时使用的默认策略。
SpringMVC 核心技术(一)_第5张图片
DispatcherServlet 在启动时会自动注册这些默认的 Bean,无需我们注册,如果我们注册了,默认的将不会注册。因此我们入门案例中就没有配置 BeanNameUrlHandlerMapping、SimpleControllerHandlerAdapterDispatcherServlet,默认会注册这两个 Bean。里面还有一些其他的默认 Bean,我们可以自行去了解。

Controller

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 整合 MyBatis 点击这里。

SpringMVC 强大的参数绑定

默认参数类型

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参数上。

绑定 pojo 类型

1、不使用注解情况下,使用对象接收表单数据,请求的参数名称和 pojo 的属性名称一致,会自动将请求参数赋值给 pojo 的属性。
2、@ModelAttribute 绑定请求参数到对象

public String test1(@ModelAttribute("item") Item item)  

它的作用是将该绑定的命令对象以“item”为名称添加到模型对象中供视图页面展示使用。
我们此时可以在视图页面使用${item.id}来获取绑定的命令对象的属性。 

绑定包装 pojo 类型

//编写一个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>

数组、List 类型的参数绑定

数组类型的参数绑定

功能要求商品列表页面中的每个商品前有一个 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> 
<%-- 
"${pageContext.request.contextPath }/item/queryItemById.action" 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";
}
List 类型的参数绑定

实现商品数据的批量修改。

//使用包装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>

你可能感兴趣的:(Spring,MVC)