springMVC 笔记

一、SpringMVC 简介

1、什么是 MVC

MVC 是一种软件架构思想,将软件按照模型、视图、控制器来划分。

M:Model,模型层,指工程中的 JavaBean,作用是处理数据。

JavaBean 大致分为两类:

  • 一类称为实体类 Bean,专门存储业务数据的。
  • 一类称为业务处理 Bean,指 Service 或 Dao 对象,专门用于处理业务逻辑和数据访问的。

V:View,视图层,指工程中的 html 或 jsp 页面,作用是与用户进行交互,展示数据。

C:Controller,控制层,指工程中的 Servlet,作用是接收请求和响应浏览器。

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

2、什么是 SpringMVC

SpringMVC 是 Spring 的一个后续产品,是 Spring 的一个子项目。

SpringMVC 是 Spring 为表述层开发提供的一套完整的解决方案。在表述层框架经历 Strust、WebWork、Strust2 等诸多产品的历代更迭之后,目前业界普遍选择了 SpringMVC 作为 Java EE 项目表述层的首选方案

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

3、SpringMVC 的特点

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

二、HelloWorld

1、开发环境

  • IDE:idea 2021.2.2
  • 构建工具:maven 3.8
  • 服务器:tomcat 9
  • Spring 版本:5.3.14

2、创建 maven 工程

  1. 添加 web 模块

  2. 打包方式:war

  3. 引入依赖

    <dependencies>
        <!-- SpringMVC -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.14</version>
        </dependency>
    
        <!-- 日志 -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
            <scope>test</scope>
        </dependency>
    
        <!-- servlet -->
        <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>
    

3、配置 web.xml

注册 SpringMVC 的前端控制器(中央处理器) DispatcherServlet

  1. 默认配置方式

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

    <!-- 配置 SpringMVC 的前端控制器(中央处理器),对浏览器发送的请求统一进行处理 -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--
                设置 springMVC 的核心控制器能处理的请求的请求路径
                "/" 可以匹配除了 .jsp 为后缀所有请求
                "/*" 则可以匹配所有请求
            -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
  2. 扩展配置方式

    可通过 init-param 标签设置 SpringMVC 配置文件的位置和名称,通过 load-on-startup 标签设置 SpringMVC 前端控制器(中央处理器)——DispatcherServlet 的初始化时间。

    <!-- 配置 SpringMVC 的前端控制器(中央处理器),对浏览器发送的请求统一进行处理 -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置(指定)SpringMVC 配置文件的路径和名称 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springMVC.xml</param-value>
        </init-param>
        <!--
                设置此 servlet 的初始化时间是什么时候
                当值为 0 或者大于 0 时,表示容器在启动时就加载并初始化这个 servlet
                当值小于 0 或者没有指定时,则表示容器在该 servlet 第一次被请求时,才会去加载
             -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--
                设置 springMVC 的核心控制器能处理的请求的请求路径
                "/" 可以匹配除了 .jsp 为后缀所有请求
                "/*" 则可以匹配所有请求
            -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

