SpringMVC

image-20230612101158344

1.1什么是MVC

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

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

JavaBean分为两类:

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

V:View,视图层:指工程中的html或jsp等页面,作用是与用户进行

交互,展示数据

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

MVC的工作流程:

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

1.2什么是SpringMVC

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

Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行Web开发时,可以选择使用Spring的Spring MVC框架。

1.3SpringMVC的特点

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

2.SpringMVC常用组件和执行流程

  • DispatcherServlet前端控制器,不需要工程师开发,由框架提供

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

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

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

  • Handler处理器(控制器),需要工程师开发

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

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

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

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

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

  • View视图

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

这些组件共同协作来处理用户的HTTP请求,他们的执行流程是:

  1. 当一个HTTP请求到达服务器时,DispatcherServlet会接收到这个请求。

  2. DispatcherServlet会调用HandlerMapping(处理器映射器)来查找与该请求对应的Handler(处理器)。

  3. HandlerMapping会根据请求的URL、method等信息查找与该请求对应的Handler,并将其返回给DispatcherServlet

  4. DispatcherServlet会调用HandlerAdapter(处理器适配器)来执行找到的Handler

    • 调用getHandlerAdapter方法会遍历所有已注册的处理器适配器,并调用它们的supports方法来检查它们是否支持当前请求的处理器。如果找到了一个支持当前请求的处理器适配器,那么该方法就会返回这个处理器适配器,然后用处理器适配器执行处理器。
    • 这里使用了适配器模式:**将不同类型的处理器(Handler)转换为统一的接口,以便能够使用统一的方式来处理HTTP请求。**
  5. HandlerAdapter会调用相应的控制器方法来处理用户的请求,并返回一个包含模型数据和视图名称的ModelAndView对象。

  6. DispatcherServlet会调用ViewResolver(视图解析器)来解析ModelAndView对象中的视图名称,得到相应的View(视图)对象。

  7. DispatcherServlet会调用找到的视图对象来渲染模型数据,并将渲染结果返回给客户端。

3.SpringMVC框架环境搭建

  1. 创建Maven工程

  2. 配置pom.xml配置文件,添加相应的依赖

依赖中设计Thymeleaf的相关知识,下面介绍什么是Thymeleaf:

Thymeleaf是面向Web和独立环境的现代服务器端Java模板引擎,能够处理HTML,XML,JavaScript,CSS甚至纯文本。 Thymeleaf的主要目标是提供一个优雅和高度可维护的创建模板的方式

 <groupId>com.bilibili</groupId>
    <artifactId>SpringMVC</artifactId>
    <version>1.0-SNAPSHOT</version>
<!--    设置打包方式-->
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
<!--        springMVC依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.16</version>
        </dependency>
<!--日志-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-site</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>
<!--        spring和thymeleaf整合包-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.12.RELEASE</version>
        </dependency>
    </dependencies>
  1. 在main文件夹下添加Web模块

image-20230612103117244

创建后是一个普通文件,要将其更改为web模块进行以下配置:

image-20230612103144834

  1. 添加web.xml的配置文件

接下来修改web.xml配置文件的路径,路径为webapp目录下

image-20230612103229592

创建成功

image-20230612103237668

  1. 配置web.xml

一:默认的配置方式

此配置作用下,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>

二:扩展的配置方式

可通过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>
                 <param-name>contextConfigLocation</param-name>
                 <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>
             <url-pattern>/</url-pattern>
         </servlet-mapping>

在resources文件中创建SpringMVC配置文件

  1. 创建控制器(作用:请求响应)

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

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

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

一:创建控制器

image-20230612103430479

二:对控制器类添加注解

@Controller 
//对此类添加注解,设置为控制层组件,交由Spring的IOC容器进行管理
public class control1 {

}

三:配置SpringMVC配置文件

一:开启注解扫描,将Control控制器类交由IOC容器管理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解扫描-->
    <context:component-scan base-package="com.bilibili.Sevlet.Controller"></context:component-scan>
</beans>

四:出现以下图标表示该类已经交由IOC容器进行管理

  1. 配置Thymeleaf视图解析器
<!-- 配置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>

目前完整的SpringMVC.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="cn.edu.pdsu.controller"></context:component-scan>
    <!-- 配置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>
</beans>

4.SpringMVC项目实测

  1. 为HTMl页面添加Thymeleaf命名空间

