SpringMVC框架学习
SpringMVC框架学习

SpringMVC框架学习

一、SpringMVC简介

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视图,渲染数据后最终响应给浏览器

2、什么是SpringMVC

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

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

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

3、SpringMVC的特点

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

二、入门案例

1、引入依赖

<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、配置前端控制器

在web.xml配置文件中进行配置

  • 目的:配置前端控制器,对游览器的请求统一处理

  • 默认方式(通常不使用)

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

<servlet>
    <servlet-name>SpringMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>SpringMVC</servlet-name>
    <url-pattern>/</url-pattern>
    <!--
    设置springMVC的核心控制器所能处理的请求的请求路径
    /所匹配的请求可以是/login或.html或.js或.css方式的请求路径
    但是/不能匹配.jsp请求路径的请求
    -->
</servlet-mapping>
  • 拓展方式

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

<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>
      <!--
   作为框架的核心组件,在启动过程中有大量的初始化操作要做
而这些操作放在第一次请求时才执行会严重影响访问速度
因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时
   -->
  </servlet>
  <servlet-mapping>
      <servlet-name>SpringMVC</servlet-name>
      <url-pattern>/</url-pattern>
  </servlet-mapping>

注:

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

/所匹配的请求可以是/login或.html或.js或.css方式的请求路径,但是/不能匹配.jsp请求路径的请求

因此就可以避免在访问jsp页面时,该请求被DispatcherServlet处理,从而找不到相应的页面

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

3、创建请求控制器

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

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

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

import org.springframework.stereotype.Controller;

/*创建控制器*/
@Controller
public class FirstController {

}

4、创建SpringMVC的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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.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>

</beans>

5、测试

@RequestMapping注解:

  1. 处理请求和控制器方法之间的映射关系

  2. value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径

  • 在请求控制器中创建处理请求的方法
/*创建控制器*/
@Controller
public class FirstController {

    // @RequestMapping注解:处理请求和控制器方法之间的映射关系
    // @RequestMapping注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径
    /*跳转到/WEB-INF/templates/index.html*/
    @RequestMapping( "/") //value=("/")
    public String index(){
        /*返回视图名称*/
        return "index";
    }

    /*请求跳转到target.html页面*/
    @RequestMapping("/target")
    public String toTarget(){
        return "target";
    }
}
  • 在主页index.html中设置超链接
<!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> <!--使用thymeleaf语法书写绝对路径-->
</body>
</html>

6、总结

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

三、@RequestMapping注解

1、@RequestMapping注解的功能

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

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

注:在同一目录下,@RequestMapping注解的value值不能相同,除非在类上也添加@RequestMapping注解,将他们设置为不同目录

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

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

2、@RequestMapping注解的位置

  • @RequestMapping标识一个类:设置映射请求的请求路径的初始信息(value值加在具体信息之前)

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

@Controller
/*在类上添加@RequestMapping注解表示在类中方法返回的值上添加类上的value值*/
@RequestMapping("/success")
public class RequestMappingController {

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

访问链接地址

<a th:href="@{/success/success}">请求跳转到success.html</a>

3、@RequestMapping注解的value属性

  • @RequestMapping注解的value属性通过请求的地址匹配请求映射
  • @RequestMapping注解的value属性是一个字符串类型的数组(String[]),表示该请求映射能够匹配到多个请求地址所对应的请求
  • @RequestMapping注解的value属性必须设置,至少通过请求地址匹配请求映射
@RequestMapping(value={"/success" ,"/success2"}) //满足其中一个请求地址即可
public String success(){
    return "success";
}
<a th:href="@{/success/success}">请求跳转到success.html</a> <br>
<a th:href="@{/success/success2}">请求跳转到success.html</a> <br>

注:若value属性不匹配,页面报错404

4、@RequestMapping注解的method属性