注: 标签中使用 / 和 /* 的区别:

/ 可以匹配除了 .jsp 为后缀所有请求,因此就可以避免在 访问 jsp 页面的时候被 DIspatcherServlet 处理,从而找不到相应的页面

/* 则可以匹配所有请求,例如,在使用过滤器的时候,若需要对所有请求进行过滤,就需要使用 /* 的写法

4、创建请求控制器

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

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

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

@Controller
public class HelloController {
    
}

5、配置 SpringMVC 的配置文件

<!-- 自动扫描包(开启注解扫描) -->
<context:component-scan base-package="com.zxy.controller" />


<!-- 配置 thymeleaf 视图解析器 -->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
    <!-- 属性注入 -->
    <property name="order" value="1" />
    <property name="characterEncoding" value="UTF-8" />
    <property name="templateEngine">
        <!-- 内部 bean -->
        <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
            <property name="templateResolver">
                <!-- 内部 bean -->
                <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 id="" calss="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>

6、测试 HelloWrold

  1. 在 WEB-INF 目录下创建 templates 文件夹,在 templates 文件夹中创建 index.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>首页</h1>
    <a th:href="@{/target}">访问目标页面 target.html</a>
    </body>
    </html>
    
  2. 在请求控制器中创建处理请求的方法

    @Controller
    public class HelloController {
    
        @RequestMapping("/")
        public String index() {
            // 返回视图名称
            return "index";
        }
    }
    

当启动项目的时候,浏览器会自动发起 http://localhost:8080/工程名/ 请求,然后被前端控制器(中央处理器)接收到,接下来寻找符合请求的处理方法来处理。

7、总结

浏览器发送请求,若请求地址符合前端控制器(中央处理器)的 url-pattern,则该请求就会被前端控制器(中央处理器)——DispatcherServlet 处理。

前端控制器(中央处理器)会读取 SpringMVC 的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中 @RequestMapping 注解的 value 属性值进行匹配,若匹配成功,则该注解所标识的控制器方法,就是处理这个请求的方法。

三、@RequestMapping 注解

1、@RequestMapping 注解的功能

顾名思义,@RequestMapping 注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。

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

2、@RequestMapping 注解的位置

@RequestMapping 只能标识在类或方法上:

  • @RequestMapping 标识一个类:设置映射的请求路径的初始信息

  • @RequestMapping 标识一个方法:设置映射请求路径的具体信息

@Controller
public class TestController {
    // 此时该请求映射的请求路径为:/
    @RequestMapping("/")
    public String index() {
        return "index";
    }
}

//===================================================

@Controller
@RequestMapping("/test")
public class TestController {
    // 此时该请求映射的请求路径为:/test/
    @RequestMapping("/")
    public String index() {
        return "index";
    }
}

3、@RequestMapping 注解的 value 属性

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

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

注:该属性匹配规则是 ”或“,匹配不成功则报 404 错误

4、@RequestMapping 注解的 method 属性

@RequestMapping 注解的 method 属性通过请求的请求方式匹配请求映射。

@RequestMapping 注解的 method 属性是一个 RequestMethod 类型的数组,表示该请求映射能够匹配多种请求方式的请求。

注:对于处理指定请求方式的控制器方法,SpringMVC 中提供了 @RequestMapping 的派生注解

处理 get 请求的映射—— @GetMapping

处理 post请求的映射—— @PostMapping

处理 put请求的映射—— @PutMapping

处理 delete请求的映射—— @DeleteMapping

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

若要发送 put 和 delete 请求时,则需要通过 Spring 提供的过滤器 HiddenHttpMethodFilter,在 restful 部分会讲到。

注:该属性匹配规则是 ”或“,匹配不成功则报 405 错误

5、@RequestMapping 注解的 params 属性(了解)

@RequestMapping 注解的 params 属性通过请求的请求参数匹配请求映射。

@RequestMapping 注解的 params 属性是一个字符串型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系。

  • "param" :要求请求映射所匹配的请求必须携带该 param 参数
  • "!param" :要求请求映射所匹配的请求必须不能携带该 param 参数
  • "param=value" :要求请求映射所匹配的请求必须携带该 param 参数,并且该参数值为 value
  • "param!=value" :要求请求映射所匹配的请求必须携带该 param 参数,并且该参数值不能为 value

注:该属性匹配规则是 ”与“,匹配不成功则报 400 错误

6、@RequestMapping 注解的 headers属性(了解)

@RequestMapping 注解的 haders属性通过请求的请求头信息匹配请求映射

@RequestMapping 注解的 haders属性是一个字符串型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系。

  • "header" :要求请求映射所匹配的请求必须携带该 header 请求头信息
  • "!header" :要求请求映射所匹配的请求必须不能携带该 header 请求头信息
  • "header=value" :要求请求映射所匹配的请求必须携带该 header 请求头信息,并且该请求头信息值为 value
  • "header!=value" :要求请求映射所匹配的请求必须携带该 header 请求头信息,并且该请求头信息值为 value

注:该属性匹配规则是 ”与“,匹配不成功则报 404 错误

7、SpringMVC 支持 ant 风格的路径

? :表示任意的单个字符

* :表示任意的 0 个或多个字符

** :表示任意的一层目录或多层目录

注:在使用 ** 时,只能使用 /**/XXX 的方式,如果 **的前面或后面出现了其他字符,则会被当成单个 * 来解析

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

