1 Star 0 Fork 0

Pipiing / SpringMVC_learns

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

SpringMVC_learn

介绍

以下是我自己整理的关于 尚硅谷 学习视频中SpringMVC的学习笔记,源码可参考SpringMVC/learn中的代码,日常更新学习笔记,让我们一起学习一起进步!!

1、SpringMVC简介

1.1 什么是MVC

MVC是一种软件架构的思想将软件按照模型(Model)、视图(View)、控制器(Controller)来划分

  • M:Model,模型层,指工程中的JavaBean,作用是处理数据
    1. 实体类Bean专门存储业务数据,如Student、User等
    2. 业务处理Bean:指Service或Dao对象,专门用于处理业务逻辑和数据访问
  • V:View,视图层,指工程中的Html、Jsp等页面,作用是与用户进行交互,展示数据
  • C:Controller,控制层,指工程中的Servlet,作用是接收请求和响应游览器

MVC的工作流程:用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller 调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果 找到相应的View视图,渲染数据后最终响应给浏览器

(用户操作 -> View -> Controller -> Model -> Controller -> View ->展示(响应)数据用户)

1.2 什么是SpringMVC

SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案,目前业界普遍选择了 SpringMVC 作为 Java EE 项目 表述层开发首选方案

:三层架构分为表述层(或表示层)、业务逻辑层、数据访问层表述层表示前台页面和后台servlet

1.3 SpringMVC的特点

  • Spring 家族原生产品,与 IOC 容器等基础设施无缝对接
  • 基于原生的Servlet,通过了功能强大的前端控制器DispatcherServlet,对请求和响应进行统一处理
  • 表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
  • 代码清新简洁,大幅度提升开发效率
  • 内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
  • 性能卓著,尤其适合现代大型、超大型互联网项目要求

2、HelloWorld

2.1 搭建SpringMVC框架

  1. 添加Web模块
  2. 设置打包方式:War包
  3. 添加依赖
<dependencies>
  <!-- SpringMVC -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.1</version>
  </dependency>
  <!-- 日志 --> 
  <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
  </dependency>
  <!-- ServletAPI -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
  </dependency>
  <!-- Spring5和Thymeleaf整合包 --> 
  <dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
    <version>3.0.12.RELEASE</version>
  </dependency>
</dependencies>

2.2 配置web.xml

注册SpringMVC的前端控制器DispatcherServlet

2.2.1 默认配置方式

此配置作用下,SpringMVC的配置文件默认位于WEB-INF下,默认名称为<servlet-name>-servlet.xml,例如,以下配置所对应SpringMVC的配置文件位于WEB-INF下,文件名为springMVC-servlet.xml

<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
<servlet>
  <servlet-name>springMVC</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>springMVC</servlet-name>
  <!--
      设置springMVC的核心控制器所能处理的请求的请求路径
      /所匹配的请求可以是/login或.html或.js或.css方式的请求路径
      但是/不能匹配.jsp请求路径的请求
    -->
  <url-pattern>/</url-pattern>
</servlet-mapping>

如果使用前端控制器来处理.jsp,就会使用前端控制器的Servlet来处理,而不会掉用该Jsp所对应的Servlet进行处理,我们之前都是一个请求对应一个Servlet,可是Jsp请求本身就是一个Servlet,所以请求Jsp时已经有一个特定的Servlet来处理该请求啦(就是Jsp本身)

2.2.2 扩展配置方式

可通过init-param标签设置SpringMVC配置文件的位置名称,通过load-on-startup标签设置SpringMVC前端控制器DispatcherServlet的初始化时间

<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
<servlet>
  <servlet-name>SpringMVC</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 -->
  <init-param>
    <!-- contextConfigLocation为固定值 -->
    <param-name>contextConfigLocation</param-name>
    <!-- 使用classpath:表示从类路径查找配置文件,例如maven工程中的src/main/resources -->
    <param-value>classpath:springMVC.xml</param-value>
  </init-param>
  <!-- 
      作为框架的核心组件,在启动过程中有大量的初始化操作要做
      而这些操作放在第一次请求时才执行会严重影响访问速度
      因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时
 	-->
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>SpringMVC</servlet-name>
  <!--
      设置springMVC的核心控制器所能处理的请求的请求路径
      /所匹配的请求可以是/login或.html或.js或.css方式的请求路径
      但是/不能匹配.jsp请求路径的请求
  -->
  <url-pattern>/</url-pattern>
</servlet-mapping>

2.3 创建请求控制器

由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要创建处理具体请求的类,即请求控制器

请求控制器中每一个处理请求的方法称为控制器方法

因为SpringMVC的控制器由一个POJO(普通的Java类)担任,因此需要通过@Controller注解将其标识为一个控制层组件,交给Spring的IoC容器管理,此时SpringMVC才能够识别控制器的存在

@Controller // 表示控制层组件
public class HelloController {}

2.4 创建SpringMVC的配置文件

<!-- 开启组件扫描 -->
<context:component-scan base-package="com.chen.controller"/>

<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
  <property name="order" value="1"/>
  <property name="characterEncoding" value="UTF-8"/>
  <property name="templateEngine">
    <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
      <property name="templateResolver">
        <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
          <!-- 视图前缀 -->
          <property name="prefix" value="/WEB-INF/templates/"/>
          <!-- 视图后缀 -->
          <property name="suffix" value=".html"/>
          <property name="templateMode" value="HTML5"/>
          <property name="characterEncoding" value="UTF-8" />
        </bean>
      </property>
    </bean>
  </property>
</bean>

<!--
   处理静态资源,例如html、js、css、jpg
  若只设置该标签,则只能访问静态资源,其他请求则无法访问
  此时必须设置<mvc:annotation-driven/>解决问题
 -->
<mvc:default-servlet-handler/>

<!-- 开启mvc注解驱动 -->
<mvc:annotation-driven>
  <mvc:message-converters>
    <!-- 处理响应中文内容乱码 -->
    <bean class="org.springframework.http.converter.StringHttpMessageConverter">
      <property name="defaultCharset" value="UTF-8" />
      <property name="supportedMediaTypes">
        <list>
          <value>text/html</value>
          <value>application/json</value>
        </list>
      </property>
    </bean>
  </mvc:message-converters>
</mvc:annotation-driven>

2.5 测试HelloWorld

2.5.1 实现对首页的访问

在请求控制器中创建处理请求的方式