  • @RequestMapping注解的method属性通过请求的方式(get、post)匹配请求映射
  • @RequestMapping注解的method属性是一个RequestMethod类型的数组( {RequestMehod.GET} ),表示该请求映射能够匹配多种请求方式的请求
  • 若当前的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则游览器会报405错误
@RequestMapping(
        value = "method",
        method = {RequestMethod.GET,RequestMethod.POST}
)
public String method(){
    return "success";
}
<form th:action="@{/success/method}" method="get">
    <input type="submit">
</form>

注:若method属性不匹配,页面报错405

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

@PostMapping("/method") //同时指定了value值与method值
public String method2(){
 return "success";
}

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

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

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

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

2、常用的请求方式有get,post,put,delete

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

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

5、RequestMapping注解的params属性

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

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

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

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

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

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

/*验证params属性*/
@RequestMapping(
        value = "/param",
        method = {RequestMethod.GET,RequestMethod.POST},
        params = "username=admin" //表明请求参数中必须有username参数且值为admin
)
public String param(){
    return "success";
}
<a th:href="@{/success/param(username='admin')}">验证params属性</a>

注:若当前请求满足@RequestMapping注解的value和method属性,但是不满足params属性,此时页面回报错400

6、RequestMapping注解的headers属性(不常用)

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

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

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

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

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

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

/*验证headers属性*/
@RequestMapping(
        value = "headers",
        headers = {"Host=localhost:8080"}
)
public String headers(){
    return "success";
}
<a th:href="@{/success/headers}">验证headers属性</a>

7、SpringMVC支持ant风格书写的路径

  • ?:表示任意的单个字符

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

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

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

/*验证ant风格路径*/
@RequestMapping(
        /*value = "/a?a/ant" 单个字符*/
        /*value = "/a*a/ant" 0个或多个字符*/
        value = "a/**/ant" //表示多层目录
)
public String ant(){
    return "success";
}

8、SpringMVC路径中的占位符

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

  • rest方式:/deleteUser/1

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

/*验证SpringMVC中的占位符*/
@RequestMapping(
        value = "/placeholder/{id}/{username}"
)
public String placeholder(@PathVariable("id") Integer id, @PathVariable("username") String username){ //可以获取客户端请求的参数
    System.out.println(id);
    System.out.println(username);
    return "success";
}
<a th:href="@{/success/placeholder/100/marry}">验证SpringMVC中的占位符</a>

四、SpringMVC获取请求参数

1、通过ServletAPI获取

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

/*测试使用ServletAPI获取请求参数*/
@RequestMapping("/param1")
public String param1(HttpServletRequest request){
    String id = request.getParameter("id");
    String username = request.getParameter("username");
    System.out.println("id="+id);
    System.out.println("username="+username);
    return "success";
}
<a th:href="@{/param1(id=100,username='admin')}">获取请求参数</a>

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

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

/*测试使用控制器方法的形参获取请求参数*/
@RequestMapping("/param2")
public String param2(String username,Integer password){
    System.out.println("username: "+username);
    System.out.println("password: "+password);
    return "success";
}
<form th:action="@{/param2}" method="get">
    用户名:<input type="text" name="username" value="用户名">
    密码:<input type="text" name="password" value="密码">
    <input type="submit">
</form>

注:

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

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

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

3、@RequestParam

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

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

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

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

若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错400:Required String parameter 'xxx' is not present;若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null

  • defaultValue:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为""时,则使用默认值为形参赋值
/*当请求参数和控制器方法形参不同名时,
使用@RequestParam注解将 请求参数和控制器方法形参形成映射关系*/
@RequestMapping("/param3")
public String param3(
        @RequestParam("user_name") String username, //请求参数为user_name
        @RequestParam("pass_word") Integer password) //请求参数为pass_word
{
    System.out.println("username: "+username);
    System.out.println("password: "+password);
    return "success";
}
<form th:action="@{/param3}" method="get">
    用户名:<input type="text" name="user_name" value="用户名">
    密码:<input type="text" name="pass_word" value="密码">
    <input type="submit">
</form>

4、@RequestHeader

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

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

@RequestMapping("/param4")
public String param4(
        String username,
        String password,
        @RequestHeader("Host") String host){
    System.out.println("username: "+username);
    System.out.println("password: "+password);
    System.out.println("host: "+host);
    return "success";
}
<form th:action="@{/param4}" method="get">
    用户名:<input type="text" name="username" value="用户名">
    密码:<input type="text" name="password" value="密码">
    <input type="submit">
</form>

5、@CookieValue

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

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

/*@CookieValue获取Cookie信息*/
@RequestMapping("/param5")
public String param5(
        String username,
        String password,
        @CookieValue("JSESSIONID") String JSESSIONID){
        System.out.println("username: "+username);
        System.out.println("password: "+password);
        System.out.println("JSESSIONID: "+JSESSIONID);
    return "success";
}
<form th:action="@{/param5}" method="get">
    用户名:<input type="text" name="username" value="用户名">
    密码:<input type="text" name="password" value="密码">
    <input type="submit">
</form>

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