原始方式:/queryUser?id=1

rest 方式:/queryUser/1

SpringMVC 路径中的占位符常用于 restful 风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的 @RequertMapping 注解的 value 属性中通过占位符{XXX}表示传输的数据,再通过 @PathVariable 注解,将占位符所表示的数据赋值给控制器方法的形参。

四、SpringMVC 获取请求参数

1、通过 Servlet API 获取

将 HttpServletRequest 作为控制器方法形参,此时 HttpServletRequest 类型的参数表示封装了当前请求的请求报文对象。

2、通过控制器方法的形参获取

再控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,就会将请求参数赋值给同名的形参。

接收多个同名参数的值的不同情况:

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

若使用字符串数组类型的形参,此参数的数组中包含了每一个数据

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

3、@RequestParam

@RequestParam 是将请求参数和控制器方法 形参创建映射关系

@RequestParam 注解一共有三个属性:

  • value :指定为形参赋值的请求参数的参数名。
  • required :指定是否必须传输此请求参数名,默认为 true。
    • 若值为 true,则当前请求必须传输 value 所指定的请求参数,若没有传输,且没有设置 defaultValue 属性时,则会报错:400。
    • 若值为 false,则当前 value 属性所指定的请求参数不是必须的,如果传了,则赋值,没有则为 null。
  • defaultValue :不管 required 属性值为 true 还是 false,当 value 所指定的请求参数没有传输,或传输了但值为 "" 时,则使用当前属性值为形参赋值。

4、@RequestHeader

@RequestHeader 是将请求头信息和控制器方法的形参创建映射关系。

@RequestHeader 注解一共有三个属性:value、required、defaultValue,用法同 @RequestParam 一样。

5、@CookieValue

@CookieValue 是将 cookie 数据和控制器方法的形参创建映射关系。

@CookieValue 注解一共有三个属性:value、required、defaultValue,用法同 @RequestParam 一样。

6、通过 POJO(实体类)获取请求参数

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

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

解决获取请求参数的乱码问题,可以使用 SpringMVC 提供的编码过滤器 CharacterEncodingFilter,需要在 web.xml 中进行注册,该过滤器不仅可以解决请求参数的乱码问题,还可以解决响应乱码问题。

<!-- 注册 springMVC 提供的编码过滤器 -->
<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>
    <init-param>
        <param-name>forceResponseEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

五、域对象共享数据

1、使用 servlet API 向 request 域对象共享数据

在控制器方法形参中声明 HttpServletRequest 类型的参数,使用该参数就可以对 request 域精选操作。

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

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
    /*
            ModelAndView 有 Model 和 View 的功能
            Model 主要用于向请求域共享数据
            View 主要用于设置视图,实现页面跳转
         */
    ModelAndView mv = new ModelAndView();
    // 向请求域共享数据
    mv.addObject("msg", "hello world");
    // 设置视图,实现页面跳转
    mv.setViewName("index");
    return mv;
}

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

@RequestMapping("/testModel")
public String testModel(Model model) {
    model.addAttribute("msg", "hello world");
    return "index";
}

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

@RequestMapping("/testMap")
public String testMap(Map<String, Object> map) {
    map.put("msg", "hello world");
    return "index";
}

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

@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap) {
    modelMap.addAttribute("msg", "hello world");
    return "index";
}

6、Model、ModelMap、Map 的关系

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