/*
 "index"使用视图前缀加视图后缀可变为 -> "/WEB-INF/templates/index.html"
 @RequestMapping注解:处理请求和控制器方法之间的映射关系
 @RequestMapping注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径:'/'
 localhost:8080/
  */
@RequestMapping("/")
public String index(){
  // 返回视图名称
  return "index";
}
2.5.2 通过超链接跳转指定页面
  1. 在主页index.html中设置超链接
  2. 在请求控制器中创建处理请求的方法
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
		<h1>首页</h1>
		<a th:href="@{/target}">访问目标页面target.html</a>
</body>
</html>
/*
 重点:return的值要和html文件名相同,而注释中的value值要和跳转的href相同
 "target" -> "/WEB-INF/templates/target.html"
  */
public String toTarget(){
  // 返回视图名称
  return "target";
}

2.6 总结

浏览器发送请求,若请求地址符合前端控制器的url-pattern(/:不能匹配.jsp请求的路径请求),该请求就会被前端控制器 DispatcherServlet处理,前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器, 将请求地址和控制器中@RequestMapping注解的value属性值进行匹配(href:@{/target}和@RequestMapping("/target")),若匹配成功,该注解所标识的 控制器方法就是处理请求的方法,处理请求的方法需要返回一个字符串类型视图名称("target"),该视图名称会 被视图解析器解析加上前缀和后缀组成视图的路径("/WEB-INF/templates/target.html"),通过Thymeleaf对视图进行渲染,最终转发到视图所对应页面(localhost:8080/target)

3、@RequestMapping注解

3.1 @RequestMapping注解的功能

  • 从注解名称上我们可以看见,@RequestMapping注解的作用就是将请求和处理请求的控制器方法进行关联,建立映射关系

  • SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求

3.2 @RequestMapping注解的位置

  1. 标识在类上:设置映射请求的请求路径的初始信息
  2. 标识在方法上:设置映射请求请求路径的具体信息
@Controller
@RequestMapping("/hello") // 标识在类:设置映射请求的请求路径的初始信息
public class RequestMappingController {
  /* 
   标识在方法:设置映射请求的请求路径的具体信息
   /test/testRequestMapping
  */
    @RequestMapping("/testRequestMapping") 
    public String success(){
        return "success";
    }
}

3.3 @RequestMapping注解的value属性

  • @RequestMapping注解的value属性通过请求的请求地址匹配请求映射

  • @RequestMapping注解的value属性是一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求

  • @RequestMapping注解的value属性必须设置至少通过请求地址匹配请求映射

<a th:href="@{/test}">测试RequestMapping的value属性 -> /test</a><br>
<a th:href="@{/testRequestMapping}">测试RequestMapping的value属性 -> /testRequestMapping</a><br>
@RequestMapping(
  value = {"/testRequestMapping","/test"}
) // 一个控制器方法可以对应多个请求地址
public String success() {
  return "success";
}

3.4 @RequestMapping注解的method属性

  • @RequestMapping注解的method属性通过请求的请求方式(get或post)匹配请求映射
  • @RequestMapping注解的method属性是一个RequestMethod类型的数组表示该请求映射能够匹配多种请求方式的请求
  • 若当前请求的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则浏览器报错 405:Request method 'POST' not supported
<a th:href="@{/test}">测试RequestMapping注解的method属性 -> GET</a><br>
<form th:action="@{/test}" method="post">
  <input type="submit" value="测试RequestMapping注解的method属性 -> POST">
</form>
@RequestMapping(
  value = {"/testRequestMapping","/test"},
  method = {RequestMethod.GET,RequestMethod.POST}
) // method:匹配对应的请求方式的请求
public String success() {
  return "success";
}

注:

  1. 对于处理指定请求方式的控制器方法,SpringMVC中提供了@RequestMapping的派生注解
    1. 处理get请求的映射 -> @GetMapping
    2. 处理post请求的映射 -> @PostMapping
    3. 处理put请求的映射 -> @PutMapping
    4. 处理delete请求的映射 -> @DeleteMapping
  2. 常用的请求方式有get,post,put,delete

但是目前浏览器只支持get和post请求,若在form表单提交时,设置method为其他请求方式的字符串(put或delete),则按照默认的get请求处理

若要发送put和delete请求,则需要通过Spring提供的过滤器HiddenHttpMethodFilter,在RESTful部分会使用到

<!-- 测试GetMapping和PostMapping注解 -->
<a th:href="@{/testGetMapping}">测试GetMapping注解 -> /testGetMapping</a><br>
<form th:action="@{/testPostMapping}" method="post">
  <input type="submit" value="测试PostMapping注解 -> /testPostMapping">
</form>
@GetMapping("/testGetMapping")
public String testGetMapping(){
  return "success";
}

@PostMapping("/testPostMapping")
public String testPostMapping(){
  return "success";
}

3.5 @RequestMapping注解的params属性(了解)

  • @RequestMapping注解的params属性通过请求的请求参数匹配请求映射
  • @RequestMapping注解的params属性是一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系
  1. param:要求请求映射所匹配的请求必须携带param请求参数
  2. !param:要求请求映射所匹配的请求必须不能携带param请求参数
  3. param=value:要求请求映射所匹配的请求必须携带param请求参数并且param=value
  4. param!=value:要求请求映射所匹配的请求必须携带param请求参数但是param!=value
<a th:href="@{/testParamsAndHeaders(username='Forget',password='202428')}">测试@RequestMapping的 params属性 -> /testParamsAndHeaders</a><br>
// params中的参数必须要同时满足,才能成功匹配请求方法
@RequestMapping(
  value = "/testParamsAndHeaders",
  params = {"username","password=202428"}
)
public String testParamsAndHeaders(){
  return "success";
}

若当前请求满足@RequestMapping注解的value和method属性,但是不满足params属性,此时 页面回报错400:Parameter conditions "username, password!=123456" not met for actual request parameters: username={Forget}, password={123456}

3.6 @RequestMapping注解的headers属性(了解)

  • 用法与param属性一致,一共有四种表达式设置请求头参数和请求映射的匹配关系,并且headers中的参数必须也要同时满足

  • 若当前请求满足@RequestMapping注解的value和method属性,但是不满足headers属性此时页面显示404错误,即资源未找到

3.7 SpringMVC支持ant风格的路径

  • ?:表示任意的单个字符
  • *:表示任意的字符(0个或多个)
  • **:表示任意的0层或多层目录(注意点)

:在使用**时,只能使用/**/xxx的方式