  • 设置一个实体类,实体类的属性与客户端请求参数相同
  • 控制器方法的形参为实体类对象,再通过实体类的get方法获取请求的参数

实体类

public class User {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private String sex;
    private String email;
}
/*get,set,toString*/

控制器方法

/*使用实体类接收请求参数*/
@RequestMapping("/param9")
public String param9(User user){
    System.out.println(user);
    return "success";
}

客户端页面

<form th:action="@{/param9}" method="get">
    id:<input type="text" name="id"><br>
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    年龄:<input type="text" name="age"><br>
    性别:<input type="radio" name="sex" value="男">男<input type="radio" name="sex" value="女">女<br>
    邮箱:<input type="text" name="email"><br>
    <input type="submit" value="使用实体类接收请求参数">
</form>

7、Tomcat服务器初始化顺序

1、Listener监听器

2、Filter过滤器

3、servlet程序

8、设置Filter过滤器解决POST请求乱码

  • get请求乱码问题可以在Tomcat文件的serve.xml文件中更改

  • post请求乱码问题则需要在web.xml配置文件中添加filter过滤器更改

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

<!--配置filter过滤器,使得post请求不乱码-->
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!--将请求编码设为UTF-8-->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <!--将响应编码设置为UTF-8-->
    <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>

注:字符编码过滤器CharacterEncodingFilter一定要放在所有过滤器之前

五、域对象共享数据

1、四大域对象

  • pageContext(pageContextImpl类)

当前jsp页面中有效

  • request (HttpServletRequest类)

只在一次请求中保存,服务器跳转后依然有效

  • session(HttpSession类)

一整个会话中都有效(打开游览器到游览器关闭)

  • application(ServletContext类)

整个web工程范围内都有效

2、使用servletAPI向request域中共享数据

  • scopeController
@RequestMapping("/userServletAPI")
public String useServletAPI(HttpServletRequest request){
    request.setAttribute("username","admin");
    request.setAttribute("password","123123123");
    request.setAttribute("email","admin@qq.com");
    return "success";
}
  • scope.html(请求转发到succes.html页面)
<a th:href="@{/userServletAPI}">use Servlet to setAttribute</a>
  • success.html(显示request域中数据)
username:<p th:text="${username}"></p>
<br>
password:<p th:text="${password}"></p>
<br>
email:<p th:text="${email}"></p>
<br>

3、使用ModelAndView向request域中共享数据

  • scopeController
@RequestMapping("/useModelAndView")
public ModelAndView useModelAndView(){
    ModelAndView modelAndView = new ModelAndView();
    /*处理模型数据,即向request域中共享数据*/
    modelAndView.addObject("username","useModelAndView");
    modelAndView.addObject("password","123123123");
    modelAndView.addObject("email","admin@qq.com");
    /*设置视图名称*/
    modelAndView.setViewName("success");
    return modelAndView;
}
  • scope.html(请求转发到succes.html页面)
<a th:href="@{/useModelAndView}">use useModelAndView to setAttribute</a>

success页面同上

4、使用Model向request域中共享数据

@RequestMapping("useModel")
public String useModel(Model model){
    model.addAttribute("username","useModel");
    model.addAttribute("password","123123123");
    model.addAttribute("email","admin@qq.com");
    return "success";
}
  • scope.html(请求转发到succes.html页面)
<a th:href="@{/useModel}">use Model to setAttribute</a>

success页面同上

5、使用Map向request域中共享数据

@RequestMapping("useMap")
public String useMap(Map<String,Object> map){
    map.put("username","useMap");
    map.put("password","123123123");
    map.put("email","admin@qq.com");
    return "success"; 
}
  • scope.html(请求转发到succes.html页面)
<a th:href="@{/useMap}">use Map to setAttribute</a>

success页面同上

6、使用ModelMap向request域中共享数据

@RequestMapping("useModelMap")
public String useModelMap(ModelMap modelMap){
    modelMap.addAttribute("username","useModelMap");
    modelMap.addAttribute("password","123123123");
    modelMap.addAttribute("email","admin@qq.com");
    return "success";
}
  • scope.html(请求转发到succes.html页面)
<a th:href="@{/useModelMap}">use ModelMap to setAttribute</a>

success页面同上

7、Model、ModelMap、Map的关系

  • Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的
public interface Model{} //Model为接口

public class ModelMap extends LinkedHashMap<String, Object> {}

public class ExtendedModelMap extends ModelMap implements Model {}

public class BindingAwareModelMap extends ExtendedModelMap {}

8、向session域中共享数据

使用servletAPI方式共享(比较简单)

@RequestMapping("toSession")
public String toSession(HttpSession session){
    session.setAttribute("username","toSession");
    session.setAttribute("password","123123123");
    session.setAttribute("email","admin@qq.com");
    return "success";
}
  • 在themeleaf中 需要使用(session.键)的方式获取session域中数据
username:<p th:text="${session.username}"></p>
<br>
password:<p th:text="${session.password}"></p>
<br>
email:<p th:text="${session.email}"></p>

9、向application域中共享数据

@RequestMapping("toApplication")
public String toApplication(HttpSession session){
    ServletContext application = session.getServletContext(); //通过ServletContext获取application域对象
    application.setAttribute("username","toApplication");
    application.setAttribute("password","123123123");
    application.setAttribute("email","admin@qq.com");
    return "success";
}
  • 在themeleaf中 需要使用(application.键)的方式获取application域中数据
username:<p th:text="${application.username}"></p>
<br>
password:<p th:text="${application.password}"></p>
<br>
email:<p th:text="${application.email}"></p>

六、SpringMVC的视图

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

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

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

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

1、ThymeleafView

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

@RequestMapping("/testHello")
public String testHello(){
    return "hello";
}

2、servlet转发和重定向的区别

  • 请求转发之间是共享同一个request

    浏览器地址不会改变 (一次请求)

    原理:当浏览器向服务器请求AServlet时,tomcat服务器会指向Aservlet,之后内部转发到BServlet

    我们在AServlet中保存一个键值对转发到BServlet中,会发现在BServlet中可以拿到键值对的值:

    request.getRequestDispatcher("BServlet").forward(request, response);
  • 重定向servlet之间有各自独立的request对象

    浏览器地址会改变(两次请求)

    原理:当浏览器向服务器请求AServlet时,tomcat服务器会指向AServlet中,通过SendREdirect重定向至BServlet,此时服务器会向客户端返回一为302的状态码,要求客户端重新请求BServlet,客户端会再次发送请求BServlet;

    我们同样的保存键值对后重定向到BServlet中发现拿不到键值对的值:

    response.sendRedirect("BServlet");

3、转发视图

  • SpringMVC中默认的转发视图是InternalResourceView

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

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

例如"forward:/","forward:/employee"

@RequestMapping("/forwardTest") //地址栏显示forwardTest,最终页面显示success
    public String forwardTest(){
        return "forward:viewTest";
}

跳转后页面:http://localhost:8080/SpringMVC_Demo3/forwardTest

4、重定向视图

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

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

  • 例如"redirect:/","redirect:/employee"

@RequestMapping("/redirectTest") //地址栏显示viewTest,最终页面显示success
public String redirectTest(){
    return "redirect:/viewTest";
}

跳转后页面:http://localhost:8080/SpringMVC_Demo3/viewTest

5、视图控制器view-controller

  • 当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view-controller标签进行表示
<!--配置视图控制器-->
<!--
path:设置处理的请求地址
view-name:设置请求地址所对应的视图名称
-->
    <mvc:view-controller path="/" view-name="index"></mvc:view-controller>

<!--跳转到视图路径为/paramTest,视图名称为param的页面-->
    <mvc:view-controller path="/paramTest" view-name="param"></mvc:view-controller>

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

当SpringMVC设置任何一个视图控制器(view-controller)时,其他控制器的请求映射全部失效,此时需要在SpringMVC.xml核心配置文件中开启MVC注解驱动

< mvc:annotation-driven/>

6、JSP页面的视图解析器

  • 在SpringMVC.xml配置文件中配置视图解析器
<!--配置jsp页面的视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/templates/"></property>
    <property name="suffix" value=".jsp"></property>
</bean>
  • jsp页面的跳转控制器
@RequestMapping("/toSuccess")
public String toSuccess(){
    return "success";
}
  • 设置跳转链接(需要获取动态路径)
<a href="${pageContext.request.contextPath}/toSuccess">跳转到success页面</a>

七、RESTful

1、RESTful简介

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

  • 资源

资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个URI来标识。URI既是资源的名称,也是资源在Web上的地址。对某个资源感兴趣的客户端应用,可以通过资源的URI与其进行交互。

  • 资源的表述

资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移(交换)。资源的表述可以有多种格式,例如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 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求

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

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

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

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

在web.xml中注册HiddenHttpMethodFilter