接下就可以使用Thymeleaf语法

image-20230612103636558

  1. 测试项目

一:首先将Tomcat服务器集成到Idea中

二:配置控制器,添加方法,返回视图名称

@Controller
//对此类添加注解,设置为控制层组件,交由Spring的IOC容器进行管理
public class control1 {
//@RequestMapping注解:处理请求和控制器方法之间的映射关系
//@RequestMapping注解的value属性可以通过请求地址匹配请求,
//value 的值为当前工程的资源路径
    @RequestMapping(value = "/")
    public String index(){
//        返回视图名称 index
        return "index";
    }
}

三:开启服务器

image-20230612103750283

5.@RequestMapping注解详解

5.1@RequestMapping注解的功能

@RequestMapping注解的作用:将请求和处理请求的控制器方法关联起来,建立映射关系

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

5.2@RequestMapping注解的位置

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

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

采用@RequestMapping标识一个类的目的是对于不同的控制器,如果里面的方法的@RequestMapping注解value值相同的话就会出现错误(value值唯一)

5.3@RequestMapping注解的value值

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

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

如下: 下面两个地址满足一个就可以

@RequestMapping(value = {"/target","/second"})
public String target(){
    return "target";
}

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

5.4@RequestMapping注解的method属性

@RequestMapping注解的method属性通过请求的请求方式(get或post)匹配请求映射

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

当没有设置method的属性时,就是不以请求方式条件,任何的请求方式都能够匹配

若当前请求的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则浏览器报错405

实例:

一:设置method属性值为POST,表单的提交方式也为POST

5.5@RequestMapping注解的派生注解

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

处理get请求的映射–>@GetMapping

处理post请求的映射–>@PostMapping

处理put请求的映射–>@PutMapping

处理delete请求的映射–>@DeleteMapping

如下图测试@PostMapping注解:

5.6@RequestMapping注解的params属性

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

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

“param”:要求请求映射所匹配的请求必须携带param请求参数

“!param”:要求请求映射所匹配的请求必须不能携带param请求参数

“param=value”:要求请求映射所匹配的请求必须携带param请求参数且param=value

“param!=value”:要求请求映射所匹配的请求必须携带param请求参数但是param!=value

5.7@RequestMapping注解的heards属性

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

“header”:要求请求映射所匹配的请求必须携带header请求头信息

“!header”:要求请求映射所匹配的请求必须不能携带header请求头信息

“header=value”:要求请求映射所匹配的请求必须携带header请求头信息且header=value

“header!=value”:要求请求映射所匹配的请求必须携带header请求头信息且header!=value

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

测试:

查看请求头信息

<!--   拼接请求参数-->
   <a th:href="@{/target(username ,password = 123)}">测试Heards</a>

设置headers的端口号为8081(默认端口号为8080),出现以下错误。

 @RequestMapping(value = "/target",
                    params = {"username","password=123"},
//            要求请求头中含有以下键值对
                    headers = {"Host=localhost:8081"})
    public String params(){
        return "target";
    }

image-20230612104648186

当端口号为8080时,页面跳转正常

image-20230612104700551

5.8SpringMVC支持ant风格的路径

?:表示任意的单个字符

控制器中设置资源路径如下:

@RequestMapping(value = "/a?a/testAnt")
public String testAnt(){
    return "target";
}
<a th:href="@{/aaa/testAnt}">测试ant风格</a>

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

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

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

5.9SpringMVC支持路径中的占位符

  • 原始方式:/deleteUser?id=1
  • rest方式:/deleteUser/1

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

但是注意通过这种方法传递参数,不能够通过Request请求来获取参数,因为通过此种方法传递的参数只有属性值,没有属性名

控制器:

//通过占位符来表示传输的数据
@RequestMapping(value = "/testPath/{id}/{username}")
//@PathVariable注解将占位符所表示的数据赋值给控制器方法的形参
public String testPath(@PathVariable("id") Integer id,@PathVariable("username") String username){
    System.out.println("学号:"+id);
    System.out.println("姓名:"+username);
    return "target";
}
\<a th:href="@{/testPath/201530326/神里绫华}"\>测试路径中的占位符\</a\>

页面跳转成功,并且输出数据

image-20230612104919341

6.SpringMVC获取到请求参数

6.1首先解决SpringMVC获取到请求参数乱码的问题(通过过滤器实现)

问题如下:

image-20230612105003363

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

注:

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

<!--配置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>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

设置后编码正确显示

6.2通过ServletAPI获取请求参数

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

页面跳转控制器

@RequestMapping("/")
public String param(){
    return "testParams";
}

处理器请求的控制器:获取到请求并输出

@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request){
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    System.out.println(username+":"+password);
    return "target";
}
<a th:href="@{/testServletAPI(username='神里绫华',password=1234)}">
测试使用ServletAPI获取请求参数</a>

image-20230612105122998

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

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

<a th:href="@{/testParams(username='神里绫华',password=1234)}">
测试使用控制器形参获取请求参数</a>

通过控制器方法的形参来获取请求参数与通过ServletAPI获取请求参数的方式传递的参数不同

@RequestMapping("/testParams")
public String testParams(String username,int password){
    System.out.println(username+":"+password);
    return "target";
}

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

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

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

下面以表单为例:

<form th:action="@{/testParamsForm}" method="post">
    用户名: <input type="text" name="username">
    密码: <input type="password" name="password">
    爱好: <input type="checkbox" name="hobby" value="骑行">骑行
         <input type="checkbox" name="hobby" value="跑步">跑步
         <input type="submit" value="提交">
</form>
@RequestMapping("/testParamsForm")
public String testParamsFrom(String username,int password,String[] hobby){
    System.out.println(username+":"+password+":"+ Arrays.toString(hobby));
    return "target";
}

image-20230612105419883

image-20230612105426968

6.4@RequestParam注解处理请求参数和控制器方法的形参的映射关系

对于上面的“通过控制器的形参来获取请求参数”时,如果形参和请求参数名不相同,那么就会出现空值的情况。

对于这种问题,就需要采用@RequestParam注解的方式来解决

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

value:指定为形参赋值的请求参数的参数名

required:设置是否必须传输此请求参数,默认值为true,表示必须传输

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

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

//下面表示用形式username来表示请求参数user_name
//并设置为不必须填写该请求参数
//若请求参数为null,就将其设置默认值“神里绫华”
@RequestParam( 
value = "user_name",
required = false,
defaultValue = "神里绫华") String username)

image-20230612105749811

image-20230612105820057

6.5@RequestHeader注解处理请求头信息和控制器方法的形参的映射关系

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

@RequestHeader注解一共有三个属性:valuerequireddefaultValue,用法同@RequestParam

image-20230612112810176

6.6@CookieValue注解处理Cookie数据和控制器方法的形参的映射关系

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

@CookieValue注解一共有三个属性:valuerequireddefaultValue,用法同@RequestParam

@CookieValue("Idea-c500deca") String JSESSIONID)

image-20230612112900553

6.7通过实体类(POJO)来获取请求参数s

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

测试:

实体类

image-20230612113059840

以表单形式发送请求参数,请求参数名和User类属性名一致

image-20230612113109177

控制器:

@RequestMapping("/testJOPO")
public String testJOPO(User user){
    System.out.println(user);
    return "target";
}

输出结果:

image-20230612113213223

7.作用域对象共享数据

7.1什么是作用域对象

一个有作用范围的对象(在Servlet之间),可以在范围内共享数据

只要是域对象都有如下方法

  • setAttribute(name,value);存储数据的方法
  • getAttribute(name);根据name获取对应数据值
  • removeAttribute(name);删除数据

7.2作用域对象的分类

下面作用域对象的作用范围按照从大到小的顺序

image-20230612113422577

7.3向Request域共享数据

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

  1. 创建控制器,使用ServletAPI向Request域对象中添加数据
//    使用ServletAPI向Request域对象共享数据
    @RequestMapping("/testRequestByServletAPI")
    public String testRequestByServletAPI(HttpServletRequest request){
        //        向request域对象共享数据
        request.setAttribute("username","神里绫华");
        return "target";
    } 
  1. index页面
<a th:href="@{/testRequestByServletAPI}">
使用ServletAPI向Request域对象共享数据</a>
  1. target页面:根据键名在Request域中获取到数据
<h1 th:text="${username}"></h1>

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

  1. ModelAndView对象的作用

MVC模型中的,M为模型(Model),V为视图(View)。ModelAndView 是一种变量类型,这类类型包含了 模型和视图的数据。但返ModelAndView 时,即把模型数据和视图数据一起返回。一般把ModelAndView 数据返回给浏览器,若浏览器根据模型数据和视图数据来渲染网页。等于ModelAndView把Model 和 View 的数据打包在一起

  1. ModelAndView对象的使用