3.8 SpringMVC支持路径中的占位符(重点)

原始方式/deleteUser?user_id=1

rest方式/deleteUser/1

  • SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据,在 通过@PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参
<a th:href="@{/testPath/1/Forget}">测试路径中的占位符RESTful风格 -> /testPath</a><br>
/*
 将占位符中的值,自动赋值给方法中的形参
  */
@RequestMapping("/testPath/{userId}/{userName}")
public String testPath(@PathVariable("userId") Integer id, @PathVariable("userName") String name) {
  System.out.println("userId = " + id + ",userName = " + name);
  return "success";
  // 最终输出的内容为 -> "userId = 1,userName = Forget"
}

4、SpringMVC获取请求参数

4.1 通过ServletAPI获取

  • HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象
/*
 1、通过ServletAPI获取
 形参位置的request表示当前请求
 意味着可以使用原生的ServletAPI获取请求参数
  */
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request) {
  String username = request.getParameter("userName");
  String password = request.getParameter("password");
  System.out.println("username:" + username + ",password:" + password);
  return "success";
}

4.2 通过控制器方法的形参获取请求参数

  • 在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在 DispatcherServlet中就会将请求参数赋值给相应的形参
<a th:href="@{/testParam(username='Forget',password='202428')}">测试使用控制器的形参获取请求参数</a><br>
/*
 2、通过控制器方法的形参获取请求参数
 设置和请求参数同名的形参,DispatcherServlet中就会将请求参数赋值给相应的形参
 若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串数组或者字符串类型的形参接收此请求参数
     -- 若使用字符串数组类型的形参,此参数的数组中包含了每一个数据
     -- 若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果
  */
@RequestMapping("/testParam")
public String testParam(String username, String password, String[] hobby) {
  System.out.println("username:" + username + ",password:" + password + ",hobby:" + Arrays.toString(hobby));
  return "success";
}

请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串数组或者字符串类型的形参接收此请求参数

  • 若使用字符串数组类型的形参,此参数的数组中包含了每一个数据
  • 若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果

4.3 @RequestParam

  • @RequestParam是将请求参数控制器方法的形参创建映射关系
  • @RequestParam注解一共有三个属性:
    1. value:指定为形参赋值的请求参数的参数名
    2. required:设置是否必须传输此请求参数,默认值为true

若设置为true时,则当前请求必须传输value所指定的请求参数若没有传递该请求参数且没有设置defaultValue属性,则页面报错400:Required String parameter 'xxx' is not present

若设置为false时,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参值为null

  • defaultValue:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为""(空)时,则使用默认值为形参赋值

4.4 @RequestHeader

  • @RequestHeader是将请求头信息控制器方法的形参创建映射关系
  • @RequestHeader注解一共有三个属性:value、required、defaultValue,用法同@RequestParam

4.5 @CookieValue

  • @CookieValue是将cookie数据控制器方法的形参创建映射关系
  • @CookieValue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam

4.6 通过Pojo获取请求参数

可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值

<form th:action="@{/testPojo}" method="post">
  用户名:<input type="text" name="username"><br>
  密码:<input type="password" name="password"><br>
  性别:<input type="radio" name="sex" value="男">
  <input type="radio" name="sex" value="女"><br>
  年龄:<input type="text" name="age"><br>
  邮箱:<input type="text" name="email"><br>
  <input type="submit">
</form>
@RequestMapping("/testPojo")
public String testPojo(User user) {
  System.out.println(user);
  return "success";
  // 输出结果:User{username='Forget', password='202428', sex='男', age='20', email='1292379046@qq.com'}
}

4.7 解决获取请求参数的乱码问题

解决获取请求参数的乱码问题,可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter,但是必须在web.xml中进行注册

<!-- 设置字符编码过滤器 -->
<filter>
  <filter-name>CharacterEncodingFilter</filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  <!-- encoding:解决POST请求乱码 -->
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
  <!-- forceEncoding:顺便解决响应乱码 -->
  <init-param>
    <param-name>forceEncoding</param-name>
    <param-value>true</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>CharacterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

:SpringMVC中处理编码的过滤器一定要配置在其他过滤器之前,否则无效

5、域对象共享数据

5.1 使用ServletAPI向request域对象共享数据

/*
 1、使用ServletAPI向request域对象共享数据
  */
@RequestMapping("/testRequestServletAPI")
public String testRequestServletAPI(HttpServletRequest request){
  request.setAttribute("testRequestScope","Hello World ServletAPI");
  return "success";
}

5.2 使用ModelAndView向request域对象共享数据

/*
 2、使用ModelAndView向request域对象共享数据
 ModelAndView有 Model 和 View 的功能
 Model:主要用于向请求域中共享数据
 View:主要用于设置视图,实现页面跳转
  */
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
  // 1、创建ModelAndView对象
  ModelAndView mav = new ModelAndView();
  // 2、向请求域添加共享数据
  mav.addObject("testRequestScope","Hello World ModelAndView");
  // 3、设置视图,实现页面跳转 相当于return "success",前缀+视图名称+后缀进行资源的访问
  mav.setViewName("success");
  return mav;
}

5.3 使用Model向request域对象共享数据

/*
 3、使用Model向request域对象共享数据
  */
@RequestMapping("/testModel")
public String testModel(Model model){
  model.addAttribute("testRequestScope","Hello World Model");
  return "success";
}

5.4 使用map向request域对象共享数据

/*
 4、使用map向request域对象共享数据
 需要在形参中创建map集合,添加到map集合中的数据会成为共享数据
 Map<String,Object> 键为字符串,值为Object(任意)的对象
  */
@RequestMapping("/testMap")
public String testMap(Map<String,Object> map) {
  map.put("testRequestScope","Hello World Map");
  return "success";
}

5.5 使用ModelMap向request域对象共享数据

/*
 5、使用ModelMap向request域对象共享数据
 需要在形参中创建ModelMap对象,使用modelMap的addAttribute()添加共享数据
  */
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
  modelMap.addAttribute("testRequestScope","Hello World ModelMap");
  return "success";
}

5.6 Model、ModelMap、Map的关系

Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的