 <!--注册HiddenHttpMethodFilter,使用REST风格,将页面中的post请求转为put请求或delete请求-->
<filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern> <!--设置所有路径-->
</filter-mapping>

注:

目前为止,SpringMVC中提供了两个过滤器:CharacterEncodingFilter和HiddenHttpMethodFilter

在web.xml中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter

原因:

  • 在 CharacterEncodingFilter 中通过 request.setCharacterEncoding(encoding) 方法设置字符集的

  • request.setCharacterEncoding(encoding) 方法要求前面不能有任何获取请求参数的操作

  • 而 HiddenHttpMethodFilter 恰恰有一个获取请求方式的操作:

  • String paramValue = request.getParameter(this.methodParam);

注: 过滤器的执行顺序为配置顺序

4、使用RESTful模拟增删改查

  • 查询操作(所有用户)
/*查询所有用户信息 user-->get请求方式 */
@RequestMapping(value="/user",method = RequestMethod.GET)
public String getAllUsers(Model model){
    model.addAttribute("method","查询所有用户信息");
    return "success";
}
<a th:href="@{/user}">查询所有用户信息</a>
  • 查询操作(通过id查询个人)
/*根据id查询用户信息 user/1-->get请求方式*/
@RequestMapping(value="/user/{1}",method = RequestMethod.GET)

方法内部同上

<a th:href="@{/user/1}">根据id查询用户信息</a>
  • 添加操作
/*添加用户信息 user-->post请求方式*/
@RequestMapping(value = "/user",method = RequestMethod.POST)

由于是post请求,所以使用form表单形式

<form th:action="@{/user}" method="post">
    用户名: <input type="text" name="username" value="用户名">
    密码: <input type="text" name="password" value="密码">
    <input type="submit" value="提交">
</form>
  • 修改操作

修改和删除操作需要使用HiddenHttpMethodFilter 将 POST 请求转换为 DELETE 或 PUT 请求

/*修改用户信息 user-->put请求方式*/
@RequestMapping(value="/user",method=RequestMethod.PUT)

在form表单中需要添加隐藏input,并标明请求类型

<form th:action="@{/user}" method="post"> <!--游览器只能接受到get和post请求-->
    <input type="hidden" name="_method" value="put">
    用户名: <input type="text" name="username" value="用户名">
    密码: <input type="text" name="password" value="密码">
    <input type="submit" value="提交">
</form>
  • 删除操作
/*删除用户信息 user/1-->delete请求方式*/
@RequestMapping(value="/user/{1}",method=RequestMethod.DELETE)
<input type="hidden" name="_method" value="delete">

form表单内容同上

八、RESTful案例

1、项目准备

  • pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>SpringMVC_Rest</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.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>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

</project>
  • SpringMVC.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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.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>

<!--以下内容在项目实现过程中添加,但为了保持代码完整性,在此添加-->
<!--跳转到首页面-->
    <mvc:view-controller path="/" view-name="index"/>
<!--跳转到用户添加页面-->
    <mvc:view-controller path="/toAdd" view-name="addEmployee"/>
<!--开放对静态资源的访问-->
    <mvc:default-servlet-handler/>
    <!--开启mvc的注解驱动-->
    <mvc:annotation-driven/>
</beans>
  • web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--配置过滤器编码(必须首先配置)-->
    <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>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!--配置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>
    <!--配置前端控制器-->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:SpringMVC.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
  • 创建实体类Employee
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender;

    //get,set,控制器,toString
}
  • 模拟持久层EmloyeeDao
@Component
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);
    }
}

2、项目实现

  • 实现前端控制器(包含各个功能的请求映射方法)
@Controller
public class EmployeeController {

    /*创建EmployeeDao对象*/
    private static EmployeeDao employeeDao = new EmployeeDao();

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

    /*获取所有员工信息*/
    @RequestMapping(value = "/getAllEmployees", method = RequestMethod.GET)
    public String getAllEmployees(Model model) {
        Collection<Employee> employees = employeeDao.getAll();
        /*往request域中存储Employee对象的集合*/
        model.addAttribute("employees", employees);
        return "employee_list";
    }

    /*删除成员*/
    @RequestMapping(value = "/getAllEmployees/{id}", method = RequestMethod.DELETE)
    public String deleteEmployee(@PathVariable("id") Integer id) {
        employeeDao.delete(id);
        return "redirect:/getAllEmployees";
    }

