当前位置: 代码迷 >> 综合 >> Spring5 mvc篇
  详细解决方案

Spring5 mvc篇

热度:99   发布时间:2023-12-22 04:58:36.0

目录

  • 第一章 SpringMVC的概述
  • 第二章 SpringMVC的原理
  • 第三章 SpringMVC的入门
    • 3.1、项目创建
    • 3.2、环境准备
  • 第四章 SpringMVC的控制器对象
  • 第五章 SpringMVC的请求映射
    • 5.1、位置
    • 5.2、属性
      • 5.2.1、value
      • 5.2.2、method
      • 5.2.3、params
      • 5.2.4、headers
      • 5.2.5、consumes
      • 5.2.6、produces
  • 第六章 SpringMVC的控制器方法
    • 6.1、控制器方法的参数
      • 6.1.1、接收单个参数
      • 6.1.2、接收多个参数
      • 6.1.3、接收对象数据类型
      • 6.1.4、接收集合数据类型
        • 6.1.4.1、array数组
        • 6.1.4.2、list集合
        • 6.1.4.3、set集合
        • 6.1.4.4、map集合
      • 6.1.5、接收请求头的参数
        • 6.1.5.1、获取指定请求头
        • 6.1.5.2、获取所有请求头
      • 6.1.6、接收Cookie的参数
      • 6.1.7、接收矩阵变量参数
      • 6.1.8、解决中文乱码问题
    • 6.2、控制器方法的返回值
      • 6.2.1、返回值为 ModeAndView
      • 6.2.2、返回值为 String
      • 6.2.3、返回值为 Void
      • 6.2.4、返回值为 Object
  • 第七章 SpringMVC的页面跳转
    • 7.1、请求转发
    • 7.2、重定向
  • 第八章 SpringMVC的拦截器
    • 8.1、拦截器概述
    • 8.2、拦截器定义
    • 8.3、拦截器注册
    • 8.4、单个拦截器执行顺序
    • 8.5、多个拦截器执行顺序
  • 第九章 SpringMVC的国际化
  • 第十章 SpringMVC的异常处理
    • 10.1、异常处理概述
    • 10.2、自定义异常类
    • 10.3、定义全局异常处理
    • 10.4、定义全局错误页面
    • 10.5、配置组件扫描异常
    • 10.6、定义错误方法测试
  • 第十一章 SpringMVC的文件上传
  • 第十二章 SpringMVC的跨域解决
  • 第十三章 SpringMVC的细节处理
    • 13.1、提交表单中有日期
    • 13.2、返回数据中有日期
    • 13.3、视图快捷导航标签
    • 13.4、url-pattern的写法


配套资料,免费下载
链接:https://pan.baidu.com/s/14AOFoYKdu-QDAFf_3GvwqQ
提取码:7hqi
复制这段内容后打开百度网盘手机App,操作更方便哦

第一章 SpringMVC的概述

三层架构

三层架构的介绍:

我们的开发架构一般都是基于两种形式,一种是C/S架构,也就是客户端/服务器,另一种是B/S架构,也就是浏览器/服务器。在JavaEE开发中,几乎全都是基于B/S架构的开发。那么在B/S架构中,系统标准的三层架构包括:用户界面层(User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer)。区分层次的目的就是为了解除各个模块之间的耦合关系,提高代码复用性。

image-20201224144856016

三层架构的优势:

  • 结构清晰,耦合度低
  • 可维护性高,可扩展性高
  • 利于开发任务同步进行,容易适应需求变化

三层架构的劣势:

  • 降低了系统的性能
  • 有时会导致级联的修改
  • 增加了代码量,增加了工作量

各层之间的方案:

  • 用户界面层: Spring MVC、Struts 2
  • 业务逻辑层: Spring
  • 数据访问层: Hibernate、MyBatis、JdbcTemplate

MVC

MVC最开始是存在于桌面程序中的,全名是Model View Controller,是数据模型(model)-用户界面(view)-控制器(controller)的缩写,它是一种软件设计模式。使用MVC的目的在于将M(数据模型)和V(用户界面)的实现代码分离,从而使同一个程序可以使用不同的表现形式。C(控制器)存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新。

img

我用Windows的计算器小程序为例,解释一下MVC模式,虽然它不一定使用这个模式编写。

在这个计算器程序中,外部的那些按钮和最上面的显示条,就是"视图层",那些需要运算的数字就是"数据层",执行加减乘除的那些内部运算步骤就是"控制层",每一层执行不同的功能,整个程序的结构非常清楚。

Spring MVC

“ Spring Web MVC”通常简称为“ Spring MVC”,他是基于Servlet API构建的原始Web框架,从一开始就已包含在Spring框架中,专门是做Web 开发的,Spring MVC内部也维护了一个容器,被称为Spring MVC容器,他是IOC容器的一个子类,既然是子类,Spring MVC容器是可以访问IOC中管理的对象的。

第二章 SpringMVC的原理

请求流程:

20201224161654

父子容器:

第三章 SpringMVC的入门

3.1、项目创建

image-20201224162542931

image-20201224162633149

image-20201224162657388

image-20201224162954387

修改 web.xml

<?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_4_0.xsd"version="4.0"></web-app>

修改 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.caochenlei</groupId><artifactId>springmvc-demo-01</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><build><resources><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include><include>**/*.conf</include></includes><filtering>false</filtering></resource><resource><directory>src/main/resources</directory><includes><include>**/*.properties</include><include>**/*.xml</include><include>**/*.conf</include></includes><filtering>false</filtering></resource></resources></build>
</project>

3.2、环境准备

导入依赖:

<!--导入SpringMVC依赖-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.2</version>
</dependency>
<!--导入JSON处理工具包-->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.12.0</version>
</dependency>
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.0</version>
</dependency>
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId><version>2.12.0</version>
</dependency>
<!--Servlet、JSP相关依赖-->
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope>
</dependency>
<dependency><groupId>javax.servlet.jsp</groupId><artifactId>javax.servlet.jsp-api</artifactId><version>2.3.3</version><scope>provided</scope>
</dependency>
<!--JSP相关标签依赖-->
<dependency><groupId>taglibs</groupId><artifactId>standard</artifactId><version>1.1.2</version>
</dependency>
<dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version>
</dependency>

创建容器:

<listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-ioc.xml</param-value>
</context-param><servlet><servlet-name>spring-mvc</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><load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>spring-mvc</servlet-name><url-pattern>/*.do</url-pattern>
</servlet-mapping>

添加类包:

  • com.caocheneli.spring5.pojo
  • com.caocheneli.spring5.dao
  • com.caocheneli.spring5.service
  • com.caocheneli.spring5.controller

添加配置: spring-ioc.xml

xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
<!--开启组件扫描-->
<context:component-scan base-package="com.caocheneli.spring5"><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

添加配置: spring-mvc.xml

xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
<!--开启组件扫描-->
<context:component-scan base-package="com.caocheneli.spring5.controller"></context:component-scan>

创建控制器: HelloController

@Controller
public class HelloController {
    @RequestMapping("/hello.do")public void hello() {
    System.out.println("Hello SpringMVC");}
}

打开浏览器: http://localhost:8080/hello.do

image-20201224194914465

打开控制台:

image-20201224195008391

第四章 SpringMVC的控制器对象

Spring MVC提供了一个基于注解的编程模型,使用 @Controller 组件来标识当前类为控制器类。

@Controller
public class HelloController {
    @RequestMapping("/hello.do")public void hello() {
    System.out.println("Hello SpringMVC");}
}

第五章 SpringMVC的请求映射

@RequestMapping 是一个用来处理请求地址映射的注解,可用于类或方法上。

5.1、位置

用在控制器类上面: 表示类中的所有响应请求的方法都是以该地址作为父路径

@Controller
@RequestMapping("/hello")
public class HelloController {
    }

用在控制器方法上: 提供进一步的细分映射信息,他跟控制器类上的请求路径组成一个完整的请求路径,如果类上没有使用 @RequestMapping 注解,则父路径就是为空("")。

@Controller
@RequestMapping("/hello")
public class HelloController {
    //完整请求地址:localhost:8080/hello/world.do@RequestMapping("/world.do")public void world() {
    System.out.println("Hello World");}//完整请求地址:localhost:8080/hello/spring.do@RequestMapping("/spring.do")public void spring() {
    System.out.println("Hello Spring");}
}

5.2、属性

5.2.1、value

说明: 指定请求的实际地址。

示例:

第一种形式: 单个请求地址

@RequestMapping(value = "/world.do")
public void todo1() {
    System.out.println("Hello World");
}

第二种形式: 多个请求地址

@RequestMapping(value = {
    "/world.do","/spring.do","/mybatis.do"
})
public void todo2() {
    System.out.println("Hello World");System.out.println("Hello Spring");System.out.println("Hello MyBatis");
}

第三种形式: 地址路径含有变量

@RequestMapping(value = "/spring/{content}.do")
public void todo2(@PathVariable String content) {
    System.out.println("Hello Spring " + content);
}

第四种形式: 地址路径含有正则表达式

@RequestMapping(value = "/spring/{content:[a-z]+}.do")
public void todo3(@PathVariable String content) {
    System.out.println("Hello Spring " + content);
}

5.2.2、method

说明: 指定请求的method类型, GET、POST、PUT、DELETE等。

示例:

只接收GET请求:

@RequestMapping(value = "/get.do", method = RequestMethod.GET)
public void get() {
    System.out.println("get ...");
}
<form action="/hello/get.do" method="get"><input type="submit" value="get请求">
</form>

只接收POST请求:

@RequestMapping(value = "/post.do", method = RequestMethod.POST)
public void post() {
    System.out.println("post ...");
}
<form action="/hello/post.do" method="post"><input type="submit" value="post请求">
</form>

只接收PUT请求:

<!--用于处理PUT、DELETE请求-->
<filter><filter-name>HiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping><filter-name>HiddenHttpMethodFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
@RequestMapping(value = "/put.do", method = RequestMethod.PUT)
public void put() {
    System.out.println("put ...");
}
<form action="/hello/put.do" method="post"><input type="hidden" name="_method" value="put"><input type="submit" value="put请求">
</form>

只接收DELETE请求:

<!--用于处理PUT、DELETE请求-->
<filter><filter-name>HiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping><filter-name>HiddenHttpMethodFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
@RequestMapping(value = "/delete.do", method = RequestMethod.DELETE)
public void delete() {
    System.out.println("delete ...");
} 
<form action="/hello/delete.do" method="post"><input type="hidden" name="_method" value="delete"><input type="submit" value="delete请求">
</form>

GET请求简写形式:

@GetMapping("/get.do")
public void get() {
    System.out.println("get ...");
}

POST请求简写形式:

@PostMapping("/post.do")
public void post() {
    System.out.println("post ...");
}

PUT请求简写形式:

@PutMapping("/put.do")
public void put() {
    System.out.println("put ...");
}

DELETE请求简写形式:

@DeleteMapping("/delete.do")
public void delete() {
    System.out.println("delete ...");
}

5.2.3、params

说明: 指定请求中必须包含某些参数值时,才让该方法处理。

示例:

//设定必须包含username和age两个参数,且age参数不为10(可以有多个参数)
@RequestMapping(value = "/params.do", method = RequestMethod.GET, params = {
    "username", "age!=10"})
public void params() {
    System.out.println("params ...");
}

5.2.4、headers

说明: 指定请求中必须包含某些指定的header值,才能让该方法处理请求。

示例:

@RequestMapping(value = "/headers.do", method = RequestMethod.GET, headers = "Host=localhost:8080")
public void headers() {
    System.out.println("headers ...");
}

5.2.5、consumes

说明: 指定处理请求的提交内容类型 (Content-Type),例如 application/json, text/html。

示例:

@RequestMapping(value = "/consumes.do", method = RequestMethod.POST, consumes = "application/json")
public void consumes() {
    System.out.println("consumes ...");
}
$.ajax({
    url: "/hello/consumes.do",type: "post",headers: {
    "Content-Type": "application/json"}
});

5.2.6、produces

说明: 指定请求响应返回的内容类型(Accept),例如 application/json, text/html。

示例:

@RequestMapping(value = "/produces.do", method = RequestMethod.POST, produces = "application/json")
public void produces() {
    System.out.println("produces ...");
}
$.ajax({
    url: "/hello/produces.do",type: "post",headers: {
    "Accept": "application/json"}
});

第六章 SpringMVC的控制器方法

6.1、控制器方法的参数

核心原理

SpringMVC的控制器方法能接收多少种参数类型,完全取决于参数解析器,我们只讲解常用部分,其余部分可以自己探索。

参数解析器:HandlerMethodArgumentResolver

image-20210101081603764

6.1.1、接收单个参数

第一种形式: 通过参数名称,只要保证请求参数名与该请求处理方法的参数名相同即可。

请求地址:http://localhost:8080/hello/getParam1.do?username=zhangsan

@RequestMapping("/getParam1.do")
public void getParam1(String username) {
    System.out.println("getParam1 " + username);
}

第二种形式: 通过注解获取,只要保证请求参数名与 @RequestParam 注解的参数名相同即可。

请求地址:http://localhost:8080/hello/getParam1.do?username=zhangsan

@RequestMapping("/getParam1.do")
public void getParam1(@RequestParam("username") String name) {
    System.out.println("getParam1 " + name);
}

第三种形式: 通过路径变量,只要保证在路径中的变量名和 @PathVariable 注解的参数名相同即可。

请求地址:http://localhost:8080/hello/zhangsan.do

@RequestMapping("/{username}.do")
public void getParam1(@PathVariable("username") String name) {
    System.out.println("getParam1 " + name);
}

6.1.2、接收多个参数

第一种形式: 通过参数名称,只要保证请求参数名与该请求处理方法的参数名相同即可。

请求地址:http://localhost:8080/hello/getParam2.do?username=zhangsan&password=123546

@RequestMapping("/getParam2.do")
public void getParam2(String username, String password) {
    System.out.println("getParam2 " + username + " " + password);
}

第二种形式: 通过注解获取,只要保证请求参数名与 @RequestParam 注解的参数名相同即可。

请求地址:http://localhost:8080/hello/getParam2.do?username=zhangsan&password=123546

@RequestMapping("/getParam2.do")
public void getParam2(@RequestParam("username") String name, @RequestParam("password") String pwd) {
    System.out.println("getParam2 " + name + " " + pwd);
}

第三种形式: 通过路径变量,只要保证在路径中的变量名和 @PathVariable 注解的参数名相同即可。

请求地址:http://localhost:8080/hello/zhangsan/123456.do

@RequestMapping("/{username}/{password}.do")
public void getParam2(@PathVariable("username") String name, @PathVariable("password") String pwd) {
    System.out.println("getParam2 " + name + " " + pwd);
}

6.1.3、接收对象数据类型

public class User {
    private String name;private String age;public String getName() {
    return name;}public void setName(String name) {
    this.name = name;}public String getAge() {
    return age;}public void setAge(String age) {
    this.age = age;}@Overridepublic String toString() {
    return "User{" +"name='" + name + '\'' +", age='" + age + '\'' +'}';}
}

第一种形式: 通过属性名获取

请求地址:http://localhost:8080/hello/getParam3.do?name=zhangsan&age=18

@RequestMapping(value = "/getParam3.do", method = RequestMethod.GET)
public void getParam3(User user) {
    System.out.println("getParam3 " + user);
}

第二种形式: 通过请求体获取

xmlns:mvc="http://www.springframework.org/schema/mvc"
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
<!--开启MVC注解-->
<mvc:annotation-driven/>
@RequestMapping(value = "/getParam3.do", method = RequestMethod.POST)
public void getParam3(@RequestBody User user) {
    System.out.println("getParam3 " + user);
}
let user = {
    name: "zhangsan",age: 18
};
$.ajax({
    url: "/hello/getParam3.do",type: "post",contentType: "application/json",data: JSON.stringify(user)
});

6.1.4、接收集合数据类型

6.1.4.1、array数组

第一种形式: 通过属性名获取

请求地址:http://localhost:8080/hello/getParam4.do?array=zhangsan&array=lisi

@RequestMapping(value = "/getParam4.do", method = RequestMethod.GET)
public void getParam4(@RequestParam("array") String[] array) {
    System.out.println("getParam4 " + Arrays.toString(array));
}

第二种形式: 通过数组名获取

@RequestMapping(value = "/getParam4.do", method = RequestMethod.POST)
public void getParam4(@RequestParam("array[]") String[] array) {
    System.out.println("getParam4 " + Arrays.toString(array));
}
let arr = ["zhangsan", "lisi"];
$.ajax({
    url: "/hello/getParam4.do",type: "post",data: {
    array: arr}
});

第三种形式: 通过请求体获取

@RequestMapping(value = "/getParam4.do", method = RequestMethod.POST)
public void getParam4(@RequestBody String[] array) {
    System.out.println("getParam4 " + Arrays.toString(array));
}
let arr = ["zhangsan", "lisi"];
$.ajax({
    url: "/hello/getParam4.do",type: "post",contentType: "application/json",data: JSON.stringify(arr)
});

6.1.4.2、list集合

第一种形式: 通过属性名获取

请求地址:http://localhost:8080/hello/getParam5.do?list=zhangsan&list=lisi

@RequestMapping(value = "/getParam5.do", method = RequestMethod.GET)
public void getParam5(@RequestParam("list") List<String> list) {
    System.out.println("getParam5 " + list);
}

第二种形式: 通过数组名获取

@RequestMapping(value = "/getParam5.do", method = RequestMethod.POST)
public void getParam5(@RequestParam("list[]") List<String> list) {
    System.out.println("getParam5 " + list);
}
let list = ["zhangsan", "lisi"];
$.ajax({
    url: "/hello/getParam5.do",type: "post",data: {
    list: list}
});

第三种形式: 通过请求体获取

@RequestMapping(value = "/getParam5.do", method = RequestMethod.POST)
public void getParam5(@RequestBody List<String> list) {
    System.out.println("getParam5 " + list);
}
let list = ["zhangsan", "lisi"];
$.ajax({
    url: "/hello/getParam5.do",type: "post",contentType: "application/json",data: JSON.stringify(list)
});

6.1.4.3、set集合

第一种形式: 通过属性名获取

请求地址:http://localhost:8080/hello/getParam6.do?set=zhangsan&set=lisi

@RequestMapping(value = "/getParam6.do", method = RequestMethod.GET)
public void getParam6(@RequestParam("set") Set<String> set) {
    System.out.println("getParam6 " + set);
}

第二种形式: 通过集合名获取

@RequestMapping(value = "/getParam6.do", method = RequestMethod.POST)
public void getParam6(@RequestParam("set[]") Set<String> set) {
    System.out.println("getParam6 " + set);
}
let set = ["zhangsan", "lisi"];
$.ajax({
    url: "/hello/getParam6.do",type: "post",data: {
    set: set}
});

第三种形式: 通过请求体获取

@RequestMapping(value = "/getParam6.do", method = RequestMethod.POST)
public void getParam6(@RequestBody Set<String> set) {
    System.out.println("getParam6 " + set);
}
let set = ["zhangsan", "lisi"];
$.ajax({
    url: "/hello/getParam6.do",type: "post",contentType: "application/json",data: JSON.stringify(set)
});

6.1.4.4、map集合

第一种形式: 通过键值对获取

请求地址:http://localhost:8080/hello/getParam7.do?name1=zhangsan&name2=lisi

@RequestMapping(value = "/getParam7.do", method = RequestMethod.GET)
public void getParam7(@RequestParam Map<String, String> map) {
    System.out.println("getParam7 " + map);
}

第二种形式: 通过键值对获取

@RequestMapping(value = "/getParam7.do", method = RequestMethod.POST)
public void getParam7(@RequestParam Map<String, String> map) {
    System.out.println("getParam7 " + map);
}
let map = {
    "name1": "zhangsan","name2": "lisi"
};
$.ajax({
    url: "/hello/getParam7.do",type: "post",data: map
});

第三种形式: 通过请求体获取

@RequestMapping(value = "/getParam7.do", method = RequestMethod.POST)
public void getParam7(@RequestBody Map<String, String> map) {
    System.out.println("getParam7 " + map);
}
let map = {
    "name1": "zhangsan","name2": "lisi"
};
$.ajax({
    url: "/hello/getParam7.do",type: "post",contentType: "application/json",data: JSON.stringify(map)
});

6.1.5、接收请求头的参数

6.1.5.1、获取指定请求头

请求地址:http://localhost:8080/hello/getHeaderValue.do

@RequestMapping("/getHeaderValue.do")
public void getHeaderValue(@RequestHeader("User-Agent") String userAgent) {
    System.out.println("getHeaderValue " + userAgent);
}

6.1.5.2、获取所有请求头

请求地址:http://localhost:8080/hello/getHeaderValue.do

@RequestMapping("/getHeaderValue.do")
public void getHeaderValue(@RequestHeader Map<String,String> header) {
    System.out.println("getHeaderValue " + header);
}

6.1.6、接收Cookie的参数

请求地址:http://localhost:8080/hello/getCookieValue.do

@RequestMapping("/getCookieValue.do")
public void getCookieValue(@CookieValue("JSESSIONID") String jsessionid) {
    System.out.println("getCookieValue " + jsessionid);
}

请求地址:http://localhost:8080/hello/getCookieValue.do

@RequestMapping("/getCookieValue.do")
public void getCookieValue(@CookieValue("JSESSIONID") Cookie cookie) {
    System.out.println("getCookieValue " + cookie.getValue());
}

6.1.7、接收矩阵变量参数

修改 web.xml

<servlet><servlet-name>spring-mvc</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><load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>spring-mvc</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>

修改 spring-mvc.xml

<mvc:annotation-driven enable-matrix-variables="true">...
</mvc:annotation-driven>

请求地址:http://localhost:8080/hello/sellCar;low=34;brand=bm,audi,yd

@RequestMapping("/{path}")
public void getHeaderValue(@MatrixVariable("low") Integer low,@MatrixVariable("brand") List<String> brand,@PathVariable("path") String path) {
    System.out.println("low " + low);System.out.println("brand " + brand);System.out.println("path " + path);
}

请求地址:http://localhost:8080/hello/boss1;age=20/emp1;age=10

@RequestMapping("/{bossId}/{empId}")
public void getHeaderValue(@MatrixVariable(value = "age", pathVar = "bossId") Integer bossAge,@MatrixVariable(value = "age", pathVar = "empId") Integer empAge,@PathVariable("bossId") String bossId,@PathVariable("empId") String empId) {
    System.out.println(bossId + " age " + bossAge);System.out.println(empId + " age " + empAge);
}

恢复 spring-mvc.xml

<mvc:annotation-driven>...
</mvc:annotation-driven>

恢复 web.xml

<servlet><servlet-name>spring-mvc</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><load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>spring-mvc</servlet-name><url-pattern>*.do</url-pattern>
</servlet-mapping>

6.1.8、解决中文乱码问题

<!--注册字符集过滤器-->
<filter><filter-name>characterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><!--指定字符集编码--><init-param><param-name>encoding</param-name><param-value>utf-8</param-value></init-param><!--强制Request使用字符集encoding--><init-param><param-name>forceRequestEncoding</param-name><param-value>true</param-value></init-param><!--强制Response使用字符集encoding--><init-param><param-name>forceResponseEncoding</param-name><param-value>true</param-value></init-param>
</filter>
<filter-mapping><filter-name>characterEncodingFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>

6.2、控制器方法的返回值

核心原理

SpringMVC的控制器方法能返回多少种返回值类型,完全取决于返回值处理器,我们只讲解常用部分,其余部分可以自己探索。

返回值处理器:HandlerMethodReturnValueHandler

image-20210101090418479

6.2.1、返回值为 ModeAndView

若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据,此时处理器方法返回 ModelAndView 比较好。当然,若要返回 ModelAndView,则处理器方法中需要定义 ModelAndView 对象。

在使用时,若该处理器方法只是进行跳转而不传递数据,或只是传递数据而并不向任何资源跳转(如对页面的 AJAX 异步响应),此时若返回 ModelAndView,则将总是有一部分多余:要么 Model 多余,要么 View 多余,即此时返回 ModelAndView 将不合适。

请求地址:http://localhost:8080/hello/page.do

@RequestMapping("/page.do")
public ModelAndView page() {
    ModelAndView mv = new ModelAndView();mv.addObject("msg", "Hello World");mv.setViewName("/page.jsp");return mv;
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title></title>
</head>
<body>
<h1>${msg}</h1>
</body>
</html>

但是,细心的你如果注意到,在浏览器地址输入:http://localhost:8080/page.jsp,他也能访问 page.jsp 这是很不安全的,因此,我们建议把所有的 jsp 放到 WEB-INF 文件夹中,这样一来,外部直接输入 http://localhost:8080/page.jsp 就访问不到了,那这时候,我们的视图名称该如何写呢,请参考以下示例:

@RequestMapping("/page.do")
public ModelAndView page() {
    ModelAndView mv = new ModelAndView();mv.addObject("msg", "Hello World");mv.setViewName("/WEB-INF/page.jsp");return mv;
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title></title>
</head>
<body>
<h1>${msg}</h1>
</body>
</html>

我们会觉得,每次都要是写上要跳转视图的物理视图地址,会感觉很麻烦,而且,万一我的所有 jsp 界面都移动到了 WEB-INF/aaa 文件夹中,那我还得需要手动来修改源码中的视图名称,这是不可取的,我们希望,在代码中,只写返回视图的名称,而不是一个物理视图地址,这时候,我们可以使用Spring给我们提供的视图解析器,此时视图名称这个字符串与视图解析器中的 prefix、suffix 相结合,即可形成要访问的 URI。

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/"/><property name="suffix" value=".jsp"/>
</bean>
@RequestMapping("/page.do")
public ModelAndView page() {
    ModelAndView mv = new ModelAndView();mv.addObject("msg", "Hello World");mv.setViewName("page");return mv;
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title></title>
</head>
<body>
<h1>${msg}</h1>
</body>
</html>

6.2.2、返回值为 String

处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址。

如果我们想要携带数据,可以直接在参数中指明数据模型 Model model

@RequestMapping("/page.do")
public String page(Model model) {
    model.addAttribute("msg", "Hello World");return "page";
}

当然,也可以直接返回资源的物理视图名。不过,此时就不需要再在视图解析器中再配置前辍与后辍了。

@RequestMapping("/page.do")
public String page(Model model) {
    model.addAttribute("msg", "Hello World");return "/WEB-INF/page.jsp";
}

6.2.3、返回值为 Void

对于处理器方法返回 void 的应用场景,主要用于服务端直接向浏览器发回数据,所以也就无需视图页面了。使用此种方式可以进行 AJAX 响应。处理器对于 AJAX 请求中所提交的参数,可以使用逐个接收的方式,也可以以对象的方式整体接收。只要保证 AJAX 请求参数与接收的对象类型属性同名。为了方便模拟,我们直接使用GET请求来进行演示,浏览器地址:http://localhost:8080/hello/myVoid.do?name=zhangsan&age=18

image-20201227140903293

@RequestMapping("/myVoid.do")
public void myVoid(String name, String age, HttpServletResponse response) throws IOException {
    //创建用户User user = new User();user.setName(name);user.setAge(age);//转为JSONObjectMapper om = new ObjectMapper();String json = om.writeValueAsString(user);//写到页面PrintWriter pw = response.getWriter();pw.write(json);pw.flush();pw.close();
}

6.2.4、返回值为 Object

处理器方法也可以返回 Object 对象。这个 Object 可以是 Integer,String,自定义对象, Map,List 等。但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。 返回对象,需要使用@ResponseBody 注解,将转换后的 JSON 数据放入到响应体中。

第一步:需要导入相关依赖,之前我们已经导入过了。

<!--导入JSON处理工具包-->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.12.0</version>
</dependency>
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.0</version>
</dependency>
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId><version>2.12.0</version>
</dependency>

第二步:将 Object 数据转化为 JSON 数据,需要由消息转换器 HttpMessageConverter 完成。而转换器的开启,需要由<mvc:annotation-driven/>来完成。 SpringMVC 使用消息转换器实现请求数据和对象,处理器方法返回对象和响应输出之间的自动转换。

<!--开启MVC注解-->
<mvc:annotation-driven/>

第三步:返回自定义类型对象时,不能以对象的形式直接返回给客户端浏览器,而是使用 @ResponseBody 注解将对象转换为 JSON 格式的数据发送给浏览器的。 由于转换器底层使用了 Jackson 转换方式将对象转换为 JSON 数据,所以需要导入 Jackson 的相关 Jar 包。

请求地址:http://localhost:8080/hello/myObject.do?name=zhangsan&age=18

@RequestMapping("/myObject.do")
@ResponseBody
public User myObject(String name, String age) {
    //创建对象User user = new User();user.setName(name);user.setAge(age);//返回对象return user;
}

image-20201227142445316

如果说,我们当前控制器中的所有方法均是返回 JSON 字符串,我们不必在每一个方法上都写一遍 @ResponseBody ,而是直接在类上写,这样就代表了,当前类下所有的方法都是返回的 JSON 字符串。

@Controller
@RequestMapping("/hello")
@ResponseBody
public class HelloController {
    }

当然了,SpringMVC给我们提供了一个复合注解叫做 @RestController ,它是由 @Controller@ResponseBody 复合组成的,目的就是为了简化书写,提高程序阅读。

@RestController
@RequestMapping("/hello")
public class HelloController {
    }

第七章 SpringMVC的页面跳转

当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发与重定向。而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器。 注意,对于请求转发的页面,可以是 WEB-INF 中页面;而重定向的页面,是不能为 WEB-INF 中的页面。因为重定向相当于用户再次发出一次请求,而用户是不能直接访问 WEB-INF 中资源的。

image-20201227144722623

SpringMVC 框架把原来 Servlet 中的请求转发和重定向操作进行了封装。现在可以使用简单的方式实现转发和重定向。

  • forward:表示转发,实现 request.getRequestDispatcher(“xx.jsp”).forward()
  • redirect:表示重定向,实现 response.sendRedirect(“xxx.jsp”)

7.1、请求转发

处理器方法返回 ModelAndView 时,需在 setViewName() 指定的视图前添加 forward:,且此时的视图不再与视图解析器一同工作,这样可以在配置了解析器时指定不同位置的视图。视图页面必须写出相对于项目根的路径。forward 操作不需要视图解析器。处理器方法如果返回 String,在视图路径前面加入 forward: 加上视图完整路径。

请求地址:http://localhost:8080/hello/forward.do

返回值为 ModelAndView :

@RequestMapping("/forward.do")
public ModelAndView forward() {
    ModelAndView mv = new ModelAndView();mv.addObject("msg", "forward ...");mv.setViewName("forward:/WEB-INF/page.jsp");return mv;
}

返回值为 String :

@RequestMapping("/forward.do")
public String forward(Model model) {
    model.addAttribute("msg", "forward ...");return "forward:/WEB-INF/page.jsp";
}

7.2、重定向

处理器方法返回 ModelAndView 时,需在 setViewName() 指定的视图前添加 redirect:,且此时的视图不再与视图解析器一同工作,这样可以在配置了解析器时指定不同位置的视图。视图页面必须写出相对于项目根的路径。redirect 操作不需要视图解析器。处理器方法如果返回 String,在视图路径前面加入 redirect: 加上视图完整路径。

请求地址:http://localhost:8080/hello/redirect.do

返回值为 ModelAndView :

@RequestMapping("/redirect.do")
public ModelAndView redirect() {
    ModelAndView mv = new ModelAndView();System.out.println("redirect ...");mv.setViewName("redirect:/page.jsp");return mv;
}

返回值为 String :

@RequestMapping("/redirect.do")
public String redirect() {
    System.out.println("redirect ...");return "redirect:/page.jsp";
}

第八章 SpringMVC的拦截器

8.1、拦截器概述

SpringMVC 中的 Interceptor 拦截器是非常重要和相当有用的,它的主要作用是拦截指定的用户请求,并进行相应的预处理与后处理。其拦截的时间点在“处理器映射器根据用户提交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器, 在处理器适配器执行处理器之前”。当然,在处理器映射器映射出所要执行的处理器类时, 已经将拦截器与处理器组合为了一个处理器执行链,并返回给了中央调度器。

8.2、拦截器定义

public class MyInterceptor implements HandlerInterceptor {
    @Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
    System.out.println("MyInterceptor preHandle ...");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception {
    System.out.println("MyInterceptor postHandle ...");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {
    System.out.println("MyInterceptor afterCompletion ...");}
}

自定义拦截器,需要实现 HandlerInterceptor 接口。而该接口中含有三个方法:

  • preHandle(request,response, Object handler):该方法在处理器方法执行之前执行。其返回值为 boolean,若为 true,则紧接着会执行处理器方法,且会将 afterCompletion()方法放入到一个专门的方法栈中等待执行。
  • postHandle(request,response, Object handler,modelAndView):该方法在处理器方法执行之后执行。处理器方法若最终未被执行,则该方法不会执行。 由于该方法是在处理器方法执行完后执行,且该方法参数中包含 ModelAndView,所以该方法可以修改处理器方法的处理结果数据,且可以修改跳转方向。
  • afterCompletion(request,response, Object handler, Exception ex):当 preHandle()方法返回 true 时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有工作完成之后才执行该方法。即该方法是在中央调度器渲染(数据填充)了响应页面之后执行的,此时对 ModelAndView 再操作也对响应无济于事。afterCompletion 是最后执行的方法,主要用于清除资源。

8.3、拦截器注册

<!--注册拦截器-->
<mvc:interceptors><mvc:interceptor><mvc:mapping path="/**"/><bean class="com.caocheneli.spring5.interceptor.MyInterceptor"/></mvc:interceptor>
</mvc:interceptors>

8.4、单个拦截器执行顺序

单个拦截器中方法与处理器方法的执行顺序如下图:

image-20201227151916022

8.5、多个拦截器执行顺序

多个拦截器中方法与处理器方法的执行顺序如下图:

image-20201227153356535

第九章 SpringMVC的国际化

第一步:编写国际化资源文件集合,在 resources 目录下,分别建立以下文件

i18n.properties

i18n.username=username:
i18n.password=password:

i18n_zh_CN.properties

i18n.username=账户:
i18n.password=密码:

i18n_en_US.properties

i18n.username=username:
i18n.password=password:

第二步:配置国际化资源文件

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"><property name="defaultEncoding" value="UTF-8"/><property name="useCodeAsDefaultMessage" value="true"/><property name="basenames"><list><value>classpath:i18n</value></list></property>
</bean>

第三步:配置默认区域信息解析器

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"><property name="defaultLocale" value="zh_CN"/>
</bean>

第四步:配置区域设置更改拦截器

<mvc:interceptors>...<!--配置区域设置更改拦截器--><bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"><property name="paramName" value="lang"/></bean>
</mvc:interceptors>

第五步:编写页面跳转控制方法

@RequestMapping("/loginPage.do")
public String loginPage() {
    return "login";
}

第六步:编写需要国际化的页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head><title></title>
</head>
<body>
<form action="/login.do" method="post"><p><spring:message code="i18n.username"/><input type="text" name="username"></p><p><spring:message code="i18n.password"/><input type="text" name="password"></p>
</form>
<p><a href="/hello/loginPage.do?lang=zh_CN">切换为中文</a><a href="/hello/loginPage.do?lang=en_US">切换为英文</a>
</p>
</body>
</html>

第七步:在浏览器地址栏输入http://localhost:8080/hello/loginPage.do进行访问

17

第十章 SpringMVC的异常处理

10.1、异常处理概述

SpringMVC 框架常使用 @ExceptionHandler 注解处理异常。使用注解 @ExceptionHandler 可以将一个方法指定为异常处理方法。该注解只有一个可选属性 value,为一个 Class 数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。而被注解的方法,其返回值可以是 ModelAndView、String、void、自定义Object,方法名随意,方法参数可以是 Exception 及其子类对象、HttpServletRequest、HttpServletResponse 等。系统会自动为这些方法参数赋值。对于异常处理注解的用法,也可以直接将异常处理方法注解于 Controller 之中。

10.2、自定义异常类

UserException

public class UserException extends Exception {
    public UserException() {
    super();}public UserException(String message) {
    super(message);}
}

UserNameException

public class UserNameException extends UserException {
    public UserNameException() {
    super();}public UserNameException(String message) {
    super(message);}
}

UserAgeException

public class UserAgeException extends UserException {
    public UserAgeException() {
    super();}public UserAgeException(String message) {
    super(message);}
}

10.3、定义全局异常处理

GlobalExceptionHandler

//@ControllerAdvice:控制器增强(也就是给控制器类增加异常处理功能)
@ControllerAdvice
public class GlobalExceptionHandler {
    //@ExceptionHandler(异常的class):表示异常的类型,当发生此类型异常时,由当前方法处理@ExceptionHandler(value = UserNameException.class)public ModelAndView doNameException(Exception exception) {
    ModelAndView mv = new ModelAndView();mv.addObject("msg", "姓名不能为空!");mv.addObject("ex", exception);mv.setViewName("error");return mv;}//@ExceptionHandler(异常的class):表示异常的类型,当发生此类型异常时,由当前方法处理@ExceptionHandler(value = UserAgeException.class)public ModelAndView doAgeException(Exception exception) {
    ModelAndView mv = new ModelAndView();mv.addObject("msg", "年龄必须在18-60岁之间!");mv.addObject("ex", exception);mv.setViewName("error");return mv;}//处理其它异常,UserNameException、UserAgeException之外的异常@ExceptionHandlerpublic ModelAndView doOtherException(Exception exception) {
    ModelAndView mv = new ModelAndView();mv.addObject("msg", "系统发生错误,请通知管理员!");mv.addObject("ex", exception);mv.setViewName("error");return mv;}
}

10.4、定义全局错误页面

error.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>error page</title>
</head>
<body>
<p>异常信息:${ex.message}</p>
<p>异常提示:${msg}</p>
</body>
</html>

10.5、配置组件扫描异常

<!--开启组件扫描-->
<context:component-scan base-package="com.caocheneli.spring5.controller"></context:component-scan>
<context:component-scan base-package="com.caocheneli.spring5.interceptor"></context:component-scan><!--开启MVC注解-->
<mvc:annotation-driven/>

10.6、定义错误方法测试

@RequestMapping("/error.do")
public String error(String name, Integer age) throws UserException {
    if (name == null) {
    throw new UserNameException("姓名不正确");}if (age == null || (age < 18 && age > 60)) {
    throw new UserAgeException("年龄不正确");}return "login";
}

请求地址:http://localhost:8080/hello/error.do?name=%E5%BC%A0%E4%B8%89&age=20

image-20201227210638389

请求地址:http://localhost:8080/hello/error.do?age=20

image-20201227210954195

请求地址:http://localhost:8080/hello/error.do?name=%E5%BC%A0%E4%B8%89age=90

image-20201227211034378

第十一章 SpringMVC的文件上传

第一步:创建表单上传页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>upload page</title>
</head>
<body>
<form action="/hello/upload.do" method="post" enctype="multipart/form-data">文件上传:<input type="file" name="file"><input type="submit" value="上传">
</form>
</body>
</html>

第二步:配置多媒体解析器

<!--配置多媒体解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!--设定文件默认编码--><property name="defaultEncoding" value="UTF-8"></property><!--设定文件的最大值(5*1024*1024=5M)--><property name="maxUploadSize" value="5242880"></property><!--设定文件上传时写入内存的最大值,如果小于这个参数不会生成临时文件,默认为10240--><property name="maxInMemorySize" value="40960"></property><!--上传文件的临时路径,目录需要自己手动创建--><property name="uploadTempDir" value="fileUpload/temp"></property><!--是否延迟文件解析--><property name="resolveLazily" value="true"/>
</bean>

第三步:导入文件上传依赖

<!--文件上传相关依赖-->
<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.4</version>
</dependency>

第四步:创建文件上传方法

@RequestMapping(value = "/upload.do", method = RequestMethod.POST)
@ResponseBody
public String upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
    // 文件不为空if (!file.isEmpty()) {
    // 文件存放目录String catalog = "/fileUpload/";// 文件存放路径String path = request.getServletContext().getRealPath(catalog);// 文件存放名称String name = String.valueOf(new Date().getTime() + "_" + file.getOriginalFilename());File destFile = new File(path, name);// 文件保存操作try {
    file.transferTo(destFile);} catch (IllegalStateException | IOException e) {
    e.printStackTrace();}// 文件访问地址String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + catalog + name;// 返回文件地址return "success, the file url is : " + url;} else {
    return "error!";}
}

第五步:测试文件上传是否可用

请求地址:http://localhost:8080/upload.jsp

18

第十二章 SpringMVC的跨域解决

Spring MVC使您可以处理CORS(跨源资源共享)。出于安全原因,浏览器禁止AJAX调用当前来源以外的资源。例如,您可以在一个标签页中拥有您的银行帐户,而在另一个标签页中拥有evil.com。来自evil.com的脚本不应使用您的凭据向您的银行API发出AJAX请求,例如从您的帐户中提取资金!跨域资源共享(CORS)是由大多数浏览器实施的W3C规范,可让您指定授权哪种类型的跨域请求,而不是使用基于IFRAME或JSONP的安全性较低且功能较弱的变通办法。

该 @CrossOrigin 注释能够对带注释的控制器方法跨域请求,如下面的示例所示:

@CrossOrigin
@GetMapping("/getUserInfo.do")
@ResponseBody
public User getUserInfo(){
    User user = new User();user.setName("张三");user.setAge("18");return user;
}

除了细粒度的控制器方法级别配置外,您可能还想定义一些全局CORS配置。

<mvc:cors><mvc:mapping path="/api/**"allowed-origins="https://domain1.com, https://domain2.com"allowed-methods="GET, PUT"allowed-headers="header1, header2, header3"exposed-headers="header1, header2"allow-credentials="true"max-age="3600"/><!--Add more mappings...-->
</mvc:cors>

第十三章 SpringMVC的细节处理

13.1、提交表单中有日期

当form表单中的数据是基本类型的时,直接请求action中的url,一点问题都没有。但是当form表单中有时间类型的数据时,且对应的controller是用一个java对象来绑定对应form提交的数据时,就会出现问题,无法提交成功。解决办法就是在对应的controller中新增下面的方法:

@InitBinder
public void initBinder(WebDataBinder binder) {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");dateFormat.setLenient(false);binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}

13.2、返回数据中有日期

我们经常会返回JSON类型的字符串,有时候,难免会包含一些日期类型,但是我们想要在转换为JSON字符串的时候,就将日期的格式给固定了,那我们可以参考以下做法:

<mvc:annotation-driven><!--处理@ResponseBody里面日期类型--><mvc:message-converters><bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"><property name="objectMapper"><bean class="com.fasterxml.jackson.databind.ObjectMapper"><property name="dateFormat"><bean class="java.text.SimpleDateFormat"><constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss"/></bean></property></bean></property></bean></mvc:message-converters>
</mvc:annotation-driven>

13.3、视图快捷导航标签

当我们为了确保系统安全而把所有的 jsp 页面放到 WEB-INF 目录下时,外界是不能直接进行访问的,我们必需先要定义一个视图跳转的方法,跳转到相对应的界面,但是这样做你不觉得太麻烦了吗,那么,SpringMVC又提供了一种便捷的配置方式来进行访问:

<mvc:view-controller path="/loginPage.do" view-name="login"/>

13.4、url-pattern的写法

  • *.do

    在没有特殊要求的情况下,SpringMVC 的中央调度器 DispatcherServlet 常使用后辍匹配方式,如写为*.do 或者 *.action, *.mvc 等。

  • /

    可以写为 /,如果访问静态资源,这时 DispatcherServlet 会将例如 .css、.js、.jpg 等资源的获取请求,当作是一个普通的控制器请求。中央调度器会调用处理器映射器为其查找相应的处理器。当然也是找不到的,所以在这种情况下,所有的静态资源获取请求也均会报 404 错误。<url-pattern/> 的值并不是说写为/后,静态资源就无法访问了。经过一些配置后,该问题也是可以解决的。

    第一种解决方法:声明 <mvc:default-servlet-handler/> 后 , SpringMVC 框架会在容器中创建 DefaultServletHttpRequestHandler 处理器对象。它会像一个检查员,对进入 DispatcherServlet 的 URL 进行筛查,如果发现是静态资源的请求,就将该请求转由 Web 应用服务器默认的Servlet处理。一般的服务器都有默认的Servlet, 在Tomcat中,有一个专门用于处理静态资源访问的 Servlet 名叫 DefaultServlet。

    第二种解决方法:在 Spring3.0 版本后,Spring 定义了专门用于处理静态资源访问请求的处理器 ResourceHttpRequestHandler。并且添加了标签,专门用于解决静态资源无法访问问题。需要在 springmvc 配置文件中添加如下形式的配置:

    <mvc:resources mapping="/css/**" location="/css/"/>
    <mvc:resources mapping="/js/**" location="/js/"/>
    <mvc:resources mapping="/images/**" location="/images/"/>
    
  • /*

    / 会拦截所有的请求但是不包括 *.jsp*/* 则是真正会拦截所有请求包括 *.jsp