/* 
BindingAwareModelMap继承于ExtendedModelMap
ExtendedModelMap继承于ModelMap,并且实现Model接口
ModelMap继承于LinkedHashMap
LinkedHashMap实现Map接口
因此在本质上Model、ModelMap、Map都是BindingAwareModelMap类型
*/
public interface Model{}
public class ModelMap extends LinkedHashMap<String, Object> {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class BindingAwareModelMap extends ExtendedModelMap {}

5.7 向session域共享数据

/*
 6、使用原生ServletAPI向session域共享数据
 需要在形参中创建HttpSession对象,使用session的setAttribute()添加共享数据
  */
@RequestMapping("/testSession")
public String testSession(HttpSession session){
  session.setAttribute("testSessionScope","Hello World Session");
  return "success";
}

5.8 向application域共享数据

/*
 7、使用ServletAPI向Application域中共享数据
 需要在形参中创建HttpSession对象,获取Application对象
  */
@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
  // 使用session获取application对象
  ServletContext application = session.getServletContext();
  application.setAttribute("testApplication","Hello World Application");
  return "success";
}

6、SpringMVC的视图

  • SpringMVC中的视图是View接口,视图的作用渲染数据将模型Model中的数据展示给用户
  • SpringMVC视图的种类很多,默认有转发视图(InternalResourceView)重定向视图(RedirectView)
  • 当工程引入jstl的依赖,转发视图自动转换JstlView
  • 若使用的视图技术为Thymeleaf在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView

6.1 ThymeleafView

  • 当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转
/*
 1、测试ThymeleafView
  */
@RequestMapping("/testThymeleafView")
public String testThymeleafView(){
  return "success";
}

image-20220423081229348

6.2 转发视图(InternalResourceView)

  • SpringMVC中默认的转发视图InternalResourceView
  • 当控制器方法中所设置的视图名称以"forward:"为前缀时,创建InternalResourceView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉剩余部分作为最终路径通过转发的方式实现跳转
/*
 2、测试转发视图InternalResourceView
 此时视图名称有forward:前缀是转发视图
 会创建两次视图:"forward:+请求路径"
     -- 第一次是"forward:/testThymeleafView"的转发视图
     -- 第二次是"/testThymeleafView"的Thymeleaf视图
 地址栏不会发生改变:localhost:8080/testForward
 */
@RequestMapping("/testForward")
public String testForward() {
  return "forward:/testThymeleafView";
}

image-20220423081416551

6.3 重定向视图(RedirectView)

  • SpringMVC中默认的重定向视图RedirectView
  • 当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建RedirectView视图,此时的视图名称不 会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉剩余部分作为最 终路径通过重定向的方式实现跳转
/*
 3、测试重定向视图RedirectView
 此时视图名称有redirect:前缀是重定向视图
 会创建两次视图:"redirect:+请求路径"
    -- 第一次是"redirect:/testThymeleafView"的转发视图
    -- 第二次是"/testThymeleafView"的Thymeleaf视图
 地址栏会发生改变:localhost:8080/testThymeleafView
 */
@RequestMapping("/testRedirect")
public String testRedirect() {
  return "redirect:/testThymeleafView";
}

image-20220423081431552

:重定向视图在解析时,会先将redirect:前缀去掉,然后会判断剩余部分是否以/开头若是则会自动拼接上下文路径

补充:转发和重定向的区别?

  1. 转发是发起一次请求,游览器发送再由服务器内部进行转发,重定向是发起两次请求,第一次访问Servlet,第二次访问重定向的地址
  2. 转发不会改变地址栏中的地址重定向会改变地址栏中的地址(重新访问服务器中的资源)
  3. 转发可以访问到request域中的共享数据,因为转发是一次请求,使用的request对象是同一个而重定向不行,因为重定向是两次请求,不是使用的同一个request对象

6.4 视图控制器view-controller

  • 当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view- controller标签进行表示
<!--
设置视图控制器,完成只有页面跳转的功能
path:设置处理的请求地址(相当于RequestMapping("/"))
view-name:设置请求地址所对应的视图名称,如success.html,相当于return "success")
-->
<mvc:view-controller path="/" view-name="index"/>

:当SpringMVC中设置任何一个view-controller时其他控制器中的请求映射将全部失效,此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签

<!-- 开启SpringMVC的注解驱动 可以使得view-controller和其他控制器中的请求映射一起使用 -->
<mvc:annotation-driven/>

7、RESTful

7.1 RESTful简介

  • REST(Representational State Transfer)表示层资源状态转移(前端的视图页面 -> 后端的控制层)
7.1.1 资源

资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。万物皆资源:一个类、一个Jsp页面、Css文件等都是资源

7.1.2 资源的表述(表现形式)

资源的表述是一段对于资源在某个特定时刻的状态的描述,资源的表述可以有多种格式,例如HTML/XML/JSON/纯文本/图片/视频/音频等等

7.1.3 状态转移

状态转移是在客户端和服务器端之间转移(transfer)代表资源状态的表述,在浏览器和服务器端的交互的一种状态,游览器发送请求和请求路径,间接的操作资源

7.2 RESTful的实现

  • 在HTTP协议中,有四种表示操作方式GET、POST、PUT、DELETE

    1. GET:用来获取资源

    2. POST:用来新建资源

    3. PUT:用来更新资源

    4. DELETE:用来删除资源

  • REST 风格提倡 URL 地址使用统一的风格设计从前到后各个单词使用斜杠分开不使用问号键值对方式携带请求参数而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性

操作 传统方式 REST风格
查询操作 getUserById?id=1 user/1 -> get请求方式
保存操作 saveUser user -> post请求方式
删除操作 deleteUser?id=1 user/1 -> delete请求方式
更新操作 updateUser user -> put请求方式

7.3 HiddenHttpMethodFilter

  • 由于浏览器只支持发送get和post方式的请求,那么该如何发送putdelete请求呢?

  • SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们 POST 请求转换为 DELETE PUT 请求

    HiddenHttpMethodFilter 处理put和delete请求的条件:

    1. 当前请求的请求方式必须为post
    2. 当前请求必须传输请求参数_method
  • 满足以上条件,HiddenHttpMethodFilter 过滤器就会将当前请求的请求方式转换为请求参数 _method的值,因此请求参数 _method的值是最终的请求方式

<!-- 在web.xml中注册HiddenHttpMethodFilter -->
<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>

:目前为止,SpringMVC中提供了两个过滤器:CharacterEncodingFilterHiddenHttpMethodFilter,只需要记住编码过滤器要放在首位

8、RESTful案例

8.1 准备工作

  • 实现CURD操作,对员工信息的增删改查

  • 准备Employee实体类