    /*添加成员*/
    @RequestMapping(value = "/getAllEmployees", method = RequestMethod.POST)
    public String saveEmployee(Employee employee) { //根据实体类对应的属性 获取客户端form表单提交内容
        employeeDao.save(employee);
        return "redirect:/getAllEmployees";
    }

    /*根据id查询用户信息(修改成员信息时,回显数据)*/
    @RequestMapping(value = "/getAllEmployees/{id}", method = RequestMethod.GET)
    public String getEmployeeById(@PathVariable("id") Integer id, Model model) {
        Employee employee = employeeDao.get(id);
        model.addAttribute("employee", employee);
        return "updateEmployee";
    }

    /*修改成员*/
    @RequestMapping(value = "/getAllEmployees", method = RequestMethod.PUT)
    public String updateEmployee(Employee employee) {
        employeeDao.save(employee);
        return "redirect:/getAllEmployees";
    }

}
  • 实现成员展示页面及其功能链接

点击删除按钮后,页面再跳转并回到主页面,并删除选择的用户

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Employee list</h1>

<!--展示员工信息-->
<table id="dataTable" border="1" cellspacing="0"  cellpadding="0" style="text-align: center">
    <tr>
        <th colspan="5">Employee list</th>
    </tr>

    <tr>
        <th>id</th>
        <th>lastName</th>
        <th>email</th>
        <th>gender</th>
        <th>options <a th:href="@{/toAdd}">add</a></th> <!--选项--><!--包含添加页面超链接-->
    </tr>

    <tr th:each="employee: ${employees}">
        <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>
            <a @click="deleteEmployee" th:href="@{'/getAllEmployees/'+${employee.id}}">delete</a>
            <a th:href="@{'/getAllEmployees/'+${employee.id}}">update</a>
        </td>
    </tr>
</table>

<!--删除功能表单-->
<form id="delete_form" method="post">
    <input type="hidden" name="_method" value="delete">
</form>

<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>

<script type="text/javascript">
    var vue = new Vue({
        el:"#dataTable",
        methods:{
            //event表示当前事件
            deleteEmployee:function (event) {
                //通过id获取表单标签
                var delete_form = document.getElementById("delete_form");
                //将触发事件的超链接的href属性为表单的action属性赋值
                delete_form.action = event.target.href;
                //提交表单
                delete_form.submit();
                //阻止超链接的默认跳转行为
                event.preventDefault();
            }
        }
    });
</script>

</body>
</html>
  • 实现用户添加界面

输入信息点击提交按钮后,会以id顺延的方式在主页面显示

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>添加页面</h1>

<form th:action="@{/getAllEmployees}" method="post"><br>
    lastName: <input type="text" name="lastName"><br>
    email: <input type="text" name="email"><br>
    gender: <input type="radio" name="gender" value="1">男
            <input type="radio" name="gender" value="0">女<br>
    <input type="submit" value="提交"><br>
</form>

</body>
</html>
  • 实现用户修改界面

实现了用户信息回显,并提交后在主页面修改用户信息

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>修改用户信息页面</h1>
<form th:action="@{/getAllEmployees}" method="post"><br>
    <input type="hidden" name="_method" value="PUT">
    <input type="hidden" name="id" th:value="${employee.id}"><br>
    lastName: <input type="text" name="lastName" th:value="${employee.lastName}"><br>
    email: <input type="text" name="email" th:value="${employee.email}"><br>
    gender: <input type="radio" name="gender" value="1" th:field="${employee.gender}">男
    <input type="radio" name="gender" value="0" th:field="${employee.gender}">女<br>
    <input type="submit" value="提交"><br>
</form>
</body>
</html>

九、HttpMessageConverter

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

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

1、@RequestBody

@RequestBody可以获取请求体,需要在控制器方法设置一个形参

使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的形参赋值

/*@RequestBody注解: 获取请求体*/
@RequestMapping(value = "RequestBody")
public String requestBodyTest(@RequestBody String requestBody){
    System.out.println(requestBody);
    return "success";
}
<form th:action="@{/RequestBody}" method="post">
    username: <input type="text" name="username">
    password: <input type="password" name="password">
    <input type="submit">
</form>

输出结果:username=OdinPeng&password=123123

2、RequestEntity

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