public interface Model {}
public interface Map<K,V> {}
public class ModelMap extends LinkedHashMap<String, Object> {}
public class BindingAwareModelMap extends ExtendedModelMap {}
public class ExtendedModelMap extends ModelMap implements Model {}

7、向 session 域对象共享数据

@RequestMapping("/testSession")
public String testSession(HttpSession session) {
    session.setAttribute("msg", "hello world");
    return "index";
}

8、向 application 域对象共享数据

方式有很多,可以先获取 request 对象或者 session 对象,然后通过它们获取 application 对象

六、SpringMVC 中的视图

SpringMVC 中的视图是 View 接口,视图的作用是渲染数据,将模型 Model 中的数据展示给用户。

SpringMVC 中视图的种类很多,默认有转发视图(InternalResourceView)和重定向视图(RedirectView)。

当工程引入 jstl 的依赖,转发视图会自动转换为 JstlView。

若使用的视图技术为 Thymeleaf,在 SpringMVC 的配置文件中配置了 Thymeleaf 的视图解析器,由此视图解析器解析后所得到的是 ThymeleafView

1、ThymeleafView

当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被 SpringMVC 配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和后缀所得到的最终路径,会通过转发的方式实现跳转。

2、转发视图

SpeingMVC 中默认的转发视图是 InternalResourceView

SpeingMVC 中创建转发视图的情况:

当控制器方法所设置的视图名以 ”forward:“ 为前缀时,创建 InternalResourceView 视图,此时的视图名称不会被 SpringMVC 配置文件中所配置的视图解析器解析,而是会将前缀 ”forward:“ 去掉,剩余部分作为最终路径通过转发的方式实现跳转。

例如:”forward:/“ 、”froward:/login“

3、重定向视图

SpeingMVC 中默认的重定向视图是 RedirectView。

当控制器方法所设置的视图名以 ”redirect:“ 为前缀时,创建 RedirectView 视图,此时的视图名称不会被 SpringMVC 配置文件中所配置的视图解析器解析,而是会将前缀 ”redirect:“ 去掉,剩余部分作为最终路径通过重定向的方式实现跳转。

例如:”redirect:/“ 、”redirect:/login“

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

4、视图控制器 view-controller

当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用 view-controller 标签进行表示

<!--
	path:指定处理的请求地址
	view-name:请求地址所要转发的视图名称
-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>

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

七、RESTFul

1、 RESTFul 简介

REST:Representational State Transfer,表现层资源状态转移。

  • 资源:资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。应为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许,而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个 URL 来标识。URL 既是资源的名称,也是资源在 Web 上的地址。对某个资源感兴趣的客户端应用,可以通过资源的 URL 与其进行交互。
  • 资源的表述:资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器之间转移(交换)。资源的表述可以有多种格式,例如 HTML / XML / JSON / 纯文本 / 图片 / 视频 / 音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。
  • 状态转移:在客户端和服务器之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。

2、RESTFul 的实现

具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。

它们分别对应四种基本操作:GET 用来获取资源;POST 用来新建资源;PUT 用来更新资源;DELETE 用来删除资源。

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

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

3、HiddenHttpMethodFilter 过滤器

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

SpringMVC 提供了 HieednHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求。

HiddenHttpMethodFilter 将 POST 请求转换为 DELETE 或 PUT 请求的条件:

  • 发送请求的方式必须是 POST
  • 请求中必须携带参数 _method,该参数的值就是你要转换的请求方式,例如:PUT,DELETE。

在 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>

八、HttpMessageConverter

HttpMessageConverter,报文信息转换器,将请求报文转换为 Java 对象,或将 Java 对象转换为响应报文。

HttpMessageConverter 提供了两个注解和两个类型:@RequestBody,@ResponseBody,RequestEntity,ResponseEntity。

1、@RequestBody

@RequestBody 可以获取请求体,需要在控制器方法设置一个形参,使用 @RequestBody 进行标识,当前请求的请求体就会为当前注解所标识的形参赋值。

@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody) {
    System.out.println("requestBody:" + requestBody);
    return "index";
}