/**
 * Employee的员工实体类
 *
 * @author Forget_chen
 * @className Employee
 * @desc
 * @date 2022/4/24 13:03
 */
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender; // 1 male, 0 female

    public Employee() {
    }

    public Employee(Integer id, String lastName, String email, Integer gender) {
        this.id = id;
        this.lastName = lastName;
        this.email = email;
        this.gender = gender;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", lastName='" + lastName + '\'' +
                ", email='" + email + '\'' +
                ", gender=" + gender +
                '}';
    }
}
  • 模拟dao层与数据库交互数据
/**
 * Employee实体类与数据库进行交互
 *
 * @author Forget_chen
 * @className EmployeeDao
 * @desc
 * @date 2022/4/24 13:07
 */
@Repository // 持久层的注解标识
public class EmployeeDao {
    private static Map<Integer, Employee> employees = null;

    static {
        employees = new HashMap<Integer, Employee>();
        employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1));
        employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1));
        employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0));
        employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0));
        employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1));
    }

    private static Integer initId = 1006;

    public void save(Employee employee) {
        if (employee.getId() == null) {
            employee.setId(initId++);
        }
        employees.put(employee.getId(), employee);
    }

    public Collection<Employee> getAll() {
        return employees.values();
    }

    public Employee get(Integer id) {
        return employees.get(id);
    }

    public void delete(Integer id) {
        employees.remove(id);
    }
}

8.2 功能清单

功能 URL 地址 请求方式
访问首页√ / GET
查询全部数据√ /employee GET
删除√ /employee/2 DELETE
跳转到添加数据页面√ /toAdd GET
执行添加√ /employee POST
跳转到更新数据页面√ /employee/2 GET
执行更新√ /employee PUT

8.3 具体功能

8.3.1 访问首页
  1. 配置视图控制器完成首页跳转
<mvc:view-controller path="/" view-name="index"/>
  1. 创建首页页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>首页</h1>
<a th:href="@{/employee}">查看全部员工信息</a>
</body>
</html>
8.3.2 查询所有员工信息
  1. 创建查询全部员工信息的控制器方法
/*
 1、查询全部数据
  */
@RequestMapping(
  value = "/employee",
  method = RequestMethod.GET
)
public String getAllEmployee(Model model){
  Collection<Employee> employee_list = dao.getAll();
  model.addAttribute( "employee_list",employee_list);
  return "employee_list";
}
  1. 创建employee_list.html页面(用于展示员工信息)
<table id="dataTable" border="1" cellpadding="0" cellspacing="0" style="text-align: center">
    <tr>
        <th colspan="5">Employee Info</th>
    </tr>
    <tr>
        <th>id</th>
        <th>lastName</th>
        <th>email</th>
        <th>gender</th>
        <th>options(<a th:href="@{/toAdd}">添加</a></th>
    </tr>
    <tr th:each="employee : ${employee_list}">
        <td th:text="${employee.id}"></td>
        <td th:text="${employee.lastName}"></td>
        <td th:text="${employee.email}"></td>
        <td th:text="${employee.gender}"></td>
        <td>
            <!-- 使用thymeleaf语法 @{/请求地址/{参数}(参数 = 值)} -->
            <a @click="deleteEmployee" th:href="@{/employee/{id}(id=${employee.id})}">delete</a>
            <!-- 跳转到更新页面,因为需要实现回现功能,要根据id查询员工信息进行显示  -->
            <a th:href="@{/employee/{id}(id=${employee.id})}">update</a>
        </td>
    </tr>
</table>
8.3.3 根据ID删除员工信息
  1. 创建处理delete请求的表单
<!-- 作用:通过超链接控制表单的提交,将post请求转换为delete请求 -->
<form id="deleteForm" method="post">
  	<!-- HiddenHttpMethodFilter要求:必须传输_method请求参数,并且值为最终的请求方式 -->
    <input type="hidden" name="_method" value="delete"/>
</form>
  1. 删除超链接并且绑定点击事件(提交事件)
  • 引入vue.js
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
  • 通过删除超链接**(不填写超链接的@href属性)**
<a @click="deleteEmployee" th:href="@{/employee/{id}(id = ${employee.id})}">delete</a>
  • 通过vue将超链接的点击事件绑定为表单的提交
<script type="text/javascript">
    var vue = new Vue({
        // el挂载点,通过选择器设置vue管理的元素 
        el:"#dataTable",
        methods:{
            deleteEmployee:function (event){
                // 根据id获取表单元素
                var deleteForm = document.getElementById("deleteForm");
                // 将当前触发事件的href赋值给deleteForm的action属性,相当于绑定点击事件的提交地址
                deleteForm.action = event.target.href;
                // 提交表单
                deleteForm.submit();
                // 取消超链接的默认行为
                event.preventDefault()
            }
        }
    });
</script>
  1. 创建根据ID删除员工信息的控制器方法
/*
 2、根据id删除员工信息
  */
@RequestMapping(
  value = "/employee/{id}",
  method = RequestMethod.DELETE
)
public String deleteEmpById(@PathVariable("id") Integer id){
  dao.delete(id);
  // 需要使用重定向功能,使用浏览器进行访问,相当于Get请求
  return "redirect:/employee";
}
8.3.4 添加员工信息
  1. 配置视图控制器实现跳转到添加数据页面
<mvc:view-controller path="/toAdd" view-name="employee_add"/>
  1. 创建employee_add.html页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>添加员工信息</title>
</head>
<body>
<form th:action="@{/employee}" method="post">
    lastName:<input type="text" name="lastName"><br>
    email:<input type="text" name="email"><br>
    gender:<input type="radio" name="gender" value="1">male
    <input type="radio" name="gender" value="0">female<br>
    <input type="submit" value="添加">
</form>
</body>
</html>
  1. 创建添加员工信息的控制器方法
/*
 3、添加员工信息
  */
@RequestMapping(
  value = "/employee",
  method = RequestMethod.POST
)
public String addEmployee(Employee emp){
  dao.save(emp) ;
  return "redirect:/employee";
}
8.3.5 更新员工信息
  1. 修改超链接(需要根据ID实现数据回现)
<!-- 跳转到更新页面,因为需要实现回现功能,要根据id查询员工信息进行显示  -->
<a th:href="@{/employee/{id}(id = ${employee.id})}">update</a>
  1. 创建根据ID查询单个员工信息的控制器方法(实现数据回现)
/*
 4、根据id查询单个员工信息
  */
@RequestMapping(value="/employee/{id}")
public String getEmpById(@PathVariable("id") Integer id,Model model){
  Employee emp = dao.get(id);
  // 将查询到的员工信息,通过Model传递给request
  model.addAttribute("employee",emp);
  // 跳转到对应的更新页面
  return "employee_update";
}
  1. 创建employee_update.html页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>更新员工信息</title>
</head>
<body>

<form th:action="@{/employee}" method="post">
    <input type="hidden" name="_method" value="put">
    <input type="hidden" name="id" th:value="${employee.id}">
    lastName:<input type="text" name="lastName" th:value="${employee.lastName}"/><br>
    email:<input type="text" name="email" th:value="${employee.email}"/><br>
    <!-- radio类型的数据,使用th:field字段回现数据 -->
    gender:<input type="radio" name="gender" value="1" th:field="${employee.gender}">male
    <input type="radio" name="gender" value="0" th:field="${employee.gender}">female<br>
    <input type="submit" value="更新">
</form>

</body>
</html>
  1. 创建更新员工信息的控制器方法
/*
 5、更新员工信息
  */
@RequestMapping(
  value = "/employee",
  method = RequestMethod.PUT
)
public String updateEmployee(Employee emp){
  // 将更新的员工信息,进行覆盖更新
  dao.save(emp);
  return "redirect:/employee";
}

9、HttpMessageConverter

  • HttpMessageConverter报文信息转换器,将请求报文转换为Java对象(浏览器发送的数据转换成Java对象),或将Java对象转换为响应报文(Java对象转换成服务器发送的数据)

  • HttpMessageConverter提供了两个注解两个类型:@RequestBody,@ResponseBodyRequestEntity,ResponseEntity

9.1 @RequestBody

  • @RequestBody可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody进行标识当前请求的请求体就会为当前注解所标识的形参赋值
<form th:action="@{/testRequestBody}" method="post">
  用户名:<input type="text" name="username" >
  密码:<input type="password" name="password" >
  <input type="submit" value="测试@RequestBody">
</form>
/*
 1、测试@RequestBody注释
 当前请求的请求体就会为当前注解所标识的形参赋值
 输出结果:requestBody:username=陈家睿&password=123456
  */
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody) {
  // 使用URL解码器,使用utf-8进行解码
  try {
    requestBody = URLDecoder.decode(requestBody, "utf-8");
  } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
  }
  System.out.println("requestBody:" + requestBody);
  return "success";
}