/*RequestEntity<String>类: 获取请求报文(请求头,请求体,url...)*/
@RequestMapping("RequestEntity")
public String requestEntityTest(RequestEntity<String> requestEntity){
    System.out.println("请求头:"+requestEntity.getHeaders());
    System.out.println("请求体:"+requestEntity.getBody());
    return "success";
}
<form th:action="@{/RequestEntity}" method="post">
    username: <input type="text" name="username">
    password: <input type="password" name="password">
    <input type="submit">
</form>

3、使用servletAPI响应客户端

/*通过servletAPI响应浏览器*/
@RequestMapping("ResponseForServletAPI")
public void ResponseForServletAPI(HttpServletResponse response){
    try {
        response.getWriter().print("hello world");
    } catch (IOException e) {
        e.printStackTrace();
    }
}
<a th:href="@{/ResponseForServletAPI}">使用servletAPI测试响应</a>

4、@ResponseBody

使用@ResponseBody注解响应浏览器请求

/*通过@ResponseBody响应浏览器*/
@RequestMapping("ResponseBodyForResponse")
@ResponseBody
public String responseBodyForResponse(){
    return "hello @ResponseBodyForResponse";
}
<a th:href="@{/ResponseBodyForResponse}">使用@ResponseBody注解测试响应</a>

5、SpringMVC处理JSON

@ResponseBody处理json的步骤:

  • 导入jackson的依赖
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.1</version>
</dependency>
  • 在SpringMVC的核心配置文件中开启mvc的注解驱动,此时在HandlerAdaptor中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将响应到浏览器的Java对象转换为Json格式的字符串
<mvc:annotation-driven />
  • 在处理器方法上使用@ResponseBody注解进行标识

  • 将Java对象直接作为控制器方法的返回值返回,就会自动转换为Json格式的字符串

/*通过@ResponseBody返回实体类响应浏览器*/
@RequestMapping("ResponseBodyForResponseByUser")
@ResponseBody
public User responseBodyForResponseByUser(){
    return new User(1,"admin","admin@qq.com","男");
}
<a th:href="@{/ResponseBodyForResponseByUser}">通过@ResponseBody返回实体类响应</a>

游览器显示方式:{"id":1,"username":"admin","email":"admin@qq.com","gender":"男"}

6、SpringMVC处理ajax

ajax:页面不发生跳转的情况下与服务器进行交互

  • 请求超链接
<div id="app">
    <a th:href="@{/testAjax}" @click="testAjax">testAjax</a><br>
</div>
  • 通过vue和axios处理点击事件
<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">
    var vue = new Vue({
        el:"#app",
        methods:{
            testAjax:function (event) {
                axios({
                    method:"post",
                    url:event.target.href,
                    params:{
                        username:"admin",
                        password:"123456"
                    }
                }).then(function (response) {
                    alert(response.data);
                });
                event.preventDefault();
            }
        }
    });
</script>
  • 控制器方法
/*SpringMVC处理ajax请求*/
@RequestMapping("testAjax")
@ResponseBody
public String testAjax(String username,String password){
    System.out.println(username);
    System.out.println(password);
    return "hello Axios";
}

7、@RestController注解☆

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

@Controller
@RestController //相当于为每个方法添加了@ResponseBody注解
public class HttpController {
    /*控制器方法*/
}

8、ResponseEntity

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

十、文件的上传与下载

1、文件的下载

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

@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
    //获取ServletContext对象
    ServletContext servletContext = session.getServletContext();
    //获取服务器中文件的真实路径
    String realPath = servletContext.getRealPath("/static/img/1.png");
    //创建输入流
    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=1.png");
    //设置响应状态码
    HttpStatus statusCode = HttpStatus.OK;
    //创建ResponseEntity对象
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
    //关闭输入流
    is.close();
    return responseEntity;
}
<a th:href="@{/testDown}">测试文件下载功能</a>

点击超链接即可下载

2、文件的上传

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

SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息

上传步骤:

  • 添加依赖:
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>
  • 在SpringMVC配置文件中添加配置:
<!--配置文件上传解析器-->
<!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/><!--根据id获取,需要加上id-->
  • 编写文件上传控制器
@RequestMapping("/testUp")
public String testUp(MultipartFile photo, HttpSession session) throws IOException {
    //获取上传的文件的文件名
    String fileName = photo.getOriginalFilename();
    //处理文件重名问题
    String hzName = fileName.substring(fileName.lastIndexOf("."));
    fileName = UUID.randomUUID().toString() + hzName;
    //获取服务器中photo目录的路径
    ServletContext servletContext = session.getServletContext();
    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";
}
  • post请求的表单
