Spring MVC

Spring MVC

控制层框架,用来和前端进行交互,接收请求,发送响应!!!

概述

SpringMVC 是一种基于 Java 实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架,它和 Struts2 都属于表现层的框架,属于 Spring FrameWork 的后续产品,Spring MVC 分离了控制器、模型对象、过滤器以及处理程序对象的角色,这种分离让它们更容易进行定制。

SpringMVC 已经成为目前最主流的 MVC 框架之一,并且随着 Spring 3.0 的发布,全面超越 Struts2,成为最优秀的 MVC 框架,它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持 RESTful 编程风格的请求。

官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html

相关组件说明

DispatcherServlet:前端控制器

​ 用户请求到达前端控制器,它就相当于mvc模式中的 c,dispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

HandlerMapping:处理器映射器

​ HandlerMapping 负责根据用户请求 url 找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

键:url,唯一;值:servlet。

Handler:处理器

​ Handler 是继 DispatcherServlet 前端控制器的后端控制器,在 DispatcherServlet 的控制下 Handler 对具体的用户请求进行处理。由于 Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。

HandlAdapter:处理器适配器

​ 通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

ViewResolver:视图解析器

​ View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成 View 视图对象,最后对View 进行渲染将处理结果通过页面展示给用户。

View:视图

​ springmvc 框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView 等。我们最常用的视图就是jsp。

一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。

【注意】在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。

需要用户开发的组件有 handler、view

因为框架已经默认加载这些组件了,所以我们不需要做任何配置,就可以使用这些组件了。

执行流程【重点】

在这里插入图片描述

  1. 用户发送请求至前端控制器 DispatcherServlet。
  2. DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。
  4. DispatcherServlet 通过 HandlerAdapter 处理器适配器调用处理器
  5. 执行处理器(Controller,也叫后端控制器)。
  6. Controller 执行完成返回 ModelAndView
  7. HandlerAdapter 将 Controller 执行结果 ModelAndView 返回给DispatcherServlet
  8. DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器
  9. ViewReslover 解析后返回具体View
  10. DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中)。
  11. DispatcherServlet 响应用户

入门

1、创建 web 项目并导入相关依赖

<?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>05_SpringMVC_01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <!--Spring MVC-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.3</version>
        </dependency>

        <!--Servlet-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>

        <!--JSP-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!--Tomcat-->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <!--端口号-->
                    <port>8080</port>
                    <!--访问路径-->
                    <path>/</path>
                    <!--Tomcat编码集-->
                    <uriEncoding>UTF-8</uriEncoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2、前端登录页面:login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录页面</title>
</head>
<body>
<h1 align="center">
    <a href="user/login">登录</a>
</h1>
</body>
</html>

【注意】这里建议加 / ,因为要使用绝对路径进行跳转

3、前端登录成功页面:index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录成功页</title>
</head>
<body>
    <h1 align="center" style="color: aqua">欢迎</h1>
</body>
</html>

4、LoginController

// 使用@RequestMapping注解声明访问的一级路径
@Controller
@RequestMapping("user")
public class LoginController {
    /**
     * 此方法用来声明登录的方法,相当于一个登录Servlet
     * 使用@RequestMapping注解声明访问的二级路径
     * 
     * @return 返回一个访问的路径
     */
    @RequestMapping("login")
    public String login() {
        System.out.println("login...");
        // 注意加/使用绝对路径访问,不加/使用相对路径访问
        return "/index.jsp";
    }
}

【注意】这里使用 @RequestMapping 来声明访问的路径

5、springMvc配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.fc.controller"/>

    <!-- 视图解析器对象 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>

    <!-- 开启SpringMVC框架注解的支持,相当于配置了处理器适配器和处理器映射器 -->
    <mvc:annotation-driven />
</beans>

6、配置 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">

    <!--配置前端控制器-->
    <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>
        <!--启动时加载-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!--配置前端控制器的访问路径-->
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

@RequestMapping【

RequestMapping 注解的作用是建立请求URL和处理方法之间的对应关系

RequestMapping 注解可以作用在方法和类上

  • 作用在类上:第一级的访问目录
  • 作用在方法上:第二级的访问目录

【注意】/ 表示应用的根目录开始,路径上不能写 /

RequestMapping 注解的属性

属性描述
path指定请求路径的 url
valuevalue 属性和 path 属性是一样的
method指定该方法的请求方式
params指定限制请求参数的条件
headers发送的请求中必须包含的请求头

前端页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试RequestMapping</title>
</head>
<body>
    <form action="user/requestMapping" method="get">
        <table>
            <tr>
                <td>姓名</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td colspan="2">
                    <input type="reset" value="重置">
                    <input type="submit" value="提交">
                </td>
            </tr>
        </table>
    </form>
</body>
</html>

Controller

// 使用@RequestMapping注解声明访问的一级路径
@Controller
@RequestMapping("user")
public class LoginController {
    /**
     * 测试RequestMapping注解
     *      path:请求的路径
     *      method:请求的类型
     *      params:请求携带的参数
     * 
     * @param username 请求参数为username
     */
    @RequestMapping(path = "requestMapping", method = RequestMethod.GET, params = "username")
    public void testRequestMapping(String username) {
        System.out.println(username);
    }
}

请求参数的绑定【重点】

绑定机制:

表单提交的数据都是 k=v 键值对格式的,例如 username=易烊千玺&password=123456

SpringMVC 的参数绑定过程是把表单提交的请求参数,作为控制器中方法的参数进行绑定的

要求:提交表单的 name 和参数的名称相同。

支持的数据类型:

  1. 基本数据类型和字符串类型
  2. 实体类型(JavaBean)
  3. 集合数据类型(List、map集合等)
使用基本数据类型与字符串进行参数绑定

注册页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>注册页面</title>
</head>
<body>
<form method="post" action="user/register">
    <table align="center">
        <caption><h1 align="center">注册</h1></caption>
        <tr>
            <td>账号</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密码</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td>年龄</td>
            <td><input type="text" name="age"></td>
        </tr>
        <tr>
            <td>性别</td>
            <td><input type="radio" name="gender" value=""><input type="radio" name="gender" value="" checked>
            </td>
        </tr>
        <tr>
            <td>生日</td>
            <td><input type="date" name="birthday"></td>
        </tr>
        <tr>
            <td>信息</td>
            <td><input type="text" name="info"></td>
        </tr>
        <tr>
            <td colspan="2" align="center">
                <input type="reset" value="重置">&nbsp;&nbsp;&nbsp;&nbsp;
                <input type="submit" value="提交">
            </td>
        </tr>
    </table>