9.2 RequestEntity

  • RequestEntity封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参
    1. getHeaders():获取请求头信息
    2. getBody():获取请求体信息
<form th:action="@{/testRequestEntity}" method="post">
  用户名:<input type="text" name="username" >
  密码:<input type="password" name="password" >
  <input type="submit" value="测试RequestEntity">
</form>
/*
 2、测试RequestEntity类型
 当前请求的请求报文就会赋值给该形参
     -- 可以通过getHeaders()获取请求头信息
     -- 通过getBody()获取请求体信息
  */
@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity) {
  // 当前的requestEntity表示当前整个请求报文的信息
  System.out.println("requestHeader:" + requestEntity.getHeaders());
  try {
    String requestBody = URLDecoder.decode(Objects.requireNonNull(requestEntity.getBody()), "utf-8");
    System.out.println("requestBody:" + requestBody);
  } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
  }
  return "success";
}

9.3 @ResponseBody

  • @ResponseBody用于标识一个控制器方法可以将该方法的返回值直接作为响应报文的响应体响应到浏览器
<a th:href="@{/testResponseBody}">测试@ResponseBody注释响应浏览器数据</a><br>
/*
 4、测试@ResponseBody注释响应浏览器数据
 @ResponseBody用于标识一个控制器方法
 可以将该方法的返回值直接作为响应报文的响应体响应到浏览器
  */
@RequestMapping(
  value = "/testResponseBody",
  produces = "text/html;charset=utf-8"
)
@ResponseBody
public String testResponseBody() {
  return "Hello,@ResponseBody注解";
}

9.4 SpringMVC处理Json

@ResponseBody处理json的步骤

9.4.1 导入Jackson的Maven依赖
<!-- 导入jackson依赖将实体类转换成Json格式数据 -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.12.1</version>
</dependency>
9.4.2 配置SpringMVC核心文件
  • 在SpringMVC的核心配置文件中开启mvc的注解驱动
<mvc:annotation-driven />

此时在HandlerAdaptor中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter可以将响应到浏览器的Java对象转换为Json格式的字符串

9.4.3 控制器方法
  1. 处理器方法上使用@ResponseBody注解进行标识
  2. Java对象直接作为控制器方法的返回值,就会自动转换Json格式的字符串
<a th:href="@{/testResponseUser}">测试@Response 注释响应User对象</a><br>
@RequestMapping("/testResponseUser")
@ResponseBody
public User testResponseUser(){
  return new User(1001,"张三","202428",23,"male");
}
// 浏览器页面响应的运行结果:
{
  id: 1001,
  username: "张三",
  password: "202428",
  age: 23,
  sex: "male"
}

9.5 SpringMVC处理Ajax

9.5.1 请求超链接
<div id="app">
  <a @click="testAxios" th:href="@{/testAxios}">SpringMVC处理Ajax</a>
</div>
9.5.2 通过Vue和Axios处理点击事件
// 引入vue.js和axios.js的静态文件
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script>
<script type="text/javascript">
  new Vue({
  el: "#app",
  methods: {
    testAxios: function (event) {
      axios({
        method: "post", // 提交方式
        url: event.target.href, // 绑定当前ajax的提交地址
        params:{ // 传递的请求参数
          username : "陈家睿",
          password : "202428",
        }
      }).then(function (response) {
        // 触发成功,所执行的函数
        alert(response.data);
      });
      // 取消当前超链接的默认行为
      event.preventDefault();
    }
  }
});
</script>
9.5.3 处理Ajax的控制器方法
/*
 5、测试SpringMVC处理Ajax
  */
@RequestMapping(
  value = "/testAxios",
  method = RequestMethod.POST
)
@ResponseBody // 该方法的返回值,传递给Ajax的回调函数中的response(响应到浏览器中的数据)
public String testAxios(String username,String password){
  if ("陈家睿".equalsIgnoreCase(username) && "202428".equalsIgnoreCase(password)){
    return "登录成功";
  }
  return "用户名或者密码错误";
}

9.6 @RestController注解

  • @RestController注解是springMVC提供的一个复合注解标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了@ResponseBody注解

9.7 ResponseEntity

  • ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文

10、文件上传和下载