多写在控制器中。一个方法若要返回 ModelAndView 变量,必需在这个方法的参数里有 ModelAndView或者方法体中含有ModelAndView对象

控制器:

    @RequestMapping("/testModelAndView")
    public ModelAndView testModelAndView(ModelAndView mav){
//        处理模型数据,即向请求域request共享数据
        mav.addObject("username","神里绫华参上");
//        设置视图名称
        mav.setViewName("target");
        return mav;
    }
<a th:href="@{/testModelAndView}">
使用ModelAndView对象向Request域对象共享数据</a>

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

与ModelAndView不同的是,Model对象只能在方法的参数中定义Mdoel对象

控制器:

@RequestMapping("/testModel")
public String testModel(Model model){
    //        向request域对象共享数据
    model.addAttribute("username","稻妻神里流太刀术皆传——神里绫华参上!");
    return "target";

}
<a th:href="@{/testModel}">
使用Model对象向Request域对象共享数据</a>

7.3.4使用Map集合向request域对象共享数据

使用Map集合就是使用键值对的方式

控制器:

    @RequestMapping("/testMap")
    public String testMap(Map<String,Object> map){
//        向request域对象共享数据
        map.put("username","神里流·霜灭");
        return "target";
    }
<a th:href="@{/testMap}">使用Map集合向Request域对象共享数据</a>

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

控制器:

    @RequestMapping("/testModelMap")
    public String testModelMap(ModelMap modelMap){
//        向request域对象共享数据
        modelMap.addAttribute("username","起舞吧!");
        return "target";
    }
<a th:href="@{/testModelMap}">
使用ModelMap对象向Request域对象共享数据</a><br>

7.3.6Model、Map和我ModelMap之间的关系

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

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

7.4使用ServtAPI向Session域共享数据

<a th:href="@{/testSession}">
使用ServletAPI向Session域对象共享数据</a><br>

控制器:

    @RequestMapping("/testSession")
    public String testSession(HttpSession session){
//        向session域对象共享数据
        session.setAttribute("username","Hello,Session!");
        return "target";
    }

在目标页面通过session点键值就可以获得Session域中的对象

<h1 th:text="${session.username}"></h1>

7.5使用ServtAPI向application域共享数据

<a th:href="@{/testApplication}">
使用ServletAPI向Application域对象共享数据</a><br>

控制器:

    @RequestMapping("/testApplication")
    public String testApplication(HttpSession session){
//        向rapplication域对象共享数据
        ServletContext application = session.getServletContext();
        application.setAttribute("username","Hello,application!");
        return "target";
    }

在目标页面通过session点键值就可以获得Session域中的对象

<h1 th:text="${application.username}"></h1>

8.SpringMVC的视图

8.1SpringMVC视图介绍

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

SpringMVC视图的种类很多,默认有转发视图InternalRescourseView和重定向视图RedirectView

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

8.2资源跳转的两种方式—请求转发和重定向的区别

1:请求转发是服务器收到请求后为了完成响应在服务器内部转到另一个资源。重定向是浏览器发送请求并收到响应以后再次向一个新地址发请求。

2:转发只产生一次请求对象且在组件间共享数据,重定向中有两次请求对象,不共享数据。

3:请求转发的方式地址栏地址不会改变,而重定向的方式会发生改变。

4:转发必须是同一个应用内的某个资源,重定向的新地址可以是任意地址。

请求转发:

重定向:

8.3ThymeleafView

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

上面的视图名称为target,经过拼接后为 target.html,最终通过转发方式实现页面的跳转

8.4转发视图

SpringMVC中默认的转发视图是InternalResourceView

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

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

添加forward后,就不会被我们配置的thymeleaf解析器解析,而是相当于一个普通请求路径,然后去和@RequestMapping中的映射路径匹配

8.5重定向视图

SpringMVC中默认的重定向视图是RedirectView

当控制器方法中所设置的视图名称以”redirect:”为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀”redirect:”去掉,剩余部分作为最

终路径通过重定向的方式实现跳转

例如:

@RequestMapping("testRedirectView")
public String testRedirectView(){
    return "redirect:/testThymeleafView";
}

8.6视图控制器view-controller

是一种SpringMVC配置文件中的标签

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