</form>
</body>
</html>

Controller层

// 使用@RequestMapping注解声明访问的一级路径
@Controller
@RequestMapping("user")
public class LoginController {
    /**
     * 注册方法
     *
     * @param username Integer类型的用户名
     * @param password String类型的密码
     * @param age int类型的年龄
     * @param birthday String类型的生日
     * @param info String类型的信息
     * @return 返回一个路径
     */
    @RequestMapping("register")
    public String register(Integer username, String password, int age, String birthday, String info) {
        System.out.println("用户名:" + username + " 密码:" + password + " 年龄:" + age + " 生日:" + birthday + " 信息:" + info);

        return "/success.jsp";
    }
}
使用引用类型作为请求参数

声明实体类

public class User {
    private Integer username;
    private String password;
    private int age;
    private String gender;
    private Date birthday;
    private String info;
    
    // Constractor、Setters、Getters
}

Controller

// 使用@RequestMapping注解声明访问的一级路径
@Controller
@RequestMapping("user")
public class LoginController {
    /**
     * 注册方法
     *
     * @param user 使用一个实体类对象作为参数
     * @return 返回一个路径
     */
    @RequestMapping("register")
    public String register(User user) {
        System.out.println(user);

        return "/success.jsp";
    }
}
使用引用类型中嵌套其他引用类型进行参数绑定

【注意】

  1. 对实体类型(JavaBean)的参数绑定要求提交表单的 name 属性与 JavaBean 中的属性名称需要一致。
  2. 如果一个 JavaBean 类中包含其他的引用类型,那么表单的 name 属性需要编写成:对象.属性

声明 UserInfo 实体类

public class UserInfo {
    private int age;
    private String gender;
    private Date birthday;
    private String info;
    
    // Constructor、Getters、Setters
}

声明 User 实体类

public class User {
    private Integer username;
    private String password;
    private UserInfo userInfo;
    
    // Constructor、Getters、Setters
}

前端页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>注册页面</title>
</head>
<body>
<form method="post" action="user/register">
    <table align="center">
        <caption><h1 align="center">注册</h1></caption>
        <tr>
            <td>账号</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密码</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td>年龄</td>
            <td><input type="text" name="userInfo.age"></td>
        </tr>
        <tr>
            <td>性别</td>
            <td>
                男<input type="radio" name="userInfo.gender" value="男">
                女<input type="radio" name="userInfo.gender" value="女" checked>
            </td>
        </tr>
        <tr>
            <td>生日</td>
            <td><input type="date" name="userInfo.birthday"></td>
        </tr>
        <tr>
            <td>信息</td>
            <td><input type="text" name="userInfo.info"></td>
        </tr>
        <tr>
            <td colspan="2" align="center">
                <input type="reset" value="重置">&nbsp;&nbsp;&nbsp;&nbsp;
                <input type="submit" value="提交">
            </td>
        </tr>
    </table>
</form>
</body>
</html>

Controller

@Controller
@RequestMapping("user")
public class LoginController {
    /**
     * 注册方法
     *
     * @param user 使用一个实体类对象作为参数,并且实体类中还引用了其他对象
     * @return 返回一个路径
     */
    @RequestMapping("register")
    public String register(User user) {
        System.out.println(user);

        return "/success.jsp";
    }
}
给 List 和 Map 映射

【注意】

// 给 List 类型的属性数据封装
list[index].属性名

// 给 Map 类型的属性数据封装
map['key'].属性名

声明 Clazz 实体类

public class Clazz {
    private List<User> users;
    private Map<String, User> map;
    
    // Constructor、Getters、Setters
}

前端页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试List和Map的映射</title>
</head>
<body>
    <form action="user/ListAndMap">
        <table align="center">
            <caption><h1 align="center" style="color: aqua">给List和Map映射</h1></caption>
            <tr>
                <td>List账号</td>
                <td><input type="text" name="users[0].username"></td>
            </tr>
            <tr>
                <td>List密码</td>
                <td><input type="password" name="users[0].password"></td>
            </tr>

            <tr>
                <td>Map账号</td>
                <td><input type="text" name="map['user'].username"></td>
            </tr>
            <tr>
                <td>Map密码</td>
                <td><input type="password" name="map['user'].password"></td>
            </tr>
            <tr>
                <td><input type="reset" value="重置"></td>
                <td><input type="submit" value="提交"></td>
            </tr>
        </table>
    </form>
</body>
</html>

Controller

@Controller
@RequestMapping("user")
public class LoginController {
    /**
     * 测试List和Map的映射
     * 
     * @param clazz 传入一个Clazz对象
     * @return 返回一个路径
     */
    @RequestMapping("ListAndMap")
    public String testListAndMap(Clazz clazz) {
        System.out.println(clazz);

        return "/success.jsp";
    }
}
直接使用 ServletAPI 对象

SpringMVC 支持使用原始 ServletAPI 对象作为控制器方法的参数,使用时直接通过声明对应的形参即可。支持原始 ServletAPI 对象中最常用的是

HttpServletRequest
HttpServletResponse
HttpSession

前端页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试Servlet Api绑定参数</title>
</head>
<body>
    <%
        request.setAttribute("username", "易烊千玺");
        request.getRequestDispatcher("user/ServletApi").forward(request, response);
    %>
</body>
</html>

Controller