<form th:action="@{/testUp}" method="post" enctype="multipart/form-data">
    请选择头像:<input type="file" name="photo"><br>
    <input type="submit" value="上传">
</form>

图片上传路径:

十一、拦截器

1、拦截器的配置

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

  • SpringMVC中的拦截器需要实现HandlerInterceptor:

@Controller
public class FirstInterceptor implements HandlerInterceptor {
    /*返回false拦截,返回true放行*/
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}
  • SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:(三种方式)
<mvc:interceptors>
    <!--方式一、二:(对DispatcherServlet所处理的所有的请求进行拦截)-->
    <bean class="com.interceptors.FirstInterceptor"/>-->

    <ref bean="firstInterceptor"/>

    <!--方式三:可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,通过mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求-->

    <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/testRequestEntity"/>
            <ref bean="firstInterceptor"></ref>
    </mvc:interceptor>

</mvc:interceptors>

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

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

  • postHandle:控制器方法执行之后执行postHandle()

  • afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()

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

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

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

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

控制台输出:

preHandle-----------------------------first
preHandle-------------------------second
postHandle------------------------second
postHandle----------------------------first
afterCompletion-----------------second
afterCompletion---------------------first

  • 若某个拦截器的preHandle()返回了false

preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行

控制台输出:

preHandle-----------------------------first
preHandle-------------------------second
afterCompletion---------------------first

十二、异常处理器

1、异常处理器的配置

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

  • HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver和SimpleMappingExceptionResolver

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

<!--配置异常处理器-->
<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"/>
</bean>

设置存在异常的跳转方法

@RequestMapping("testExceptionHandler")
public String testExceptionHandler(){
    int num=1/0;
    return "success";
}

在error.html页面中显示异常信息

<!--将异常信息展示到界面中-->
<p th:text="${exception}"></p>

页面展示:

2、基于注解的异常处理

/*创建异常处理类*/
@ControllerAdvice
public class ExceptionController {
    /*设置异常控制方法*/
    @ExceptionHandler(value={Exception.class}) //value指名出现的异常类型
    public String toException(Exception exception, Model model){
        model.addAttribute("exception",exception); //将异常共享到request域中
        return "error";
    }
}

效果与Spring.xml配置文件配置效果相同

十三、注解配置SpringMVC

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

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

在Servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果找到的话就用它来配置Servlet容器。
Spring提供了这个接口的实现,名为SpringServletContainerInitializer,这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。Spring3.2引入了一个便利的WebApplicationInitializer基础实现,名为AbstractAnnotationConfigDispatcherServletInitializer,当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer并将其部署到Servlet3.0容器的时候,容器会自动发现它,并用它来配置Servlet上下文。

用来代替web.xml

/*web工程的初始化类,用来代替web.xml*/
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {

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

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

    /*指定DispatcherMapping的映射规则,即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};
    }
}

2、创建Spring_Config类

用来代替Spring配置文件

/*Spring配置类*/
@Configuration
public class Spring_Config {
}

3、创建SpringMVC_Config类

用来代替SpringMVC.xml

/**
 * SpringMVC配置类,用来代替SpringMVC的配置文件
 * 1、组件扫描  
 2、thymeleaf视图解析器  
 3、mvc:view-controller视图控制器  
 4、mvc:default-servlet-handler开启静态资源访问
 5、mvc:annotation-driven开启注解驱动  
 6、文件上传解析器  
 7、异常处理器  
 8、拦截器
**/

@Configuration
//扫描组件
@ComponentScan("com.controller")
//开启MVC注解驱动
@EnableWebMvc
public class SpringMVC_Config implements WebMvcConfigurer {

    //使用默认的servlet处理静态资源
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

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

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

    /*配置视图控制*/
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
    }

    /*配置异常映射*/
    @Override
    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("ex");
        resolvers.add(exceptionResolver);
    }

    //配置生成模板解析器
    @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:视图解析器,不需要工程师开发,由框架提供

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

  • View:视图

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

2、SpringMVC执行流程

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

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

  • 不存在

a)再判断是否配置了mvc:default-servlet-handler

b)如果没配置,则控制台报映射查找不到,客户端展示404错误

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

  • 存在则执行下面的流程

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

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

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

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

a) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息

b) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等

c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等

d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中

7) Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。

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

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

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

11) 将渲染结果返回给客户端。

发表回复

您的电子邮箱地址不会被公开。