Spring MVC

概述

Spring MVC 是 Spirng 处理 WEB 层请求的一个 组件(模块)。
MVC 是指:

  • Model:模型层,包含实体类、业务逻辑基层、数据访问层。
  • View:视图层,用于页面的显示。
  • Controller:控制层,用来接收客户端的请求,并返回响应到客户端。

入门程序

  1. 新建 Maven 项目,选择 webapp 模板

image.png

  1. 然后补全目录

image.png

  1. 修改 Maven 编译级别

image.png

改成合适的 Java 版本

  1. 添加 Spring MVC 依赖和 Servlet 依赖
<!-- Spring MVC 依赖 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.3.18</version>
</dependency>
<!-- Spring context 命名空间-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.3.18</version>
</dependency>
<!-- 添加 servlet 依赖 -->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
</dependency>

导入 servlet 时要,注意和你使用的 Tomcat 版本
如果使用 Tomcat 10+,应该导入 jakarta.servlet
如果使用 Tomcat 10 以下的版本,应该打入 javax.servlet
具体参考:https://tomcat.apache.org/whichversion.html

  1. 将 pom.xml 中 标签的内容替换为:
<build>
  <!-- 指定资源文件 -->
  <resources>
    <resource>
      <directory>src/main/java</directory>
      <includes>
        <include>**/*.xml</include>
        <include>**/*.properties</include>
      </includes>
    </resource>
    <resource>
      <directory>src/main/resources</directory>
      <includes>
        <include>**/*.xml</include>
        <include>**/*.properties</include>
      </includes>
    </resource>
  </resources>
</build>

指定资源文件所在位置

  1. 在 webapp 目录下新建 admin 目录,再在 admin 下新建 hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

hello world

</body>
</html>
  1. 添加 springmvc 配置文件:spring-mvc.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 https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 添加包扫描,开启对 @Controller 注解的支持 -->
    <context:component-scan base-package="com.guyi.springmvc.controller"/>

    <!-- 添加视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置前缀 -->
        <property name="prefix" value="/admin/"/>

        <!-- 配置后缀 -->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>
  1. src/main/webapp/WEB-INF/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>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 初始化springmvc配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- 配置拦截的请求(需要处理的请求) -->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
  1. 创建一个 controller 包,在这个包下创建一个 HelloController.java
@Controller
@RequestMapping("/entry")
public class HelloController {
    @RequestMapping("/hello.action")
    public String hello() {
        System.out.println("GET hell");
        return "hello";
    }
}
  1. 配置 Tomcat,这里使用 Tomcat 8.5, 如下设置:

image.png

  1. 启动项目,浏览器访问 localhost:8080/springmvc/entry/hello,显示如下内容:

image.png

细节

  1. 下图中的配置是 web.xml 中的配置:

image.png
作用是将特定 URL 模式映射到一个特定的 Servlet 处理器,例如:Spring MVC 的 DispatcherServlet。
<servlet-mapping> 元素的 <url-pattern> 设置为 “/” 时,访问 “localhost:8080/springmvc/entry/hello” 时,Controller 的响应结果会被拼接为 “/admin/hello.jsp”,这时,会去访问 "“localhost:8080/springmvc/admin/hello.jsp”,可以正常访问到资源。
而当 设置为 “/*” 时,访问 “localhost:8080/springmvc/entry/hello” 时,Controller 的响应结果会被拼接为 “/springmvc/admin/hello.jsp”,这时,会去访问 "“localhost:8080/springmvc/springmvc/admin/hello.jsp”,多了一个 springmvc,不可以访问到资源。

原因如下:
<servlet-mapping> 元素的 <url-pattern> 设置为 “/” 时,表示将 DispatcherServlet 映射到 Web 应用程序的根目录下的所有 URL。例如,入门程序的上下文路径是" /springmvc",那么所有的URL,都会被 DispatcherServlet 处理,比如:请求 “/springmvc/entry/hello”。
当 设置为 “/*” 时,表示将 DispatcherServlet 映射到所有的 URL,包括根路径。这意味着所有的 URL 都会交给 DispatcherServlet 处理,包括静态资源(如CSS、JS文件等)。这种配置可能会导致性能问题,因为 DispatcherServlet 将会处理所有的请求。
因此, 设置为 “/” 比较推荐,它只映射到应用程序的根路径下的 URL,而不会处理静态资源,可以更有效地利用服务器资源。

Spring MVC 的执行流程

Spring MVC 注解开发

@RquestMapping

作用
  1. 用来映射服务器访问的路径
  2. 这个注解可以写在方法上,为这个方法注册一个可以访问的名称(路径)
  3. 这个注解可以加在类上,用于区分不同类中相同访问路径的方法(方法上@RequsestMapping()指定的路径相同)
  4. 这个注解可以区分 GET 请求和 POST 请求 :
    @RequestMapping("/path", method=RequestMethod.POST)

支持的请求类型如下:
image.png

如果不指定 method 的值,则任意请求都可以访问

详解 RequestMethod 各值的作用

RequestMethod 是 Spring 框架中的一个枚举类,用于表示 HTTP 请求的方法类型。它定义了一些常用的 HTTP 方法,包括:

  1. GET:用于获取资源。一般用于查询操作。
  2. POST:用于提交数据,向服务器发送数据。一般用于创建资源。
  3. PUT:用于更新已存在的资源。
  4. DELETE:用于删除资源。
  5. HEAD:用于获取资源的头部信息,但不返回具体内容。
  6. OPTIONS:用于获取服务器支持的 HTTP 方法。
  7. PATCH:用于局部更新资源。

这些枚举值在 Spring MVC 中的 @Controller 和 @RequestMapping 注解中经常被用到,用于指定处理请求的方法控制器应该对应的 HTTP 请求方法。
例如,使用 @RequestMapping 注解时,可以通过指定 RequestMethod 来限制处理方法只能响应特定的 HTTP 方法,如 @RequestMapping(value = "/example", method = RequestMethod.GET) 表示只能处理 GET 请求。
再比如:@RequestMapping(value = "/example", method = RequestMethod.HEAD),表示只处理处理 HTTP 请求中的 HEAD 方法,并且映射到路径 “/example”。

HEAD 方法是 HTTP 中的一个请求方法,它与 GET 方法类似,区别在于 HEAD 方法只请求服务器返回特定资源的头部信息,而不返回具体内容。通常用于获取资源的元数据,如获取内容类型、内容长度、最后修改时间等,而不需要获取实际的资源内容。
因此,在处理HEAD请求时,应注意不要在响应中包含具体内容,只返回头部信息即可。

基本使用
@Controller
@RequestMapping("/xx")
public class xxController {
    @RequestMapping("/yy")
    public String yyy() {} 
}

访问的时候,localhost:8080/springmvc/xx/yy
如果将类上的 @RequestMapping("/xx") 去掉,应当访问 localhost:8080/springmvc/yy

接收请求的参数
  1. 单个数据获取

假如有一个 form 表单

<h2>单个数据获取</h2>
<form action="${pageContext.request.contextPath}/one">
  姓名: <input type="text" name="name"/><br>
  年龄: <input type="text" name="age"/><br>
  <input type="submit" value="提交">
</form>

Spring MVC 接收表单提交的数据

/**
 * 姓名: <input type="text" name="myName"/><br>
 * 年龄: <input type="text" name="age"/><br>
 * 形参就是 name 属性的值, SpringMVC 可以进行自动类型转换
 */
@RequestMapping("/one")
public String One(String name, int age) {
    System.out.println("myName: " + myName);
    System.out.println("age: " + age);
    return "main";
}
  1. 对象封装方式获取

可以在入门程序的 index.jsp 中添加一个表单

<h2>对象封装方式获取</h2>
<form action="${pageContext.request.contextPath}/entry/user">
    姓名: <input type="text" name="name"/><br>
    年龄: <input type="text" name="age"/><br>
    <input type="submit" value="提交">
</form>

然后创建一个实体类

public class User implements Serializable {

    private static final long serialVersionUID = 8351864396859634105L;

    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private Integer age;

    // todo
    // toString、setter、getter 方法自行添加
}

在 HelloController 类中添加如下内容:

@RequestMapping(value = "/user")
public void getUser(User user) {
    System.out.println(user);
}