10.1 文件下载

  • 使用ResponseEntity实现下载文件的功能
/*
 1、测试文件下载的功能(属于文件复制的功能)
  */
@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
  // 获取ServletContext对象
  ServletContext servletContext = session.getServletContext();
  // 获取服务器中文件的真实路径
  String realPath = servletContext.getRealPath("/static/img/AlertStopIcon.png");
  System.out.println(realPath);
  // 创建输入流
  InputStream is = new FileInputStream(realPath);
  // 创建字节数组 is.available():获取对应文件的所有字节数
  byte[] bytes = new byte[is.available()];
  // 将流读到字节数组中
  is.read(bytes);
  // 创建HttpHeaders对象设置响应头信息 响应头是一个键值对类型的数据
  MultiValueMap<String, String> headers = new HttpHeaders();
  // 设置要下载方式以及下载文件的名字 attachment:以附件的形式下载
  headers.add("Content-Disposition", "attachment;filename=AlertStopIcon.png");
  // 设置响应状态码
  HttpStatus statusCode = HttpStatus.OK;
  // 创建ResponseEntity对象 new ResponseEntity(响应体,响应头信息,响应状态码)
  ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
  // 关闭输入流
  is.close();
  return responseEntity;
}

10.2 文件上传

  • 文件上传要求form表单的请求方式必须为post,并且添加属性enctype="multipart/form-data"(分成多段以流的方式(二进制形式传输)提交给服务器) SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息
10.2.1 引入上传文件依赖
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload 用于文件上传的依赖-->
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.1</version>
</dependency>
10.2.2 添加文件解析器(SpringMVC.xml)
<!-- 配置文件上传解析器 将上传的文件封装风MultipartFile对象 SpringMVC必须通过id才能查找到,且id固定为"multipartResolver" -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
10.2.3 处理文件上传的控制器方法
/*
 2、测试文件上传功能
     -- 导入上传所需的依赖
     -- 在SpringMVC.xml中配置文件上传解析器
     -- 通过Session获取ServletContext对象
     -- ServletContext对象获取服务器中photo目录的真实路径
     -- 通过transferTo上传文件到服务器target目
     解决上传文件重名的覆盖问题:使用UUID+文件后缀名作为最终的文件名
  */
@RequestMapping(
  value = "/testUp",
  method = RequestMethod.POST
)
public String testUp(MultipartFile photo, HttpSession session) throws IOException {
  // System.out.println(photo.getName()); // 获取表单中传输的name名称
  // 获取源文件名称
  String fileName = photo.getOriginalFilename();
  // 获取上传的文件后缀名
  String suffixName = fileName.substring(fileName.lastIndexOf("."));
  // 将UUID作为文件名
  String uuid = UUID.randomUUID().toString();
  // 将uuid和文件后缀名作为最终的文件名
  fileName = uuid + suffixName.replaceAll("-", "");

  // 获取ServletContext对象
  ServletContext servletContext = session.getServletContext();
  // 获取服务器中photo目录的真实路径
  String photoPath = servletContext.getRealPath("photo");
  File file = new File(photoPath);
  if (!file.exists()) {
    // 若文件路径不存在,则创建目录
    file.mkdir();
  }
  String finalPath = photoPath + File.separator + fileName; // 文件存放路径=文件夹路径+路径分隔符(/)+文件名
  photo.transferTo(new File(finalPath));
  return "success";
}

11、拦截器

11.1 拦截器的配置

  • SpringMVC中的拦截器用于拦截控制器方法的执行
  • SpringMVC中的拦截器需要实现HandlerInterceptor接口
  • SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置
<!-- 配置拦截器 -->
<mvc:interceptors>
  <!--
  拦截所有的请求:
  <bean class="com.chen.interceptors.FirstInterceptor"></bean>
  <ref bean="firstInterceptor"/>
  拦截指定的请求:
  -->
  <mvc:interceptor>
    <!-- 
		mvc:mapping设置需要拦截的请求
		跟Ant风格的写法一致,/**表示多层目录拦截
		-->
    <mvc:mapping path="/**"/>
    <!-- 
		mvc:exclude-mapping设置需要排除的请求		
		排除拦截'/'的请求路径 
		-->
    <mvc:exclude-mapping path="/"/>
    <ref bean="firstInterceptor"/>
  </mvc:interceptor>
</mvc:interceptors>

11.2 拦截器的三个抽象方法

  1. preHandle(控制器方法执行之前执行):其boolean类型的返回值表示是否拦截或放行,true为放行(调用控制器方法),false为拦截(不调用控制器方法)

  2. postHandle(控制器方法执行之后执行)

  3. afterCompletion(渲染视图之后执行)

11.3 多个拦截器的执行顺序

1、若每个拦截器的preHandle()都返回true

  • 此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关

preHandle()会按照配置的顺序执行,而postHandle()afterComplation()会按照配置的反序执行

2、若某个拦截器的perHandle()返回false

  • preHandle()返回false和它之前的拦截器的preHandle()都会执行postHandle()都不执行,返回false 的拦截器之前的拦截器的afterComplation()会按照配置的反序执行

12、异常处理器

12.1 基于配置的异常处理

  • SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver

  • HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver(默认的异常处理解析器)SimpleMappingExceptionResolver(自定义的异常处理解析器)

  • 自定义的异常处理器SimpleMappingExceptionResolver使用方法

<bean>
  class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
  <property name="exceptionMappings">
    <props>
      <!--
properties的键表示处理器方法执行过程中出现的异常 properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面
-->
      <prop key="java.lang.ArithmeticException">error</prop>
    </props>
  </property>
  <!-- exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享
-->
  <property name="exceptionAttribute" value="ex"></property>
</bean>

12.2 基于注解的异常处理

@ControllerAdvice // 将当前类标识为异常处理的组件
public class ExceptionController {
  /*
   使用@ExceptionHandler注解 处理异常进行页面跳转
    */
  @ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})
  public String testException(Exception ex, Model model){
    model.addAttribute("ex",ex);
    return "error";
  }

}

13、注解配置SpringMVC

  • 使用配置类和注解代替web.xmlSpringMVC核心配置文件的功能

13.1 创建初始化类,代替Web.xml

  • 继承AbstractAnnotationConfigDispatcherServletInitializer类,容器会自动发现它,并用它来配置Servlet上下文