2、RequestEntity

RequestEntity 封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过 该类提供的有些方法获取报文信息,例如:getHeaders() 获取请求头信息;getBody() 获取请求体信息。

@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity requestEntity) {
    System.out.println("requestEntity:" + requestEntity);
    return "index";
}

3、@ResponseBody

@ResponseBody 用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器,类似于自动帮你作了 response.getWriter().writer("") 操作。

@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody() {
    return "index";
}

加了 @ResponseBody 注解后,返回的这个字符串就会自动帮我们写入到响应体中。而不是作为视图。

4、@ResponseBody 处理 JSON

如果使用 @ResponseBody 注解标注的控制器方法的返回值不是字符串对象而是其他对象得到话,就会报错,这个时候,我们希望将此对象变成 json 格式的字符串再写入到响应体中。

@ResponseBody 处理 JSON 的步骤:

  1. 导入 jackson 的依赖

    <!-- jackson -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.12.1</version>
    </dependency>
    
  2. 在 SpringMVC 的核心配置文件中开启 mvc 的注解驱动,此时在 HandalerAdaptor 中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将返回的 Java 对象转成 json 字符串后再写入响应体中。

    <mvc:annotation-driven />
    
  3. 在处理器方法上使用 @ResponseBody 进行标注

  4. 将 Java 对象之间作为处理器方法的返回值返回,就会自动转换为 json 格式的字符串,并写入到响应体中

5、@RestController 注解

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

6、ResponseEntity

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

九、文件上传和下载

1、文件下载

使用 ResponseEntity 实现文件下载的功能

@RequestMapping("/testResponseEntity")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
    // 获取 ServletContext 对象
    ServletContext servletContext = session.getServletContext();
    // 获取服务器中文件的真实路径
    String realPath = servletContext.getRealPath("/static/images/01.jpg");
    // 创建输入流
    InputStream is = new FileInputStream(realPath);
    // 创建字节数组
    byte[] bytes = new byte[is.available()];
    // 将文件读到字节数组中
    is.read(bytes);
    // 创建 HttpHeaders 对象设置响应头信息
    MultiValueMap<String, String> headers = new HttpHeaders();
    // 设置要下载方式以及下载文件的名字
    headers.add("Content-Disposition", "attachment;filename=01.jpg");
    // 设置响应码
    HttpStatus statusCode = HttpStatus.OK;
    // 创建 ResponseEntity 对象
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, headers, statusCode);
    // 关闭输入流
    is.close();
    return responseEntity;
}

2、文件上传

文件上传要求 form 表单请求方式必须为 post,并添加属性 enctype="multipart/form-data"。例如:

<form th:action="@{/testFileUp}" method="post" enctype="multipart/form-data">
    请选择文件:<input type="file" name="photo"> <br>
    <input type="submit" value="点击上传">
</form>