启动项目,访问:[http://localhost:8080/springmvc/](http://localhost:8080/springmvc/),在输入框中输入内容,然后点击提交按钮,IDE 控制台应该打印如下内容:
image.png

具体的值应该和你输入的内容对应

  1. 动态占位符获取

还是在 index.jsp 中添加内容:

<h2>动态占位符提交数据</h2>
<a href="${pageContext.request.contextPath}/entry/dynamic/李四/22">动态提交</a>

如果页面出现乱码,在 index.jsp 的最上方添加:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

在 HelloController 类中添加如下内容:

@RequestMapping("/dynamic/{name}/{age}")
public void byDynamic(@PathVariable String name, @PathVariable("age") int age) {
    System.out.println("姓名: " + name);
    System.out.println("年龄: " + age);
}

启动项目,访问:[http://localhost:8080/springmvc/](http://localhost:8080/springmvc/),点击刚添加的链接,IDE 控制台应该打印如下内容:
image.png

  1. 映射名称不一致

还是在 index.jsp 中添加内容:

<h2>映射名称不一致</h2>
<form action="${pageContext.request.contextPath}/entry/variation">
    姓名: <input type="text" name="xxxName"/><br>
    年龄: <input type="text" name="age"/><br>
    <input type="submit" value="提交">
</form>

在 HelloController 类中添加如下内容:

@RequestMapping("/variation")
public void variationParams(@RequestParam("xxxName") String name, @RequestParam("age") Integer xxxAge) {
    System.out.println("name:" + name);
    System.out.println("xxxAge:" + xxxAge);
}

当请求的参数名称无法和方法的形参名称对应起来是,可以使用 @RequestParam() 注解来建立映射关系,将 @RequestParam()value 属性值设置为请求中的对应的参数名称即可。

启动项目,访问:[http://localhost:8080/springmvc/](http://localhost:8080/springmvc/),在输入框中输入内容,然后点击提交按钮,IDE 控制台应该打印如下内容:
image.png

具体的值应该你输入的内容对应

  1. 手动获取

index.jsp 中添加:

<h2>手工提取数据</h2>
<form action="${pageContext.request.contextPath}/manual">
    姓名: <input type="text" name="name"/><br>
    年龄: <input type="text" name="age"/><br>
    <input type="submit" value="提交">
</form>

在 HelloController 类中添加如下内容:

@RequestMapping("/manual")
public void byManual(HttpServletRequest httpServletRequest) {
    String name = httpServletRequest.getParameter("name");
    int age = Integer.parseInt(httpServletRequest.getParameter("age"));
    System.out.println("name: " + name);
    System.out.println("age: " + age);
}

启动项目,访问:[http://localhost:8080/springmvc/](http://localhost:8080/springmvc/),在输入框中输入内容,然后点击提交按钮,IDE 控制台应该打印如下内容:
image.png

具体的值应该你输入的内容对应

@RequestBody

这个标签可以用来处理 POST、PUT、PATCH、DELETE 请求的参数,这个标签不可以用于处理 GET 请求。将请求内容转化为对应的 Java 对象。

示例:

  1. controller 包下添加一个 RequestBodyController 类:
@Controller
@RequestMapping("/post")
public class RequestBodyController {
    @RequestMapping(value = "/test", method = RequestMethod.POST)
    @ResponseBody
    public User test(@RequestBody User user) {
        System.out.println(user);
        return user;
    }
}
  1. webapp/admin 下添加 test_requestbody.jsp 文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>

<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.6.3.js"></script>
<script type="text/javascript">
    function showStu() {
        const name = $("#name").val();
        const age = $("#age").val();
        const user = {
            name,
            age
        }
        user.name = name
        user.age = age
        const userJson = JSON.stringify(user);
        $.ajax({
            url: "${pageContext.request.contextPath}/post/test",
            type: "post",
            data: userJson,
            dataType: "json",
            contentType: "application/json;charset=UTF-8",
            success: function (ref){
                alert("返回结果: " + JSON.stringify({result:ref}))
                $("#myDiv").html("成功")
            },
        });
    }
</script>
<body>

姓名: <input id="name" type="text" name="name"/><br>
年龄: <input id="age" type="text" name="age"/><br>
<input type="button" value="提交" onclick="showStu()">
<div id="myDiv">等待服务器返回数据</div>

</body>
</html>

  1. 浏览器访问 localhost:8080/springmvc/admin/test_requestbody.jsp,点击按钮,观察效果。

Controller 中方法的返回值

  • String
    • 客户端资源的地址
    • 自动拼接前缀和后缀
    • 屏蔽自动拼接字符串
    • 指定返回路径
  • Object
    • 返回 json 格式的对象
    • 自动将对象或者集合转为 json
    • 一般用于 ajax 请求
    • 注意:底层使用的是 Jackson 工具进行转化,因此必须添加 Jackson 依赖
  • void
    • 一般用于 ajax 请求
  • 基本数据类型
    • 一般用于 ajax 请求
  • ModelAndView
    • 返回数据和视图对象
    • 使用的很少

示例:完成 ajax 请求访问服务器,返回 Student 集合

  1. 添加 Jackson 依赖
<!-- jackson 依赖 -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.14.1</version>
</dependency>
  1. 在 webapp 目录下新建 js,添加 JQuer 函数库
  2. 在 webapp/admin 目录下新建一个 Student.jsp
  3. 在 Student.jsp 添加如下内容
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>学生页</title>
</head>

<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.6.3.js"></script>
<script type="text/javascript">
    function showStu() {
        $.ajax({
            url: "${pageContext.request.contextPath}/student/list",
            type: "get",
            dataType: "json",
            success: function (stuList){
                var s = ""
                $.each(stuList, function (i, stu) {
                    s += stu.name + "-------" + stu.age + "<br>"
                })

                // 回显数据
                $("#myDiv").html(s)
            }
        });
    }
</script>
<body>

<a href="javaScript:showStu()">访问服务器返回学生集合</a>
<div id="myDiv">等待服务器返回数据</div>

</body>
</html>

该内容不是本篇文章重点,不做解释

  1. 添加一个 Student 类
public class Student implements Serializable {

    private static final long serialVersionUID = 4671473146568140537L;

    /**
     * 学生姓名
     */
    private String name;

    /**
     * 年龄
     */
    private Integer age;

    // todo
    // 无参构造器、全参数构造器、toString、setter、getter 自行添加
}
  1. 创建一个 UserController:
@Controller
@RequestMapping("/student")
public class UserController {
    @RequestMapping("/list")
    @ResponseBody
    public List<Student> getList() {
        List<Student> list = new ArrayList<>();

        Student student1 = new Student("张三", 22);
        Student student2 = new Student("李四", 23);
        Student student3 = new Student("王五", 24);

        list.add(student1);
        list.add(student2);
        list.add(student3);

        // Spring MVC 会将集合转为数组
        return list;
    }
}
  1. 在 springmvc.xml 文件中添加注解驱动:mvc:annctationdriven/,它用来解析 @ResponseBody 注解:

添加 mvc 命名空间:

xmlns:mvc="http://www.springframework.org/schema/mvc"

http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd

image.png

添加注解驱动器

<!-- 添加注解驱动, 专门处理 ajax 请求的 -->
<mvc:annotation-driven>
    <!-- 解决需要添加后缀问题 -->
    <mvc:path-matching suffix-pattern="true"/>
</mvc:annotation-driven>

<!-- 定义 DefaultServletHttpRequestHandler -->
<mvc:default-servlet-handler />

对于 <mvc:default-servlet-handler />:
对进入 DispatcherServlet 的 URL 进行筛查,如果发现是静态资源的请求,就将该请求转由 Web 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由 DispatcherServlet 继续处理。
如果没有配置这个内容,加载 js 的时候可能会出现问题。

  1. 启动项目,访问 [http://localhost:8080/springmvc/admin/Student.jsp](http://localhost:8080/springmvc/admin/Student.jsp)

image.png
内容变为:
image.png

Spirng MVC 解决中文乱码

在执行上面的例子时,如果有传入中文数据,打印出来的内容可能存在乱码,可以通过以下方式解决:
web.xml 文件中添加如下配置:

<!-- 中文编码过滤器配置 -->
<filter>
    <filter-name>encode</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>
    <init-param>
        <param-name>forceResponseEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encode</filter-name>
    <!-- 拦截所有请求 -->
    <url-pattern>/*</url-pattern>
</filter-mapping>

这样一般就不会出现乱码问题了。
这段配置一定不要去背!!!要用的时候复制即可。

请求转发和请求重定向

请求转发:forward
请求重定向:redirect

请求转发是基于服务器的跳转。
重定向是基于客户端的跳转,直接体现是浏览器地址栏上的 url 会改变。
image.png

为了便于描述,后续将被 **@RequestMapping()** 标记的方法称之为 action

在 Spring MVC 中默认是请求转发,如果要开启重定向,在返回地址上添加一个 redirect:
示例:

// 需要重定向
return "redirect:/other.action";

使用这种操作,Spring MVC 将不会对请求地址进行前后缀的拼接,可以将请求重定向到另外一个 action 中。

如果你希望在请求转发时,不进行前后缀的拼接,可以使用如下方式:

return "forward:/other";

使用这种操作,Spring MVC 将不会对请求地址进行前后缀的拼接,可以将请求跳转至另外一个 action 中。
如果不加 forward:,进行的也是请求转发,只不过Spring MVC 会根据设置的前后缀进行请求拼接。

实际测试一下:

  1. admin 目录下新建三个 jsp 文件

request.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>转发和重定向</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/one">请求转发页面(默认)</a><br><br>
<a href="${pageContext.request.contextPath}/two">请求转发action</a><br><br>
<a href="${pageContext.request.contextPath}/three">重定向页面</a><br><br>
<a href="${pageContext.request.contextPath}/four">重定向action</a><br><br>
</body>
</html>

test_forward.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>跳转</title>
</head>
<body>

你的请求被转发到这里,地址栏的 url 还是原来的!

</body>
</html>

test_redirect.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>重定向</title>
</head>
<body>

你的请求被重定向到这里,地址栏的 url 还是改变了!

</body>
</html>
  1. controller 包下创建 RequestTypeController.java
@Controller
public class RequestTypeController {
    @RequestMapping("/other")
    public String other() {
        return "hello";
    }

    @RequestMapping("/one")
    public String one() {
        System.out.println("这是请求转发页面跳转");
        // 默认是请求转发,使用视图解析器拼接前缀后缀进行页面跳转
        return "test_forward";
    }

    @RequestMapping("/two")
    public String two() {
        System.out.println("这是请求转发action");
        // "forward:"  表示这是请求转发action, 不要进行前后缀的拼接
        return "forward:/other";
    }

    @RequestMapping("/three")
    public String three() {
        System.out.println("这是重定向页面");
        return "redirect:/admin/test_redirect.jsp";
    }

    @RequestMapping("/four")
    public String four() {
        System.out.println("这是重定向action");
        return "redirect:/other";
    }
}
  1. 启动项目,访问 [http://localhost:8080/springmvc/admin/request.jsp](http://localhost:8080/springmvc/admin/request.jsp) ,依次点击链接,观察页面变化,特别是浏览器地址栏 url 的变化。

这里的效果不好展示,感兴趣的可以自行测试效果。

常用的 Controller 方法参数类型

  1. HttpServletRequest:用于获取 HTTP 请求的信息,如请求头、请求参数等。
  2. HttpServletResponse:用于操作 HTTP 响应,如设置响应头、发送响应内容等。
  3. HttpSession:用于获取或操作当前会话的状态信息,如存储和获取属性值等。
  4. Model:用于在请求处理方法中传递数据,以供视图层展示。可以通过添加属性到 Model 对象中,然后在视图层渲染时访问这些属性。
  5. Map:作为 Model 的替代选择,也可以用于传递数据到视图层。Map 对象可以用来存储键值对对象,将数据传递给视图层。
  6. ModelMap:继承自 LinkedHashMap,即在 Map 的基础上扩展了一些 Spring MVC 特有的功能。主要用于封装数据以供视图渲染,同时提供了方便的方法用于操作和访问 Model 数据。

注意Model、Map、ModelMap 和 request 一样,都是用请求作用域进行数据传递,所以服务器的跳转必须是请求跳转,无法在重定向时使用

日期处理

日期的提交处理

单个日期处理

假如有一个表单

<form action="${pageContext.request.contextPath}/testDate">
    日期: <input type="date" name="date"/><br>
    <input type="submit" value="提交"/><br>
</form>

接收时,可以使用如下代码进行日期格式化:

@Controller
public class MyDateAction {
    @RequestMapping("/testDate")
    public String myDate(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
        System.out.println("日期: " + date);
        return "show";
    }
}

这里使用的是 @DateTimeFormat() 注解进行日期格式化,这个注解的 pattern 属性可以用来指定日期的格式。

类中的全局日期处理器
@Controller
public class MyDateAction {
    /**
     * 格式化日期的刷子
     */
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

	/**
     * 注册一个全局日期处理的注解
     * @param dataBinder 数据(可以绑定的不止有日期)绑定
     */
    @InitBinder
    public void initBinder(WebDataBinder dataBinder) {
        /*
            需要处理的数据类型:Date.class
            使用的工具: new CustomDateEditor(sdf, true)
                sdf: 工具
                true: 允许为空
         */
        dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(sdf, true));
    }
    
    @RequestMapping("/testDate")
    public String myDate(Date date) {
        System.out.println("日期: " + date);
        return "show";
    }
}

给实体类的日期属性注入依赖

在对应的属性或者 setter 方法上添加 @DateTimeForamt(pattern="yyyy-MM-dd")

给 JSON 进行数据处理

在对应属性或者 getter 方法(一般情况下标注的是 getter,但是也可以用于 setter 方法)上添加 @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")

<mvc:annotation-driven/>

这个标签之前已经使用过了,这里主要介绍它的作用。

<mvc:annotation-driven/> 标签会自动注册两个 Bean:

  1. DefaultAnnotationHandlerMapping(处理映射器)

主要处理 @RequestMapping 注解,并将其注册到请求映射表中

  1. AnntationMethodHandlerAdapter(处理适配器)

主要处理请求,确定每个请求需要用哪个 Controller 类的哪个方法来处理

这个标签的主要作用:

  1. 支持 @Controller 注解:可以使用 @Controller 注解标记控制器类,使其成为 Spring MVC 的处理器。
  2. 支持 @RequestMapping 注解:可以使用 @RequestMapping 注解来映射 URL 和处理方法,方便定义请求和处理之间的映射关系。
  3. 支持 @PathVariable 注解:可以在处理方法的参数中使用 @PathVariable 注解,将 URL 路径中的变量值作为参数传递。
@RequestMapping("/users/{id}")
public String getUser(@PathVariable("id") Long userId, Model model) {
    // todo
}
  1. 支持 @RequestParam 注解:可以在处理方法的参数中使用 @RequestParam 注解,获取请求参数的值。
@RequestMapping("/search")
public String searchUsers(@RequestParam("q") String query, Model model) {
    // todo
}
  1. 支持 @ResponseBody 注解:可以将方法的返回值直接作为 HTTP 响应的内容,而无需处理视图解析。
@RequestMapping("/api/users")
@ResponseBody
public List<User> getUsers() {
    // 返回 User 对象集合
    return userService.getUsers();
}
  1. 支持 @RequestBody 注解:可以将 HTTP 请求的内容转换为 Java 对象,方便处理请求体的数据。
@RequestMapping(value = "/api/user", method = RequestMethod.POST)
public ResponseEntity<?> createUser(@RequestBody User user) {
    // 处理用户创建逻辑
    return ResponseEntity.ok().build();
}

需要注意的是,@RequestBody 注解不能对 GET 请求使用,可以对 POST、PUT、PATCH、DELETE 请求使用。

  1. 支持 @ModelAttribute 注解:可以在请求处理方法中使用 @ModelAttribute 注解,将从请求中获取的数据绑定到模型对象上。
@ModelAttribute("user")
public User getUser() {
    return new User();
}

@RequestMapping(value = "/register", method = RequestMethod.POST)
public String registerUser(@ModelAttribute("user") User user, Model model) {
    // 处理用户注册逻辑
    model.addAttribute("message", "User registered successfully");
    return "registration-success";
}

注意:在请求处理方法中使用 @ModelAttribute 注解,将从请求中获取的数据绑定到模型对象上。

访问 WEB-INF 下的资源

资源放在 WEB-INF 目录下可以保证资源的安全性。**在这个目录下的动态资源不可以直接被访问,必须通过请求转发的方式进行访问。**这样可以避免通过地址栏直接对资源的访问。
注意:通过重定向是无法访问放在 WEB-INF 目录下的动态资源的。

拦截器

Spring 中的 Interceptor 拦截器,主要的作用是拦截指定的用户请求,并进行相应的预处理与后处理。
值得注意的是,拦截器可以在请求处理的不同阶段进行操作,包括前置处理、后置处理和完成后处理。通过配置拦截器链,可以将自定义的拦截器应用到特定的请求或请求路径上。

拦截器的执行时机:

  • preHandle():预处理,在请求被处理之前进行操作。
  • postHandle():后处理,在请求处理之后,但结果还没渲染前进行操作,可以改变响应结果。
  • afterCompletion:最终处理,所有的请求响应结束后执行善后工作,清理对象,关闭资源。

拦截器的应用场景

  • 日志记录:记录请求信息的日志。
  • 权限检查:如登录检查等。
  • 性能检测:检测方法的执行时间。

实现拦截器的两种方式

实现 HandlerInterceptor 接口
  1. 创建一个 Interceptor 包,在这个包下编写一个实现了 HandlerInterceptor 的类:
@Component  // 会注册一个 id 为 myInterceptor 的 Bean
public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // 在请求处理之前执行的操作
        return true; // 返回 true 表示继续处理请求,返回 false 表示停止处理请求
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        // 在请求处理之后、视图渲染之前执行的操作
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // 在请求完成后执行的操作,包括视图渲染完成后
    }
}

可以根据需要,在对应的方法中编写对应的处理代码

  1. 在 spring-mvc.xml 文件中添加 <mvc:interceptors> 来配置拦截器
<!-- 添加包扫描 -->
<!--<context:component-scan base-package="com.guyi.springmvc.controller/>-->
<context:component-scan base-package="com.guyi.springmvc.controller,com.guyi.springmvc.interceptor"/>
<!-- 使用这种方式会拦截所有请求 -->
<mvc:interceptors>
    <ref bean="myInterceptor"/>
</mvc:interceptors>

如果需要指定拦截的目标:

<!-- 使用这种方式会拦截所有请求 -->
<mvc:interceptors>
    <!-- 表示放行 /admin/hello.jsp -->
    <mvc:exclude-mapping path="/admin/hello.jsp"/>
    <ref bean="myInterceptor"/>
</mvc:interceptors>

该方法支持统配,比如:/admin/*

实现 HandlerInterceptorAdapter 类
  1. 在 Interceptor 包下创建一个类并继承 HandlerInterceptorAdapter 类
@Component
public class MyInterceptor extends HandlerInterceptorAdapter {
    // 实现需要的拦截器逻辑
}
  1. 根据需要,重写对应方法来实现对应功能,比如:
  • preHandle:在请求处理之前被调用,返回值决定是否继续处理请求。
  • postHandle:在请求处理之后被调用,但是在视图被渲染之前调用,可以对ModelAndView进行操作。
  • afterCompletion:在整个请求完成之后调用,可以进行一些资源清理工作。
  1. 在 spring-mvc.mxl 中配置拦截器
<mvc:interceptors>
    <mvc:interceptor>
        <!-- 拦截的路径 -->
        <mvc:mapping path="/**"/>
        <ref bean="myInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

异常处理器

局部异常处理器

编写一个自定义异常处理器: ExceptionHandlerTestController.java

@Controller
@RequestMapping("/exception")
public class ExceptionHandlerTestController {

    /**
     * 模拟出现空指针异常的情况
     */
    @RequestMapping("/null_pointer")
    public String nullPointer() {
        String s = null;
        s.equals("");
        return "hello";
    }

    /**
     * 局部异常处理器
     */
    @ExceptionHandler({ArithmeticException.class, NullPointerException.class})
    public String localException(HttpServletRequest request, Exception e) {
        System.out.println("异常对象 = " + e);
        request.setAttribute("error", e);
        return "exception";
    }
}