@Controller
@RequestMapping("user")
public class LoginController {
    /**
     * 测试接收Servlet Api对象作为请求参数
     * 
     * @param request 请求对象
     * @param response 影响对象
     * @param session session域对象
     */
    @RequestMapping("ServletApi")
    public void testServletApi(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
        // 获取请求对象中设置的属性参数
        System.out.println(request.getAttribute("username"));

        // 给session对象中设置属性键值对
        session.setAttribute("username", "迪丽热巴");

        try {
            // 使用响应对象进行重定向操作
            response.sendRedirect("/success.jsp");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

跳转页

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录成功页</title>
</head>
<body>
    <h1 align="center" style="color: aqua">欢迎${username}</h1>
</body>
</html>

Spring MVC 中的字符集过滤器

如果请求参数是中文,可以在 web.xml 中配置 Spring 提供的字符集过滤器来解决 Post 请求中文乱码问题

web.xml 文件

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <!--配置解决中文乱码的过滤器-->
    <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>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</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>

【注意】如果要解决 GET 请求的中文乱码,需要在 Tomcat 配置文件中配置编码集为 UTF-8

<Connector port="8080" protocol="HTTP/1.1"
              connectionTimeout="20000"
              redirectPort="8443" URIEncoding="UTF-8"/>

字符串日期转换器

如果对象的属性中有Date类型,页面输入参数格式是 2021/1/1 可以自动参数绑定,如果页面输入参数格式是 2019-1-1 则无法绑定,需要使用自定义类型转换器来解决。

表单提交的任何数据类型全部都是字符串类型,但是后台定义Integer类型,数据也可以封装上,说明Spring框架内部会默认进行数据类型转换。

如果想自定义数据类型转换,可以实现Converter的接口

字符串日期转换类

/**
 * 字符串转换日期格式工具
 */
public class StringToDateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String source) {
        // 默认格式
        String format = "yyyy/MM/dd";

        // 如果日期格式中间包含- 则替换格式
        if (source.contains("-")) {
            format = "yyyy-MM-dd";
        }

        DateFormat simpleDateFormat = new SimpleDateFormat(format);

        try {
            return simpleDateFormat.parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }
}

【注意】粘过来直接用

springmvc.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--自定义类型转换器-->
    <bean name="factoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="com.fc.utils.StringToDateConverter"/>
            </set>
        </property>
    </bean>

    <!-- 开启SpringMVC框架注解的支持并使用自定义类型转换器 -->
    <mvc:annotation-driven conversion-service="factoryBean" />

</beans>

【注意】粘过来直接用

视图解析器

Spring MVC定义了ViewResolver和View接口,这些接口使您可以在浏览器中呈现模型,而无需将您与特定的视图技术联系在一起。 ViewResolver提供了视图名称和实际视图之间的映射。View 在移交给特定的视图技术之前着手准备数据。

视图解析器的实现【了解】

视图解析器描述
AbstractCachingViewResolverAbstractCachingViewResolver 的子类缓存它们解析的视图实例。缓存可以提高某些视图技术的性能。您可以通过将 cache 属性设置为false 来关闭缓存。此外,如果必须在运行时刷新某个视图(例如,当修改 FreeMarker 模板时),则可以使用removeFromCache(String viewName,Locale loc)方法。
UrlBasedViewResolverViewResolver 接口的简单实现会影响逻辑视图名称到 URL 的直接解析,而无需显式的映射定义。如果您的逻辑名称以直接的方式与视图资源的名称匹配,而不需要任意映射,则这是适当的。
InternalResourceViewResolver支持 UrlBasedViewResolver 的方便子类,支持InternalResourceView(实际上是Servlet和JSP)以及 JstlView 和TilesView 等子类。您可以使用 setViewClass(…)为该解析器生成的所有视图指定视图类
FreeMarkerViewResolverUrlBasedViewResolver 的方便子类,支持 FreeMarkerView 和自定义子类。
ContentNegotiatingViewResolverViewResolver 接口的实现,该接口根据请求文件名或 Accept 标头解析视图
BeanNameViewResolverViewResolver 接口的实现,该接口将视图名称解释为当前应用程序上下文中的 bean 名称。这是一个非常灵活的变体,它允许根据不同的视图名称来混合和匹配不同的视图类型。每个这样的“View”都可以定义为一个 bean,例如在 XML 或配置类中。

【注意】使用时直接在 XML 文件中配置InternalResourceViewResolver即可

前端 index 页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试视图解析器</title>
</head>
<body>
<%
    request.getRequestDispatcher("user/viewResolver").forward(request, response);
%>
</body>
</html>

Controller

@Controller
@RequestMapping("user")
public class LoginController {
    /**
     * 测试视图解析器
     *
     * @return 返回不带前缀和后缀的路径
     */
    @RequestMapping("viewResolver")
    public String testViewResolver() {
        return "testViewResolver";
    }
}

springMvc.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--自定义类型转换器-->
    <bean name="factoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="com.fc.utils.StringToDateConverter"/>
            </set>
        </property>
    </bean>

    <!-- 开启SpringMVC框架注解的支持并使用自定义类型转换器 -->
    <mvc:annotation-driven conversion-service="factoryBean" />

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.fc.controller"/>

    <!-- 配置视图解析器对象 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--声明前缀和后缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

前端跳转页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试视图解析器</title>
</head>
<body>
    <h1 align="center" style="color: aqua">
        测试视图解析器
    </h1>
</body>
</html>

响应数据方式

前端登录页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录成功页</title>
</head>
<body>
    <h1 align="center" style="color: aqua">欢迎${username}</h1>
</body>
</html>
返回路径字符串 String

Controller

@Controller
@RequestMapping("responseType")
public class ResponseTypeController {
    @RequestMapping("stringPath")
    public String testStringPath() {
        return "/success.jsp";
    }
}
返回 ModelAndView 对象

Web MVC框架中的 Model 和 View 的持有者。此类仅持有两者,以使控制器可以在单个返回值中返回模型和视图。

表示要由 DispatcherServlet 解析的处理程序返回的模型和视图。该视图可以采用 String 视图名称的形式,该名称需要由 ViewResolver 对象解析;或者,可以直接指定 View 对象。该模型是一个Map,允许使用多个按名称键入的对象。

常用方法

// 构造方法,用来创建一个对象
public ModelAndView() {}
    
// 给 Model 设置属性键值对
ModelAndView addObject(String attributeName, Object attributeValue);

// 设置此 ModelAndView 的视图名称,该名称将由DispatcherServlet通过ViewResolver解析
void setViewName(String viewName);

Controller 案例代码

@Controller
@RequestMapping("responseType")
public class ResponseTypeController {

    /**
     * 测试返回值类型为ModelAndView
     * @return ModelAndView
     */
    @RequestMapping("modelAndView")
    public ModelAndView testModelAndView() {
        // 创建ModelAndView对象
        ModelAndView modelAndView = new ModelAndView();

        // 设置属性键值对
        modelAndView.addObject("username", "易烊千玺");

        // 设置视图名称
        modelAndView.setViewName("/success.jsp");

        return modelAndView;
    }
}
返回 void 并使用重定向
@Controller
@RequestMapping("responseType")
public class ResponseTypeController {
	/**
     * 测试返回值为void类型并且通过响应对象进行重定向
     *
     * @param response 响应对象
     */
    @RequestMapping("voidRedirect")
    public void testVoidRedirect(HttpServletResponse response) {
        try {
            // 重定向
            response.sendRedirect("/success.jsp");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
返回 void 并使用转发
@Controller
@RequestMapping("responseType")
public class ResponseTypeController {
	/**
     * 测试返回值为void类型并且通过请求对象进行转发
     *
     * @param response 请求对象
     */
    @RequestMapping("voidForward")
    public void testVoidForward(HttpServletRequest request, HttpServletResponse response) {
        try {
            // 转发
            request.getRequestDispatcher("/success.jsp").forward(request, response);
        } catch (ServletException | IOException e) {
            e.printStackTrace();
        }
    }
}
返回路径字符串进行重定向

使用redirect关键字进行重定向(默认会把项目路径加上)

return "redirect:重定向的路径"

Controller 案例代码

@Controller
@RequestMapping("responseType")
public class ResponseTypeController {
	/**
     * 通过字符串进行重定向
     *
     * @return "redirect:重定向的路径"
     */
    @RequestMapping("stringRedirect")
    public String testStringRedirect() {
        return "redirect:/success.jsp";
    }
}
返回路径字符串进行转发

使用 forward 关键字进行请求转发

return "forward:转发的路径"

Controller 案例代码

@Controller
@RequestMapping("responseType")
public class ResponseTypeController {
	/**
     * 通过字符串进行转发
     *
     * @return "forward:转发的路径"
     */
    @RequestMapping("stringForward")
    public String testStringForward() {
        return "forward:/success.jsp";
    }
}
通过 @ResponseBody 注解返回 void

@ResponseBody 的作用是将java对象转为json格式的数据

【注意】在使用此注解之后不会再被视图处理器处理,而是直接将数据写入到输入流中,他的效果等同于通过 response 对象输出指定格式的数据。

Controller 案例代码

@Controller
@RequestMapping("responseType")
public class ResponseTypeController {
	/**
     * 通过@ResponseBody注解返回void
     */
    @RequestMapping("void")
    @ResponseBody
    public void testVoid() {

    }
}

【注意】如果不加 ResponseBody 注解,此代码会报错

通过 @ResponseBody 注解返回字符串

此效果等同于 response.append

【注意】需要使用Tomcat8或者Spring3.X的版本

@Controller
@RequestMapping("responseType")
public class ResponseTypeController {
	/**
     * 通过@ResponseBody注解返回string字符串
     *
     * 【注意】要是用Tomcat8或者Spring3.X的版本
     */
    @RequestMapping("string")
    @ResponseBody
    public String testString() {
        return "<h1 align='center' style='color: green'>Hello</h1>";
    }
}
【重点】通过 @ResponseBody 注解返回 Json 字符串

【注意】字符串中必须使用转义字符形式的双引号,使用单引号无效

pom.xml 文件中导入 jackson 依赖

<?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>05_SpringMVC_01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <!--Spring MVC-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.3</version>
        </dependency>

        <!--Jackson绑定依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.11.2</version>
        </dependency>
</project>

Controller 案例代码

@Controller
@RequestMapping("responseType")
public class ResponseTypeController {
	/**
     * 通过@ResponseBody注解返回json字符串
     */
    @RequestMapping("jsonString")
    @ResponseBody
    public String testJsonString() {
        // 注意必须用双引号
        return "{\"hello\":123}";
    }
}
【重点】通过 @ResponseBody 注解返回 Json 对象

pom.xml 文件中导入 jackson 依赖

<?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>05_SpringMVC_01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <!--Spring MVC-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.3</version>
        </dependency>

        <!--Jackson绑定依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.11.2</version>
        </dependency>
</project>

Controller 案例代码

@Controller
@RequestMapping("responseType")
public class ResponseTypeController {
	/**
     * 通过@ResponseBody注解返回json对象
     */
    @RequestMapping("bean")
    @ResponseBody
    public User testBean() {
        UserInfo userInfo = new UserInfo(12, "男", new Date(), "真帅");

        return new User(1001, "123456", userInfo);
    }
}
【重点】通过 @ResponseBody 注解返回 Map 对象

pom.xml 文件中导入 jackson 依赖

<?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>05_SpringMVC_01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <!--Spring MVC-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.3</version>
        </dependency>

        <!--Jackson绑定依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.11.2</version>
        </dependency>
</project>

Controller 案例代码

@Controller
@RequestMapping("responseType")
public class ResponseTypeController {
	/**
     * 通过@ResponseBody注解返回一个Map
     * 
     * @return map
     */
    @RequestMapping("map")
    @ResponseBody
    public Map<String, Object> testMap() {
        Map<String, Object> map = new HashMap<>();

        map.put("code", 200);
        map.put("data", new Date());
        map.put("message", "执行成功");

        UserInfo userInfo = new UserInfo(12, "男", new Date(), "真帅");
        User user = new User(1001, "123456", userInfo);

        map.put("user", user);

        List<String> list = new ArrayList<>();

        list.add("烤羊排");
        list.add("烤全羊");
        list.add("烤玉米");

        map.put("list", list);

        return map;
    }
}

其他常用注解

RequestParam

此注解用于把请求中的指定名称的参数传递给控制器中的形参赋值

属性描述
value请求参数中的名称
required请求参数中是否必须提供此参数,默认值是true,必须提供

前端页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试RequestParam注解</title>
</head>
<body>
    <a href="annotation/requestParam?id=1001">测试RequestParam注解</a>
</body>
</html>

Controller

@Controller
@RequestMapping("annotation")
public class AnnotationCollection {
    /**
     * 测试RequestParam注解
     * value属性为请求参数名
     * required属性用来声明此参数是否必须携带
     *
     * @param uid 对应请求参数中的id
     * @return 返回一个路径
     */
    @RequestMapping("requestParam")
    public String testRequestParam(@RequestParam(value = "id", required = false ) String uid) {
        System.out.println(uid);
        return "/success.jsp";
    }
}
PathVariable

通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中

URL 中的 {xxx} 占位符可以通过@PathVariable(“xxx”) 绑定到操作方法的入参中。

一般与@RequestMapping(method = RequestMethod.GET)一起使用

前端页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试常用注解</title>
</head>
<body>
    <a href="annotation/update/1001">测试PathVariable注解</a>
</body>
</html>

Controller

@Controller
@RequestMapping("annotation")
public class AnnotationCollection {
    /**
     * 测试PathVariable注解
     * 此注解可以绑定请求路径中的占位符到参数中
     *
     * @param id 请求参数id
     * @return 返回一个路径
     */
    @GetMapping("update/{id}")
    public String testPathVariable(@PathVariable("id") String id) {
        System.out.println(id);
        return "/success.jsp";
    }
}
补充:RESTful
概述

Restful 是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

Restful 风格的URL:请求路径一样,可以根据不同的请求方式去执行后台的不同方法

RESTful风格的 URL 优点
  • 结构清晰
  • 符合标准
  • 易于理解
  • 扩展方便
RequestBody

用于获取请求体内容,即 URL 中的参数部分

【注意】不能使用 Get 类型的请求

属性描述
required是否必须有请求体,默认为 true,如果使用 get 请求会报错。如果取 false 使用 get 请求会获取 null

前端页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试常用注解</title>
</head>
<body>
    <form id="form" action="annotation/requestBody" method="post">
        <table>
            <tr>
                <td>账号</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>密码</td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr>
                <td>年龄</td>
                <td><input type="text" name="userInfo.age"></td>
            </tr>
            <tr>
                <td colspan="2">
                    <input type="reset" value="重置">
                    <input type="submit" value="提交">
                </td>
            </tr>
        </table>
    </form>
</body>
</html>

Controller

@Controller
@RequestMapping("annotation")
public class AnnotationCollection {
    /**
     * 测试RequestBody注解
     * 此注解用于获取请求体内容,必须使用Post请求
     *
     * @param context 请求内容
     * @return 返回一个路径
     */
    @PostMapping("requestBody")
    public String testRequestBody(@RequestBody String context) {
        System.out.println(context);

        return "/success.jsp";
    }
}
GetMapping

@GetMapping 等价于 @RequestMapping 的 GET 请求方式

PostMapping

@PostMapping 等价于 @RequestMapping 的 POST 请求方式

RestController

@RestController 注解相当于 @ResponseBody + @Controller 合在一起的作用。

【注意】如果使用了 RestController 注解,则 Controller 中的方法无法进行页面的跳转(jsp页面,或者html),配置的视图解析器 InternalResourceViewResolver 不起作用,返回的内容就是Return 里的内容。

案例代码

@RestController
@RequestMapping("restController")
public class TestRestController {
    @RequestMapping("show")
    public User show() {
        UserInfo userInfo = new UserInfo(12, "男", new Date(), "真帅");

        return new User(1001, "123456", userInfo);
    }
}

Spring MVC 使用 ajax 进行前后端交互

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>05_SpringMVC_01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <!--Spring MVC-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.3</version>
        </dependency>

        <!--Jackson绑定依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.1</version>
        </dependency>
</project>

springMvc.xml 配置文件

【注意】如果直接访问静态资源会报错 404,因为全部都被前端控制器拦截下来,所以需要对静态资源进行放行

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启SpringMVC框架注解的支持并使用自定义类型转换器 -->
    <mvc:annotation-driven/>

    <!--设置所有静态资源不被拦截-->
    <mvc:default-servlet-handler/>

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.fc.controller"/>
</beans>

前端页面

!DOCTYPE html>
<html lang="en">
<head>
    <title>测试Ajax</title>
    <meta charset="UTF-8">
    <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-1.8.3.min.js"></script>
<!--    <script type="application/javascript" src="js/jquery-1.8.3.min.js"></script>-->
</head>
<body>
<form id="form">
    <table>
        <tr>
            <td>年龄</td>
            <td><input id="age" type="text" name="age"></td>
        </tr>
        <tr>
            <td>性别</td>
            <td><input id="gender" type="text" name="gender"></td>
        </tr>
        <tr>
            <td>信息</td>
            <td><input id="info" type="text" name="info"></td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="reset" value="重置">
                <input type="button" value="提交" onclick="ajax()">
            </td>
        </tr>
    </table>
</form>
</body>
<script>
    function ajax() {
        var age = $("#age").val();
        var gender = $("#gender").val();
        var info = $("#info").val();
        $.ajax({
            type: "POST",
            url: "ajax/show",
            data: {"age": age, "gender": gender, "info" : info},
            success: function (data) {
                alert("年龄: " + data.age);
                alert("性别: " + data.gender);
                alert("信息: " + data.info);

                $("#age").val(data.age);
                $("#gender").val(data.gender);
                $("#info").val(data.info);
            }
        });
    }
</script>
</html>

Controller

@Controller
@RequestMapping("ajax")
public class AjaxController {
    @RequestMapping("show")
    @ResponseBody
    public UserInfo show(UserInfo userInfo) {
        System.out.println(userInfo);

        userInfo.setAge(20);
        userInfo.setGender("男");
        userInfo.setInfo("易烊千玺");

        return userInfo;
    }
}

Spring MVC 文件上传

SpringMVC 框架提供了 MultipartFile 对象,该对象表示上传的文件,要求变量名称必须和表单 file 标签的 name 属性名称相同

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>05_SpringMVC_01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <!--SpringMVC-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.3</version>
        </dependency>

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

</project>

springmvc.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启SpringMVC框架注解的支持并使用自定义类型转换器 -->
    <mvc:annotation-driven/>

    <!--设置所有静态资源不被拦截-->
    <mvc:default-servlet-handler/>

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.fc.controller"/>

    <!--配置文件解析器-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="50000000" />
    </bean>

</beans>

前端页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试文件上传</title>
</head>
<body>
    <form method="post" action="file/upload" enctype="multipart/form-data">
        <input type="file" name="upload">
        <input type="submit" value="上传">
    </form>
</body>
</html>

Controller

@Controller
@RequestMapping("file")
public class FileUploadController {
    @RequestMapping("upload")
    public ModelAndView upload(MultipartFile upload) {
        // 准备一个存放图片的路径
        String path = "D:/server/apache-tomcat-8.5.37/webapps/upload";

        // 创建File对象
        File file = new File(path);

        // 如果当前路径为空
        if (!file.exists()) {

            // 创建该文件夹的多级目录
            file.mkdirs();
        }

        // 说明上传文件项
        // 获取上传文件的名称
        String filename = upload.getOriginalFilename();

        try {
            // 如果文件名称不为空
            if (filename != null) {
                // 完成文件上传
                upload.transferTo(new File(path, filename));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 创建ModelAndView对象
        ModelAndView modelAndView = new ModelAndView();

        // 将图片的路径Model中
        modelAndView.addObject("img", "http://localhost:8081/upload/" + filename);

        // 设置要跳转的路径
        modelAndView.setViewName("/success.jsp");

        return modelAndView;
    }
}

跳转页面

【注意】使用 EL 表达式要设置对应的 page 指令

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>成功页</title>
</head>
<body>
    <img src="${img}">
</body>
</html>

Spring MVC 文件下载

前端页面

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>成功页</title>
</head>
<body>

    <form action="/file/download?fileName=${img}" method="post">
        <input type="submit" value="文件下载">
    </form>
</body>
</html>

Controller

@Controller
@RequestMapping("file")
public class FileUploadController {
    /**
     * 此方法用于文件下载
     *
     * @param fileName 文件名
     * @param response 响应对象
     */
    @RequestMapping("download")
    public void download(String fileName, HttpServletResponse response) {
        // 声明下载的路径
        String path = "D:/server/apache-tomcat-8.5.37/webapps/upload";

        // 使用路径和文件名创建File对象
        File file = new File(path, fileName);

        // 设置响应头告知浏览器以下载的方式打开
        response.setHeader("Content-Disposition", "attachment;filename=" + file.getName());

        // 声明输入输出流
        ServletOutputStream outputStream = null;
        BufferedInputStream inputStream = null;
        try {
            outputStream = response.getOutputStream();
            inputStream = new BufferedInputStream(new FileInputStream(file));

            // 准备缓冲区
            byte[] buffer = new byte[1024 * 8];

            // 如果缓冲区没有读完
            while (inputStream.read(buffer) != -1) {
                // 写入数据
                outputStream.write(buffer);
                // 刷新
                outputStream.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            try {
                if (outputStream != null) {
                    outputStream.close();
                }

                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

SpringMVC 异常处理

概述

SpringMVC通过HandlerExceptionResolver处理程序的异常,包括处理映射,数据绑定及处理器执行时发生异常。

核心 API
interface HandlerExceptionResolver {
    // 尝试解决在处理程序执行期间引发的给定异常,如果合适,返回代表特定错误页面的ModelAndView
	ModelAndView resolveException(HttpServletRequest reqeust, HttpServletResponse response, Object handler, Exception ex);
}

当发生异常时,Spring MVC将调用 resolveException 方法,并转到ModelAndView 对应视图中,作为一个异常报告页面,反馈给用户!

HandlerExceptionResolver 拥有4个实现类:

DefaultHandlerExceptionResolver SimpleMappingExceptionResolver 
AnnotationMethodHandlerExceptionResolver
ResponseStatusExceptionResolver
处理方案

Spring MVC 提供了提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。

核心接口:HandlerExceptionResolver,它会将Spring MVC框架的异常转换为相应的相应状态码!

异常类型响应状态码
ConversionNotSupportedException500(Web服务器内部错误)
HttpMediaTypeNotAcceptableException406(无和请求accept匹配的MIME类型)
HttpMediaTypeNotSupportedException415(不支持MIME类型)
HttpMessageNotReadableException400
HttpMessageNotWritableException500
HttpRequestMethodNotSupportedException405
MissingServletRequestParameterException400
案例代码

自定义异常类

public class MyException extends RuntimeException {
    public MyException() {
        super();
    }

    public MyException(String message) {
        super(message);
    }
}

错误跳转页面

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" isErrorPage="true" %>
<html>
<head>
    <title>错误信息页</title>
</head>
<body>
    ${message}
</body>
</html>

自定义异常处理器

/**
 * 自定义异常处理器,实现HandlerExceptionResolver并重写其中的方法
 */
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        // 创建一个modelAndView对象
        ModelAndView modelAndView = new ModelAndView();

        // 判断抛出的异常类型是否是自定义异常并添加到modelAndView中
        if (e instanceof MyException) {
            modelAndView.addObject("message", "自定义异常" + e.getMessage());
        } else {
            modelAndView.addObject("message", e.getMessage());
        }

        // 设置跳转视图
        modelAndView.setViewName("/error.jsp");

        return modelAndView;
    }
}

springmvc.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启SpringMVC框架注解的支持并使用自定义类型转换器 -->
    <mvc:annotation-driven/>

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.fc.controller"/>

    <!--配置异常处理器-->
    <bean name="myHandlerExceptionResolver" class="com.fc.exception.MyHandlerExceptionResolver"/>

</beans>

Controller

@Controller
@RequestMapping("exception")
public class ExceptionController {
    /**
     * 测试系统异常 by / zero
     * 
     */
    @RequestMapping("sysException")
    public String testSystemException()  {

        int num = 1 / 0;
        return "/index.html";
    }

    /**
     * 测试自定义异常
     * 
     * @throws MyException 抛出自定义异常
     */
    @RequestMapping("myException")
    public void testMyException() throws MyException {

        throw new MyException("报错");
    }
}

Spring 拦截器

Spring MVC 的拦截器类似于 Servlet 中的过滤器

概述

SpringMVC 框架中的拦截器用于对处理器进行预处理和后处理的技术。

可以定义拦截器链,连接器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链中的拦截器会按着定义的顺序执行。

拦截器也是AOP思想的一种实现方式。

想要自定义拦截器,需要实现HandlerInterceptor接口,添加未实现的方法,并在 springmvc 配置文件中进行配置

过滤器与拦截器的区别
  • 过滤器是Servlet规范的一部分,任何框架都可以使用过滤器技术。拦截器是SpringMVC框架独有的。
  • 过滤器配置了/*,可以拦截任何资源。拦截器只会对控制器中的方法进行拦截。
案例代码

maven

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.5</version>
</dependency>

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>

自定义拦截器

// 自定义拦截器
public class MyInterceptor implements HandlerInterceptor {
    // 在请求到达Handler之前,先执行这个前置处理方法.当该方法返回false时,请求直接返回,不会传递到链中的下一个拦截器,更不会传递到链尾的Handler,只有返回true时,请求才会向链中的下一个处理节点传递!
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println("调用之前触发此方法:" + handler);
        /*
         * return false 代表拦截,不调用controller方法直接返回
         * true 不拦截
         */
        return true;
    }

    // 执行器调用之后调用此方法
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object
            handler, ModelAndView modelAndView) throws Exception {
        System.out.println("handler已经被调用:" + handler);
    }

    // 响应已经被渲染后,执行该方法
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
        System.out.println("响应已经进行了渲染");
    }
}

Controller

@Controller
@RequestMapping("user")
public class UserController {

    @RequestMapping("test")
    public void get() {
        System.out.println("测试拦截器");
    }
}

springMvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--扫描指定包下的注解-->
    <context:component-scan base-package="com.fc.controller"/>

    <!-- 视图解析器对象 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>

    <!-- 开启SpringMVC框架注解的支持 -->
    <mvc:annotation-driven/>

    <!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--指定拦截的路径-->
            <mvc:mapping path="/user/*"/>
            <!--指定不拦截-->
            <!--<mvc:exclude-mapping path=""/>-->
            <!--自定义拦截器-->
            <bean class="com.fc.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
</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">

    <!--配置前端控制器-->
    <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>
        <!--启动时加载-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!--配置前端控制器的访问路径-->
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

【扩展】Jersey 跨域文件上传

导入依赖

<?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>05_SpringMVC_CrossDomainUpload</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <!--springMvc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.3</version>
        </dependency>

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

        <!--jersey-->
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-core</artifactId>
            <version>1.19.1</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-client</artifactId>
            <version>1.19.1</version>
        </dependency>
    </dependencies>

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

</project>

Springmvc.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启SpringMVC框架注解的支持并使用自定义类型转换器 -->
    <mvc:annotation-driven/>

    <!--设置所有静态资源不被拦截-->
    <mvc:default-servlet-handler/>

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.fc.controller"/>

    <!--配置文件解析器-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="50000000" />
    </bean>

</beans>

Controller

@Controller
@RequestMapping("fileUpload")
public class FileUploadController {
    @RequestMapping("crossDomain")
    public String testCrossDomainUpload(MultipartFile upload) {
        // 准备文件上传的路径
        String path = "http://localhost:8081/upload/";

        // 获取文件名
        String filename = upload.getOriginalFilename();

        // 获取文件名的后缀名
        filename = filename.substring(filename.indexOf('.'));

        // 声明时间格式
        DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");

        // 获取当前时间的格式
        String format = dateFormat.format(new Date());

        // 拼接到文件名上
        filename = format + filename;

        // 创建客户端对象
        Client client = Client.create();

        // 连接到图片服务器
        WebResource resource = client.resource(path + filename);

        try {
            // 上传文件
            resource.put(upload.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }

        return "/index.jsp";
    }
}

Tomcat 下的 web.xml 文件

【注意】tomcat 在默认情况下是不支持 put 请求的。而 jersey 使用的是 put 请求。

要想实现 tomcat 支持 put 请求,需要修改 tomcat下\conf\web.xml

<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>readonly</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
</web-app>

ntext/spring-context.xsd">

<!-- 开启SpringMVC框架注解的支持并使用自定义类型转换器 -->
<mvc:annotation-driven/>

<!--设置所有静态资源不被拦截-->
<mvc:default-servlet-handler/>

<!-- 开启注解扫描 -->
<context:component-scan base-package="com.fc.controller"/>

<!--配置文件解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="50000000" />
</bean>
```

Controller

@Controller
@RequestMapping("fileUpload")
public class FileUploadController {
    @RequestMapping("crossDomain")
    public String testCrossDomainUpload(MultipartFile upload) {
        // 准备文件上传的路径
        String path = "http://localhost:8081/upload/";

        // 获取文件名
        String filename = upload.getOriginalFilename();

        // 获取文件名的后缀名
        filename = filename.substring(filename.indexOf('.'));

        // 声明时间格式
        DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");

        // 获取当前时间的格式
        String format = dateFormat.format(new Date());

        // 拼接到文件名上
        filename = format + filename;

        // 创建客户端对象
        Client client = Client.create();

        // 连接到图片服务器
        WebResource resource = client.resource(path + filename);

        try {
            // 上传文件
            resource.put(upload.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }

        return "/index.jsp";
    }
}

Tomcat 下的 web.xml 文件

【注意】tomcat 在默认情况下是不支持 put 请求的。而 jersey 使用的是 put 请求。

要想实现 tomcat 支持 put 请求,需要修改 tomcat下\conf\web.xml

<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>readonly</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
</web-app>

【扩展】MockMVC

概述

Spring MVC 测试框架(也称为MockMvc)为测试 Spring MVC 应用程序提供支持。它执行完整的 Spring MVC 请求处理,但通过模拟请求和响应对象而不是正在运行的服务器。

MockMvc 可以单独用于执行请求和验证响应。也可以通过 WebTestClient 来使用它,在该 WebTestClient 中插入MockMvc 作为服务器来处理请求。 WebTestClient 的优点是可以处理更高级别的对象而不是原始数据,并且可以切换到针对实时服务器的完整端到端 HTTP 测试并使用相同的测试API。

官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#spring-mvc-test-framework

作用

SpringMVC 单元测试的独立测试

为什么要使用 MockMVc

对模块进行集成测试时,希望能够通过输入 URL 对 Controller 进行测试,如果通过启动服务器,建立 http client 进行测试,这样会使得测试变得很麻烦,比如,启动速度慢,测试验证不方便,依赖网络环境等,所以为了可以对Controller 进行测试,就可以使用 MockMVC。

MockMvc 实现了对 Http 请求的模拟,能够直接使用网络的形式,转换到 Controller 的调用,这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便

相关API
// Web的ApplicationContext
interface WebApplicationContext extends ApplicationContext {}

// 导入的主类,以访问所有可用的MockMvcBuilders
class MockMvcBuilders {
    // 获取MockMvc
    static MockMvc webAppContextSetup(WebApplicationContext context).build();
}

// 服务器端Spring MVC测试支持的主要入口点
class MockMvc {
    // 执行一个请求并返回一个类型,该类型允许对结果链接进一步的操作
    ResultActions perform(RequestBuilder requestBuilder);
}

// 允许对已执行请求的结果应用操作
interface ResultActions {
    // 返回已执行请求的结果以直接访问结果。
    MvcResult andReturn();
    
    // 执行期望
    ResultActions andExpect(ResultMatcher matcher);
    
    // 一般执行
    ResultActions andDo(ResultHandler handler);
}

// 提供对已执行请求结果的访问
interface MvcResult {
    // 获取请求对象
    MockHttpServletRequest getRequest();
    
    // 获取响应对象
    MockHttpServletResponse getResponse();
    
    // 获取执行器
    Object getHandler();

	// 获取拦截器
	HandlerInterceptor[] getInterceptors();

	// 获取ModelAndView
	ModelAndView getModelAndView();
}

// HttpServletResponse接口的模拟实现。
class MockHttpServletResponse implements HttpServletResponse {
	// 设置编码集
    void setCharacterEncoding(String characterEncoding);
    
    // 以String的形式获取响应主体的内容
    String getContentAsString();
}

// 结果匹配规则
class MockMvcResultMatchers {
    // 访问与请求相关的断言
    static RequestResultMatchers request();
    
    // 访问与model相关的断言
    static ModelResultMatchers model();
    
    // 访问所选视图上的断言
    static ViewResultMatchers view();
    
    // 访问响应状态码的断言
    static StatusResultMatchers status();
    
    // 访问响应主体的断言
    static ContentResultMatchers content();
    
    // 访问响应Cookie的断言
    static CookieResultMatchers cookie();
}

// 此类中的方法将重用Spring TestContext Framework创建的MockServletContext,用于创建MockHttpServletRequestBuilde
class MockMvcRequestBuilders {
    // 为POST请求创建一个MockHttpServletRequestBuilde
    static MockHttpServletRequestBuilder post(String urlTemplate, Object... uriVars);
    
    static MockHttpServletRequestBuilder post(URI uri);
    
    static MockHttpServletRequestBuilder get(URI uri);
}

// 需要MockHttpServletRequest的默认生成器作为在MockMvc中执行请求的输入
class MockHttpServletRequestBuilder {
    // 设置请求的参数
    MockHttpServletRequestBuilder param(String name, String... values);
    
    // 指定requestURI的代表上下文路径的部分
    MockHttpServletRequestBuilder contextPath(String contextPath);
    
    // 设置请求的编码集
    MockHttpServletRequestBuilder characterEncoding(String encoding);
    
    // 设置请求体
    MockHttpServletRequestBuilder content(byte[] content);
    
    // 设置contentType请求头
    MockHttpServletRequestBuilder contentType(MediaType contentType);
    
    // 设置contentType请求头
    MockHttpServletRequestBuilder contentType(String contentType);
}
案例代码

Controller

@Controller
@RequestMapping("user")
public class UserController {
    @RequestMapping("testNoArgsNoResult")
    public void testNoArgsNoResult() {
        System.out.println("测试无参无返回值");
    }

    @RequestMapping("testArgsNoResult")
    public void testArgsNoResult(Integer id, String name) {
        System.out.println("测试有参无返回值");
        System.out.println(id + " " + name);
    }

    @RequestMapping("testPojo")
    public void testPojo(User user) {
        System.out.println("测试实体类对象");
        System.out.println(user);
    }

    @RequestMapping("testRESTful/{id}")
    public void testRESTful(@PathVariable("id") String id) {
        System.out.println("测试RESTful风格");
        System.out.println("接收的参数:" + id);
    }

    @RequestMapping("testPojoResult")
    @ResponseBody
    public User testPojoResult() {
        System.out.println("测试返回值为实体类对象");
        return new User(1, "易烊千玺", 20, "送你一朵小红花");
    }
}

MockMvc

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

// 声明使用配置文件的方式创建Spring容器
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:springMvc.xml")
@WebAppConfiguration
public class MockTest {
    // 注入WebApplicationContext对象
    @Autowired
    private WebApplicationContext context;

    // mockMvc核心入口类
    private MockMvc mockMvc;

    // 前置执行,所有的 @Test 执行前都会执行此方法
    @Before
    public void before() {
        // 通过WebApplicationContext获取mockMvc对象
        mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
    }

    // 测试无参无返回值
    @Test
    public void testNoArgsNoResult() {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/user/testNoArgsNoResult");

        try {
            mockMvc.perform(builder);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 测试有参无返回值类型
    @Test
    public void testArgsNoResult() {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/user/testArgsNoResult").param("id", "1").param("name", "易烊千玺");

        try {
            mockMvc.perform(builder);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 测试参数为实体类
    @Test
    public void testPojo() {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/user/testPojo").
                param("id", "1").
                param("name", "易烊千玺").
                param("age", "20").
                param("info", "送你一朵小红花");

        try {
            mockMvc.perform(builder);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 测试RESTful风格URL
    @Test
    public void testRESTful() {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/user/testRESTful/{id}", 1);

        try {
            mockMvc.perform(builder);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 测试返回值为实体类对象
    @Test
    public void testPojoResult() {
        // 获取请求的建造器
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/user/testPojoResult").contentType(MediaType.APPLICATION_JSON);

        try {
            // 获取结果
            MvcResult mvcResult = mockMvc.perform(builder).andReturn();

            // 获取响应对象
            MockHttpServletResponse response = mvcResult.getResponse();

            // 设置编码集
            response.setCharacterEncoding("UTF-8");

            // 获取响应内容的字符串形式
            String content = response.getContentAsString();

            // 展示
            System.out.println("结果:" + content);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 测试期望
    @Test
    public void testExpect() {
        // 获取请求的建造器
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/user/testPojoResult").contentType(MediaType.APPLICATION_JSON);

        try {
            // 获取结果
            MvcResult mvcResult = mockMvc.perform(builder).
                    // 期望值为200,如果响应的结果不是200就会报错
                            andExpect(MockMvcResultMatchers.status().
                            is(200)).
                            andReturn();

            // 获取响应对象
            MockHttpServletResponse response = mvcResult.getResponse();

            // 设置编码集
            response.setCharacterEncoding("UTF-8");

            // 获取响应内容的字符串形式
            String content = response.getContentAsString();

            // 展示
            System.out.println("结果:" + content);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 测试打印响应信息
    @Test
    public void testAndDo() {
        // 获取请求的建造器
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post("/user/testPojoResult").contentType(MediaType.APPLICATION_JSON);

        try {
            // 获取结果
            ResultActions resultActions = mockMvc.perform(builder).
                    // 期望值为200,如果响应的结果不是200就会报错
                            andExpect(MockMvcResultMatchers.status().
                            is(200)).
                            // 打印响应信息
                            andDo(MockMvcResultHandlers.print());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值