如跳转首页的控制器方法:

image-20230612115734456

更改为由视图控制器来实现页面跳转(在SpringMVC配置文件中添加标签)

<!--    在请求映射对应的控制器方法中,
除了页面跳转没有其他请求过程的处理,
就设置视图控制器-->
<!--
path:设置处理的请求地址
view-name:设置请求地址所对应的视图名称
-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>

视图正常显示

9.RESTFul

9.1RESTFul概念

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

RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML格式定义或JSON格式定义。RESTFUL适用于移动互联网厂商作为业务接口的场景,实现第三方OTT调用移动网络资源的功能,动作类型为新增、变更、删除所调用资源。

9.2RESTful的实现

具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GETPOSTPUTDELETE。它们分别对应四种基本操作: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请求

9.2.1RESTFul实现GET和POST操作

//    使用RESTFul模拟用户资源的增删改查

/*   /users     GET   查询所有用户信息
*    /user/1   GET   根据id查询用户信息
*    /user     POST    添加用户信息
*    /user/1   DELETE   根据id删除用户
*    /user     PUT   更新用户信息
* */
    @RequestMapping(value = "/user",method = RequestMethod.GET)
    public String findAll(){
        System.out.println("查询所用用户信息!");
        return "target";
    }

    @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
    public String findUSer(){
        System.out.println("根据id查询用户信息!");
        return "target";
    }

    @RequestMapping(value = "/user",method = RequestMethod.POST)
    public String addUSer(String username,String password ){
        System.out.println("添加用户——>姓名:"+username+"密码:"+password);
        return "target";
    }

9.2.2RESTFul实现PUT和DELETE操作

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

SpringMVC 提供了 HiddenHttpMethodFilter(是一个过滤器) 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求,其中HiddenHttpMethodFilter 处理put和delete请求的条件:

  • 当前请求的请求方式必须为post

  • 当前请求必须传输请求参数_method

满足以上条件,HiddenHttpMethodFilter 过滤器就会将当前请求的请求方式转换为请求参数_method的值,因此请求参数_method的值才是最终的请求方式。

  1. 在Web.xml配置文件中添加HiddenHttpMethodFilter 过滤器
<!--    配置HiddenHttpMethondFIlter-->
    <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>
  1. 设置控制器,将method设置为PUT请求方式
@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String updateUSer(String username,String password ){
    System.out.println("修改用户——>姓名:"+username+"密码:"+password);
    return "target";
}
  1. 通过表单发送请求
<!--   设置请求方式为POST-->
   <form th:action="@{/user}" method="post">
<!--       传输请求参数为_method,并设置为PUT请求-->
       <input type="hidden" name="_method" value="PUT">
       用户名:<input type="text"><br>
       密码:<input type="password"><br>
       <input type="submit" value="提交"><br>
   </form>

9.2.3RESTFul案例

  1. 搭建环境

创建Maven工程,配置pom.xml,接着配置web.xml,再创建SpringMVC配置文件并且配置SpringMVC配置文件

  1. 创建实体类:

  1. 创建控制器:

  1. 创建Dao:

10.HttpMessageConverter

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

HttpMessageConverter提供了两个注解和两个类型:

  • @RequestBody(请求体), RequestEntity(请求实体) 将请求报文转换为java数据
  • @ResponseBody(响应体) ,ResponseEntity(响应实体) 将java信息转化为响应报文

10.1@RequestBody

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

form表单

<form th:action="@{/testRequestBody}" method="post">
    <input type="text" name="username"><br>
    <input type="password" name="password"><br>
    <input type="submit" value="测试@requestBody">
</form>

控制器:

@RequestMapping(value = "/testRequestBody")
//为形参添加@RequestBody,这样就可以获得请求体
public String testRquestBody(@RequestBody String requestBody){
    System.out.println("requestBody:"+requestBody);
    return "target";
}

image-20230612124759258

10.2@RequestEntity

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

<form th:action="@{/testRequestEntity}" method="post">
    <input type="text" name="username"><br>
    <input type="password" name="password"><br>
    <input type="submit" value="测试@requestEntity">
</form>
    @RequestMapping(value = "testRequestEntity")
    public String testRequestEntity(RequestEntity<String> requestEntity){
//        当前requestEntity表示整个请求报文信息
        System.out.println("请求头:"+requestEntity.getHeaders());
        System.out.println("请求体:"+requestEntity.getBody());
        return "target";
    }