webapp/admin 下添加一个发生异常时跳转的页面:exception.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>异常</title>
</head>
<body>
请求处理出现了异常
</body>
</html>

启动项目访问:http://localhost:8080/springmvc/exception/null_pointer,浏览器会显示如下内容:
image.png
IDE 控制台打印:
image.png

全局异常处理器

新建一个 exception 包,在这个包下新建一个全局异常处理器类。
全局异常处理器的优先级低于局部异常处理器。

/**
 * 全局异常处理器
 */
@ControllerAdvice  //  使用 @ControllerAdvice 用来标识全局异常处理器
public class GlobalExceptionHandler {
    @ExceptionHandler({NumberFormatException.class, ClassCastException.class})
    public String globalException(Exception e, HttpServletRequest request) {
        System.out.println("异常对象 = " + e);
        request.setAttribute("error ", e);
        return "exception";
    }
}

在 ExceptionHandlerTestController.Java 中添加一个方法

/**
 * 模拟出现数据格式化异常
 */
@RequestMapping(value = "/number_format")
public String numberFormat() {
    int number = Integer.parseInt("hello");
    return "hello";
}

在 spring-mvc.xml 中将 exception 包加入被扫描的包
然后重启项目,浏览器访问 http://localhost:8080/springmvc/exception/number_format,跳转页面和局部异常处理器一致, IDE 控制台打印内容如下:
image.png