上传步骤:

  1. 添加依赖

    <!-- fileupload 文件上传使用的 -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>
    
  2. 在配置文件中配置文件上传解析器

    <!-- 配置文件上传解析器,将上传的文件封装为 MultipartFile,文件上传解析器的 id 必须为 multipartResolver -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
    
  3. 在控制器方法形参中定义 MultipartFile 类型的参数,且参数名要和表达提交中的文件域名称相同,否则为 null

    <!-- 前端页面中的表单 photo -->
    <form th:action="@{/testFileUp}" method="post" enctype="multipart/form-data">
        请选择文件:<input type="file" name="photo"> <br>
        <input type="submit" value="点击上传">
    </form>
    
    // 处理该文件上传的处理器方法,photo
    public String testFileUp(MultipartFile photo, HttpSession session) throws IOException {
    
  4. 控制方法中写业务代码,实现文件上传

    @RequestMapping("/testFileUp")
    @ResponseBody
    public String testFileUp(MultipartFile photo, HttpSession session) throws IOException {
        // 获取上传的文件的文件名
        String filename = photo.getOriginalFilename();
        // 获取上传的文件的后缀名(.jpg .png 等)
        String suffixName = filename.substring(filename.lastIndexOf("."));
        // 将 UUID 作为文件名
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        filename = uuid + suffixName;
        // 获取 ServletContext 对象
        ServletContext servletContext = session.getServletContext();
        String photoPath = servletContext.getRealPath("photo");
        File file = new File(photoPath);
        // 判断 photoPath 所对应的路径是否存在
        if (!file.exists()) {
            // 若不存在,则创建目录
            file.mkdir();
        }
        photo.transferTo(new File(photoPath + File.separator + filename));
        return "success";
    }
    

十、拦截器

1、拦截器的配置

SpringMVC 中的拦截器用于拦截器控制器方法的执行。

SpringMVC 中的拦截器需要实现 HandlerInterceptor 接口或者继承 HandlerInterceptorAdapter 类(已过时)。

SpringMVC 中的拦截器必须在 SpringMVC 的配置文件中进行配置:

<!-- 配置拦截器需要在 mvc:interceptors 标签中进行 -->
<mvc:interceptors>
    <bean class="拦截器类名"></bean>
    <ref bean="外部类的 id"></ref>
    <!-- 以上两种配置方式都是对 DispatcherServlert 所处理的所有请求进行拦截 -->
    
    <mvc:interceptor>
        <mvc:mapping path="要拦截的请求映射"/> <!-- ant 风格的路径 -->
        <mvc:exclude-mapping path="要排除拦截的请求映射"/> 
        <bean class="拦截器类名"></bean> <!-- 也可以使用 ref 标签指定拦截器 -->
    </mvc:interceptor>
    <!-- 
		以上配置方式可以通过 ref 或 bean 标签设置拦截器,通过 mvc:mapping 标签设置需要拦截的请求映射,通过 mvc:exclude-mapping 设置需要排除拦		 截的请求 。
	-->

</mvc:interceptors>

2、拦截器的三个抽象方法

SpringMVC 中的拦截器有三个抽象方法:

  • prehandle() :控制器方法执行前执行此方法,其 boolean 类型的返回值表示是否放行,返回 true 表示放行,即调用控制器方法;返回 false 表示 不放心,即不会调用控制器方法。
  • postHandle() :控制器方法执行后执行此方法。
  • afterComplation :处理完视图和模型数据,渲染完视图后执行此方法。

3、多个拦截器的执行顺序

拦截器的 prehandle() 方法会按照配置的拦截器顺序的正序执行。

拦截器的 postHandle()afterComplation 方法会按照配置的拦截器顺序的倒叙执行。

十一、异常处理器

1、基于配置的异常处理

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

HandlerExceptionResover 接口实现类有:DefaultHandlerExceptionResover 和 SimpleMappingExceptionResover。

SpringMVC 提供了自定义的异常处理器 SimpleMappingExceptionResover,使用方式:

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

2、基于注解的异常处理

@ControllerAdvice //将当前类标识为异常处理的组件,是 @Controller 的扩展注解
public class ExceptionController {
    @ExceptionHandler(ArithmeticException.class) //用于设置所标识方法处理的异常,是数组类型
    public String handlerArithmeticException(Exception ex, Model model) {
        model.addAttribute("ex", ex);
        return "error";
    }
}

十二、注解配置 SpringMVC

使用配置类和注解代替 web.xml 和 SpringMVC 配置文件的功能。

1、创建初始化类,代替 web.xml

在 Servlet 3.0 环境中,容器会在类路径中查找实现 javax.servlet.ServletContainerInitializer 接口的类,如果找到的话就用它来配置 Servlet 容器。

Spring 提供了这个接口的实现,名为 SpringServletContainerInitializer,这个类反过来又会查找实现 WebApplicationInitializer 的类,并将配置的任务交给它们来完成。Spring 3.2 引入了一个便利的 WebApplicationInitialize 基础实现,名为 AbstractAnnotationConfigDispatcherServletInitializer,当我们的类扩展了 AbstractAnnotationConfigDispatcherServletInitializer ,并将其部署到 Servlet 3.0 容器的时候,容器就会发现它,并用它来配置上下文。

/**
 * @author zxy
 * @date 2022/1/10 9:25
 * Web 工程的初始化类,用代替 web.xml
 */
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
    /**
     * 指定 Spring 的配置类
     * @return
     */
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }

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

    /**
     * 指定 DispatcherServlet 的映射规则
     * @return
     */
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    /**
     * 注册过滤器
     * @return
     */
    @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};
    }
    
    // 如果还想注册监听器等等,只需要再重写相关方法就可以了。
}

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