image-20230612125144690

10.3@ResponseBody

@ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器

不加注解默认被视图解析器解析,所以匹配完跳转页面,加上注解看到的是return作为响应体的网页内容

不添加RespondesBody注解

@RequestMapping(value = "/testRespondseBody")
public String testRespondseBody() {
    return "target";
}

添加RespondesBody注解

@RequestMapping(value = "/testRespondseBody")
@ResponseBody
public String testRespondseBody() {
    return "target";
}

image-20230612125317059

10.4SpringMVC处理json(数据交互格式)

@ResponseBody处理json的步骤:

  1. 导入jackson的依赖
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.1</version>
</dependency>

image-20230612125358914

  1. 在SpringMVC的核心配置文件中开启mvc的注解驱动,此时在HandlerAdaptor中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将响应到浏览器的Java对象转换为Json格式的字符串
<!--开启MVC的注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
  1. 在控制器方法上使用@ResponseBody注解进行标识
@RequestMapping(value = "testRequestBodyUser")
@ResponseBody
public User testRequestBodyUser(){
    return new User("201530326","神里绫华",18);
}
  1. 将Java对象直接作为控制器方法的返回值返回,就会自动转换为Json格式的字符串

image-20230612125510313

10.5@RestController注解

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

10.6@RespondesEntity注解

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

11.拦截器

11.1拦截器简介

Spring MVC 的拦截器(Interceptor)与 Java Servlet 的过滤器(Filter)类似,它主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。

在系统中,经常需要在处理用户请求之前和之后执行一些行为,例如检测用户的权限,或者将请求的信息记录到日志中,即平时所说的“权限检测”及“日志记录”。当然不仅仅这些,所以需要一种机制,拦截用户的请求,在请求的前后添加处理逻辑。

11.2创建拦截器

  1. 分别创建拦截器类和拦截器控制器

image-20230612130144486

  1. 拦截器类实现HandlerInterceptor接口

HandlerInterceptor接口中的方法有三个,快捷键Ctrl+o实现方法

image-20230612130227103

  • preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
  • postHandle:控制器方法执行之后执行postHandle()
  • afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()

11.3配置拦截器

  1. 在SpringMVC的配置文件中配置拦截器

将实现HandlerInterceptor接口的类作为拦截器

<!-- 配置拦截器 -->
<mvc:interceptors>
    <!--    IOC组件,将FirstIntercopter对象作为拦截器    -->
    <bean class="pdsu.edu.mvc.intercopter.FirstIntercopter"></bean>
</mvc:interceptors>
  1. 开启服务器测试拦截器效果

执行preHandle方法

image-20230612130356145

但是另外两个方法并没有执行,并且并没有跳转到首页,原因是因为拦截器执行了拦截

拦截的实现是因为preHandle方法中返回值为false

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("FirstIntercopter-->preHandle");
    return false;
}

综上,当preHandle方法中的返回值为false时,执行拦截,当为true时,放行

11.4配置专一拦截器(不对所有请求进行拦截)

以下两种引用IOC组件默认是对所有的请求进行拦截

<mvc:interceptors>
    <!--    引用IOC组件,将FirstIntercopter对象作为拦截器    -->
    <bean class="pdsu.edu.mvc.intercopter.FirstIntercopter"></bean>
    <ref bean="firstIntercopter"></ref>
</mvc:interceptors>

对于以上问题,可以采用对指定的路径进行拦截

   <!-- 配置拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