/**
 *
 * Web工程的初始化类,用来代替web.xml
 *
 * @author Forget_chen
 * @className com.chen.config.WebInit
 * @desc
 * @date 2022/4/28 08:32
 */
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {

  /*
   指定Spring的配置类
    */
  @Override
  protected Class<?>[] getRootConfigClasses() {
    return new Class[]{SpringConfig.class};
  }

  /*
   指定SpringMVC的配置类
    */
  @Override
  protected Class<?>[] getServletConfigClasses() {
    return new Class[]{WebConfig.class};
  }

  /*
   指定DispatcherServlet(前端控制器)的映射规则,即url-pattern
    */
  @Override
  protected String[] getServletMappings() {
    return new String[]{"/"};
  }

  /*
   注册过滤器
    */
  @Override
  protected Filter[] getServletFilters() {
    // 配置字符编码过滤器
    CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
    characterEncodingFilter.setEncoding("UTF-8");
    characterEncodingFilter.setForceResponseEncoding(true);
    // 配置HiddenHttpMethodFilter过滤器
    HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();

    return new Filter[]{characterEncodingFilter,hiddenHttpMethodFilter};
  }

}

13.2 创建SpringConfig配置类,代替Spring配置文件

@Configuration
public class SpringConfig { 
  //ssm整合之后,spring的配置信息写在此类中
}

13.3 创建WebConfig配置类,代替SpringMVC配置文件

/**
 * SpringMVC配置类:
 * 1、扫描组件
 * 2、视图解析器
 * 3、视图控制器(view-controller)
 * 4、默认servlet(default-servlet-handler)
 * 5、注解驱动(mvc-driver)
 * 6、文件上传解析器
 * 7、异常处理器
 * 8、拦截器
 *
 * @author Forget_chen
 * @className WebConfig
 * @desc
 * @date 2022/4/28 08:38
 */
/*
实现WebMvcConfigurer接口中的方法就可以配置
视图控制器(view-controller)、默认servlet(default-servlet-handler)、文件上传解析器、异常处理器、拦截器等
 */
@Configuration
@ComponentScan("com.chen.controller") // 开启组件扫描
@EnableWebMvc // 开启MVC注解驱动
public class WebConfig implements WebMvcConfigurer {
  /*
   配置视图解析器Thymeleaf
    */
  // 配置生成模板解析器
  @Bean
  public ITemplateResolver templateResolver() {
    WebApplicationContext webApplicationContext =
      ContextLoader.getCurrentWebApplicationContext();
    // ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过 WebApplicationContext 的方法获得
    ServletContextTemplateResolver templateResolver = new
      ServletContextTemplateResolver(
      webApplicationContext.getServletContext());
    templateResolver.setPrefix("/WEB-INF/webs/");
    templateResolver.setSuffix(".html");
    templateResolver.setCharacterEncoding("UTF-8");
    templateResolver.setTemplateMode(TemplateMode.HTML);
    return templateResolver;
  }

  // 生成模板引擎并为模板引擎注入模板解析器
  @Bean
  public SpringTemplateEngine templateEngine(ITemplateResolver
                                             templateResolver) {
    SpringTemplateEngine templateEngine = new SpringTemplateEngine();
    templateEngine.setTemplateResolver(templateResolver);
    return templateEngine;
  }

  // 生成视图解析器并未解析器注入模板引擎
  @Bean
  public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
    ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
    viewResolver.setCharacterEncoding("UTF-8");
    viewResolver.setTemplateEngine(templateEngine);
    return viewResolver;
  }

  /*
   配置默认servlet(default-servlet-handler)
    */
  @Override
  public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable(); // 将默认servlet启动
  }

  /*
   配置拦截器
    */
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new TestInterceptor()).addPathPatterns("/**");
  }

  /*
   配置视图控制器(view-controller)
   addViewController:设置请求地址
   setViewName:设置视图名称
    */
  @Override
  public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/hello").setViewName("hello");
  }

  /*
   配置文件上传解析器
    */
  @Bean
  public MultipartResolver MultipartResolver(){
    return new CommonsMultipartResolver();
  }

  /*
   配置异常处理器
    */
  @Override
  public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
    // 创建自定义的异常处理器SimpleMappingExceptionResolver
    SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
    Properties prop = new Properties();
    prop.setProperty("java.lang.ArithmeticException","error");
    // 设置异常映射
    simpleMappingExceptionResolver.setExceptionMappings(prop);
    // 设置共享异常信息的键
    simpleMappingExceptionResolver.setExceptionAttribute("ex");
    resolvers.add(simpleMappingExceptionResolver);
  }

}

14、SpringMVC执行流程

14.1 SpringMVC常用组件

  • DispatcherServlet(前端控制器)

    • 作用:统一处理请求响应,整个流程控制的中心,由它调用其它组件处理用户的请求
  • HandlerMapping(处理器映射器)

    • 作用:根据请求的url、method等信息查找Handler,即@RequestMapping注解
  • Handler(处理器)

    • 作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理,即控制器方法
  • HandlerAdapter(处理器适配器)

    • 作用:通过HandlerAdapter对处理器(控制器方法)进行执行
  • ViewResolver(视图解析器)

    • 作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView
  • View(视图)

    • 作用:将模型数据通过页面展示给用户(即Html页面)

14.2 SpringMVC的执行流程

  1. 用户向服务器发送请求,请求被SpringMVC前端控制器 DispatcherServlet捕获
  2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:
  • 不存在
    1. 再判断是否配置了mvc:default-servlet-handler
    2. 如果没配置,则控制台报映射查找不到,客户端展示404错误

image-20220428171904265

  1. 如果有配置则访问目标资源**(一般为静态资源,如:JS,CSS,HTML)**,找不到客户端也会展示404错误

image-20220428172130661

  • 存在
    1. 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回
    2. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter
    3. 如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法(正向)
    4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
      1. HttpMessageConveter**(@RequestBody)**: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
      2. 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
      3. 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
      4. 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
    5. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象
    6. 此时将开始执行拦截器的postHandle(...)方法(逆向)
    7. 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图
    8. 渲染视图完毕执行拦截器的afterCompletion(…)方法(逆向)
    9. 将渲染结果返回给客户端

空文件

简介

SpringMVC学习笔记和源码 展开 收起
JavaScript 等 3 种语言
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
1
https://gitee.com/CJR1292379046/SpringMVC.git
git@gitee.com:CJR1292379046/SpringMVC.git
CJR1292379046
SpringMVC
SpringMVC_learns
master

搜索帮助

53164aa7 5694891 3bd8fe86 5694891