对于全局异常处理器,还可以使用 @RestControllerAdvice 注解定义。
@RestControllerAdvice@ControllerAdvice 是 Spring 框架中用于全局异常处理和全局数据处理的注解。它们的区别主要在于处理的目标和返回值类型。
@ControllerAdvice 主要用于处理控制器层(Controller)的异常和数据。它可以定义全局异常处理器(Global Exception Handler),通过 @ExceptionHandler 注解来处理指定异常类型的方法,并返回自定义的错误页面或错误信息。另外,@ControllerAdvice 还可以使用 @ModelAttribute 注解来定义全局数据,在所有被控制器绑定的请求方法中都可以访问这些数据。
@RestControllerAdvice 是 Spring 4.3 版本引入的注解,@ControllerAdvice 的基础上增加了@ResponseBody 的功能(源码中 @RestControllerAdvice@ResponseBody 注解标注)它主要用于处理控制器层的异常,并以 JSON、XML 等形式返回处理结果。与 @ControllerAdvice 相比,@RestControllerAdvice 的异常处理方法和全局数据方法都会自动加上 @ResponseBody 注解,不需要再通过 @ResponseBody 注解单独指定返回类型。
简而言之,@RestControllerAdvice 主要是用于处理控制器层的异常,并返回 JSON 等形式的结果;而 @ControllerAdvice 用于处理控制器层的异常和数据,返回可定制的错误页面或错误信息。