<!--            设置拦截路径,/*表示上下文一层目录,/**表示拦截所有-->
            <mvc:mapping path="/*"/>
<!--            排除拦截路径首页-->
            <mvc:exclude-mapping path="/"/>
<!--    引用IOC组件,将FirstIntercopter对象作为拦截器    -->
            <ref bean="firstIntercopter"></ref>
        </mvc:interceptor>
    </mvc:interceptors>

对于以上的拦截器,对首页不进行拦截,上下文路径进行拦截

11.5多个拦截器的执行顺序

一:若每个拦截器的preHandle()都返回true,此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:

preHandle()会按照配置的顺序执行

postHandle()和afterComplation()会按照配置的反序执行

二:若某个拦截器的preHandle()返回了false,preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行

12.异常处理器

12.1基于配置的异常处理

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

HandlerExceptionResolver接口的实现类有

DefaultHandlerExceptionResolver

SimpleMappingExceptionResolver

SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver

控制器:测试算术异常

@RequestMapping(value = "testExepectionHander")
public String testExepectionHander(){
    System.out.println(1/0);
    return "target";
}

配置SpringMVC配置文件

<!--    配置异常处理-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
<!--                添加异常的全类名-->
                <prop key="java.lang.ArithmeticException">error</prop>
            </props>
        </property>
<!--       设置将异常信息共享在请求域中的键-->
        <property name="exceptionAttribute" value="exception"></property>
    </bean>

在错误提示页面中获取到请求域中的键

<h2 th:text="${exception}"></h2>

12.2基于注解的异常配置

控制器:测试算术异常

@RequestMapping(value = "testExepectionHander")
public String testExepectionHander(){
    System.out.println(1/0);
    return "target";
}

异常处理控制器

//@ControllerAdvice注解将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController {
    @ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})
    public String testExepection(Exception e, Model model){
//        通过Model对象向request域中共享异常信息
        model.addAttribute("exception",e);
        return "error";
    }
}

在错误提示页面中获取到请求域中的键

<h2 th:text="${exception}"></h2>

12.3注解配置SpringMVC

采用配置类的方式来代替web.xml配置文件和SpringMVC配置文件

web.xml配置类

//web工程的初始化类,代替web.xml
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
    //   指定Spring的配置类
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }

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

    //   指定DispatcherServlet的映射规则,即url-pattern
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    //    设置过滤器
    protected Filter[] getServletFilters() {
//        编码过滤器
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("utf-8");
        characterEncodingFilter.setForceRequestEncoding(true);
//      处理请求过滤器
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        return new Filter[]{characterEncodingFilter,hiddenHttpMethodFilter};
    }
}

SpringMVC配置文件

//代替SpringMVC的配置文件
/*
 * 1.扫描组件
 * 2.视图解析器
 * 3.视图控制器
 * 4.default-servlet-handle(静态资源的访问)
 * 5.mvc注解驱动
 * 6.文件上传解析器
 * 7.异常处理
 * 8.拦截器
 * */
//将当前类标识为一个配置类
@Configuration
//组件扫描
@ComponentScan("cn.edu.mvc.controller")
//开启MVC注解驱动
@EnableWebMvc
public class SpringMVCConfig implements WebMvcConfigurer {

    //    配置生成模板解析器
    @Bean
    public ITemplateResolver templateResolver() {
        WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
        // ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过 WebApplicationContext 的方法获得
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver( webApplicationContext.getServletContext());
        templateResolver.setPrefix("/WEB-INF/pages/");
        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;
    }
//    生成Thymeleaf视图解析器,并为解析器注入模板引擎
    @Bean
    public ViewResolver viewResolver(SpringTemplateEngine springTemplateEngine){
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setCharacterEncoding("UTF-8");
        viewResolver.setTemplateEngine(springTemplateEngine);
        return viewResolver;
    }

//    default-servlet-handle(静态资源的访问)
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

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

//视图控制器
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/hello").setViewName("hello");
    }

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

//  异常解析器
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
        Properties prop = new Properties();
        prop.setProperty("java.lang.ArithmeticException","error");
        exceptionResolver.setExceptionMappings(prop);
//      将异常信息发送到请求域中
        exceptionResolver.setExceptionAttribute("exception");
        resolvers.add(exceptionResolver);
    }
}

14.总结:SpringMVC的执行流程

  1. 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。

  2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:

  • 不存在

    • 再判断是否配置了mvc:default-servlet-handler(静态资源访问)
    • 如果没配置,则控制台报映射查找不到,客户端展示404错误
    • 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示404错误
  • 存在则执行下面的流程

  1. 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。

  2. DispatcherServlet 根据获得的Handler,选择一个合适HandlerAdapter。

  3. 如果成功获得HandlerAdapter(处理器适配器),此时将开始执行拦截器的preHandler(…)方法【正向】

  4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

    • HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
    • 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
    • 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
    • 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
  5. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。

  6. 此时将开始执行拦截器的postHandle(…)方法【逆向】。

  7. 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。

  8. 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。

  9. 将渲染结果返回给客户端。


SpringMVC
https://xhablog.online/2022/06/20/SpringMVC/
作者
Xu huaiang
发布于
2022年6月20日
许可协议