/**
 * @author zxy
 * @date 2022/1/10 9:37
 * 代替 SpringMVC 的配置文件:
 * 1、扫描组件   2、视图解析器     3、view-controller   4、default-servlet-handler
 * 5、mvc 注解驱动   6、文件上传解析器   7、异常处理  8、拦截器 ...
 */
@Configuration  // 将当前类标识为一个配置类
@ComponentScan("com.zxy")  // 扫描组件
@EnableWebMvc   // 注解驱动
public class WebConfig implements WebMvcConfigurer { // 这个接口中提供了很多配置组件的方法

    // 开启静态资源处理
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    // 拦截器
    public void addInterceptors(InterceptorRegistry registry) {
        OneInterceptor oneInterceptor = new OneInterceptor();
        registry.addInterceptor(oneInterceptor).addPathPatterns("/**");
    }

    // view-controller
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/hello").setViewName("hello");
    }

    // 异常处理
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
        Properties properties = new Properties();
        properties.setProperty("java.lang.ArithmeticException", "error");
        exceptionResolver.setExceptionMappings(properties);
        exceptionResolver.setExceptionAttribute("ex");
        resolvers.add(exceptionResolver);
    }

    // 文件上传解析器
    @Bean
    public MultipartResolver multipartResolver() {
        CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
        return commonsMultipartResolver;
    }

    // 配置 thymeleaf 视图解析器
    // 配置生成模板解析器
    @Bean
    public ITemplateResolver templateResolver() {
        WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
        // ServletContextTemplateResolver 需要一个 ServletContext 作为构造参数,所以通过 WebApplicationContext 中的方法获取
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(
                webApplicationContext.getServletContext());
        templateResolver.setPrefix("/WEB-INF/templates/");
        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;
    }

}

十三、SpringMVC 执行流程

1、SpringMVc 常用组件

  • DispatcherServlet:前端控制器(中央处理器),不需要工程师开发,由框架提供

    作用:统一处理请求和响应,整个流程控制的中心,由它调用其他组件处理用户的请求。

  • HandlerMapping:处理器映射器,不需要工程师开发,由框架提供

    作用:根据请求的 url、method 等信息查找 Handler,即控制器方法。

  • Handler:处理器,由工程师开发

    作用:在 DispatcherServlet 的控制下 Handler 对具体的用户请求进行处理。

  • HandlerAdapter:处理器适配器,不需要工程师开发,由框架提供

    作用:通过 HandlerAdapter 对处理器(控制器方法)进行执行。

  • ViewResolver:视图解析器,不需要工程师开发,由框架提供

    作用:进行视图解析,得到相应的视图。

  • View:视图,不需要工程师开发,由框架或者视图技术提供

    作用:将模型数据通过页面展示给用户

2、DispatcherServlet 初始化过程

DispatcherServlet 本质上是一个 Servlet,所以天然的遵循 Servlet 的生命周期。所以宏观上是 Servlet 生命周期来进行调度。

Servlet:init(ServletConfig):void ---->

GenericServlet:重写 init(ServletConfig):void、重载init():void ---->

HttpServletBean:重写 init():void 调用 initServletBean():void ---->

FrameworkServlet:重写 initServletBean():void 调用 initWebApplicationContext()

posted @ 2022-06-07 10:25  zxy_1221  阅读(54)  评论(0)    收藏  举报