Spring MVC 常用注解汇总

映射 URL

注解作用
@RequestMapping用来标记类和方法,用于映射URL路径到控制器的处理方法。可以用在类级别和方法级别。
@PostMapping作用和 @RequestMapping 一样,但是只能处理 POST 请求
@GetMapping作用和 @RequestMapping 一样,但是只能处理 GET 请求
@PutMapping作用和 @RequestMapping 一样,但是只能处理 PUT 请求
@PatchMapping作用和 @RequestMapping 一样,但是只能处理 PATCH请求
@DeleteMapping作用和 @RequestMapping 一样,但是只能处理 DELETE 请求

接收请求参数

注解作用
@PathVariable用于标记形参,获取 URL 中的参数
@RequestParam用于标记形参,获取 URL 中的参数
@RequestBody用于标记形参,将请求体的主体内容反序列化为 Java 对象

响应请求

注解作用
@RequestBody标记类和方法,用于将方法返回值序列化为响应的主体内容,不跳转到视图解析器
@ModelAttribute用于标记方法和参数,将方法参数或方法返回值绑定到向 Web 视图公开的命名模型属性
@SessionAttributes标记类,用于将模型对象中的属性暂存到会话中

其他

注解作用
@InitBinder用于标记方法,完成配置数据绑定器,比如日期格式化
@Validated用于标记方法,开启方法参数的校验功能,并触发校验框架对参数进行校验。
@ControllerAdvice用于处理控制器层的异常和数据,返回可定制的错误页面或错误信息。
@RestControllerAdvice处理控制器层的异常,并返回JSON等形式的结果
@ExceptionHandler用于标记方法,处理控制器内部异常,返回自定义的错误视图或错误响应
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值