超全SpringMVC知识点!!(万字总结)

前言

SpringMVC是一个Spring基于MVC模式开发的一个框架,其底层封装了Servlet,用于简化开发,而MVC是一种思想。虽然现在SpringBoot用的比较多,但是SpringMVC面试也会问到,而且技术层面也有必要整体流程有个了解。

1、MVC是什么

模型(model)-视图(view)-控制器(Controller) ,就是MVC,它通过分离模型、视图、控制器在应用程序中的角色将业务逻辑从界面中解耦。Controller负责接收用户的请求,并调用后台服务(Service和Dao)来处理业务逻辑。View用来将结果显示给用户,不包含任何逻辑(现在前后端分离之后,View层很少用了)。

在这里插入图片描述

2、SpringMVC是什么

SpringMVC是Spring家族的web成员,他是基于java的实现了Web MVC思想的请求驱动类型的轻量级web框架。

SpringMVC是服务到工作者思想的实现,前端控制器是DispatcherServlet; 应用控制器拆为处理器映射器(HandlerMapping)进行处理管理和视图解析器(View Resolver)进行视图管理; 支持本地化/国际化(Locale) 解析及文件上传等; 提供灵活的数据验证、格式化和数据绑定机制; 提供了约定大于配置的的编程支持。

SpringMVC优点:

  1. 代码分层更整洁好看一些
  2. 天生与Spring框架集成(Ioc、Aop等)
  3. 可以进行简单单元测试

3、SpringMVC请求流程 && 环境搭建

3.1 SpringMVC请求流程

在这里插入图片描述

3.2 搭建环境

3.2.1开发环境

idea2022 + Maven3.8.1 + jdk1.8 + jetty

3.2.2 环境配置步骤

在这里插入图片描述
配置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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.xxxx</groupId>
  <artifactId>Springmvc01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>Springmvc01</name>
  <packaging>war</packaging>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.source>1.8</maven.compiler.source>
      <junit.version>5.8.2</junit.version>
  </properties>

  <dependencies>
    <!-- 测试相关依赖 -->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>

    <!-- spring web -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <!-- spring mvc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <!-- web servlet -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>springmvc01</finalName>
    <plugins>
      <!-- 编译环境插件 -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
      <!--  -->
      <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId> <!-- 注意:原图文本可能有拼写错误(jetty-maven-plusin → jetty-maven-plugin) -->
        <version>9.4.27.v20200227</version>
        <configuration>
          <scanIntervalSeconds>10</scanIntervalSeconds> <!-- 扫描间隔(秒) -->
          <httpConnector>
            <port>8080</port> <!-- 端口 -->
          </httpConnector>
          <webAppConfig>
            <contextPath>/springmvc01</contextPath> <!-- 项目路径(URL路径) -->
          </webAppConfig>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

注意点:
根访问路径在pom.xml文件中定义

	<webAppConfig>
      <contextPath>/springmvc01</contextPath> <!-- 项目路径(URL路径) -->
    </webAppConfig>

配置web.xml:


<?xml version="1.0" encoding="UTF-8"?>
<web-app id="webApp_ID" version="3.0"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <!-- 字符编码过滤器 -->
    <filter>
        <description>char encoding filter</description>
        <filter-name>encodingFilter</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>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- servlet请求分发器 -->
    <servlet>
        <servlet-name>springMvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:servlet-context.xml</param-value>
        </init-param>
        <!-- 表示启动时初始化该Servlet -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springMvc</servlet-name>
        <!-- 拦截规则:
             "/" 代表拦截所有请求,
             "*.do" 拦截所有.do请求 -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

配置servlet-context.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:mvc="http://www.springframework.org/schema/mvc"
       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/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.xxxx.springmvc.controller"/>

    <!-- 使用默认 Servlet 响应静态文件 -->
    <mvc:default-servlet-handler/>

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

    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!-- 前缀:在 WEB-INF 目录下的 jsp 目录下 -->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!-- 后缀:以 .jsp 结尾的资源 -->
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

项目结构如下:

在这里插入图片描述
注意点:
①、项目结构必须严格按照这个来,jsp文件必须放WEB-INF/jsp目录下面,web.xml必须放WEB-INF下。
②、servlet-context.xml文件中 <context:component-scan base-package> 配置的包前缀必须和java目录下包名前缀相同。

jsp页面(不用在意细节,jsp只用于演示不细学):


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<!DOCTYPE html>
<html>
<head>
    <base href="<%=basePath %>">
    <title>My JSP 'hello.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">
    <meta http-equiv="keywords" content="keyword1, keyword2, keyword3">
    <meta http-equiv="description" content="This is my page">
</head>
<body>
<!-- EL 表达式接收参数值 -->
${hello}
</body>
</html>

演示代码:


package com.xxxx.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloController {

    /**
     * 请求地址的映射
     *
     * @return
     */
    @RequestMapping("/hello")
    public ModelAndView hello() {
        ModelAndView modelAndView = new ModelAndView();
        // 设置数据
        modelAndView.addObject("hello", "Hello SpringMvc");
        // 设置视图名称
        modelAndView.setViewName("hello");
        return modelAndView;
    }

}

最终效果:
在这里插入图片描述

至此,我们成功跑通了Springmvc的环境。

4. url地址映射 && 参数绑定

4.1 url地址映射之@RequestMapping

@RequestMapping方法可以将请求路径和方法绑定,可以声名在类和方法级别。

①、映射单个url

示例代码:

@Controller
public class UrlController {

    /**
     * 声名在方法上,映射单个url
     * @return
     */
    @RequestMapping("/test01")
    public ModelAndView test01() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("hello", "test01");
        modelAndView.setViewName("hello");
        return modelAndView;
    }

}

运行结果:
在这里插入图片描述

②、映射多个url

@RequestMapping({"/test02", "/test03"})
    public ModelAndView test02_3() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("hello", "test02_3");
        modelAndView.setViewName("hello");
        return modelAndView;
    }

运行结果:

在这里插入图片描述
可以看到都可以访问到。

③、映射url到控制器

其实就是相当于在Controller类上加了一层映射。

@Controller
@RequestMapping("/url")
public class HelloController {

    /**
     * 请求地址的映射
     *
     * @return
     */
    @RequestMapping("/hello")
    public ModelAndView hello() {
        ModelAndView modelAndView = new ModelAndView();
        // 设置数据
        modelAndView.addObject("hello", "Hello SpringMvc");
        // 设置视图名称
        modelAndView.setViewName("hello");
        return modelAndView;
    }
}

访问路径多了一个/url。
在这里插入图片描述

④、通过参数名称映射url

@RequestMapping(params = "test04")
    public ModelAndView test04() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("hello", "test04");
        modelAndView.setViewName("hello");
        return modelAndView;
    }

运行结果:
访问路径改为 ? 后面跟参数
在这里插入图片描述

⑤、设置请求方式

由于浏览器默认使用GET方式请求,因此改成POST就无法访问到了。

@RequestMapping(value = "test05", method = RequestMethod.POST)
    public ModelAndView test05() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("hello", "test05");
        modelAndView.setViewName("hello");
        return modelAndView;
    }

运行结果:
在这里插入图片描述

4.2 参数绑定

①、基本类型

@Controller
public class ParamsController {
   @RequestMapping("/data01")
   public void data01(int age, double money) {
       System.out.println("age: " + age + " money: " + money);
   }
   @RequestMapping("/data02")
   public void data02(@RequestParam(defaultValue = "18") int age, @RequestParam(defaultValue = "20.0") double money) {
       System.out.println("age: " + age + " money: " + money);
   }
   @RequestMapping("/data03")
   public void data03(
           @RequestParam(defaultValue = "18", name = "userAge") int age,
           @RequestParam(defaultValue = "20.0", name = "userMoney") double money) {
       System.out.println("age: " + age + " money: " + money);
   }
}

访问/data01的url

http://localhost:8080/springmvc01/data01?age=17&money=18.0

打印结果:

age: 17 money: 18.0

但是如果我们传参的时候没有传完整,就会报空指针null的错误
url:

http://localhost:8080/springmvc01/data01?age=17

在这里插入图片描述
因此,/data02模块的@RequestParam注解提供的默认值就可以解决这个问题。

访问/data02的url

http://localhost:8080/springmvc01/data02

打印结果:

age: 18 money: 20.0

@RequestParam可以设置别名,如果设置了别名,传参时名字必须与别名相同
访问/data03的url:

http://localhost:8080/springmvc01/data03?userAge=60&userMoney=100.0

打印结果:

age: 60 money: 100.0

②、包装类型 && 字符串类型

	@RequestMapping("/data04")
    public void data04(Integer age, Double money) {
        System.out.println("age: " + age + " money: " + money);
    }
    @RequestMapping("/data05")
    public void data05(String userName, String userPwd) {
        System.out.println("userName: " + userName + " userPwd: " + userPwd);
    }

包装类型和字符串类型其它的访问方式和参数赋值的方法和上述基本类型都基本相同,包括@RequestParam等注解的使用,但是不同的一点是,它们都有默认值为null。

访问data04和data04的url:

http://localhost:8080/springmvc01/data04
http://localhost:8080/springmvc01/data05

运行结果:

age: null money: null
userName: null userPwd: null

③、数组类型 && javaBean类型

@RequestMapping("/data06")
    public void data06(String[] ids) {
        for (String id : ids) {
            System.out.println("--- " + id);
        }
    }

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

User类:

public class User {
    private String id;
    private String userName;
    private String userPwd;
    // 。。。。。getter、setter、toString等方法
}

访问data06:

http://localhost:8080/springmvc01/data06?ids=1&ids=2&ids=3

运行结果:

--- 1
--- 2
--- 3

访问data07:

http://localhost:8080/springmvc01/data07?id=1&userName=admin&userPwd=123456

运行结果:

User{id='1', userName='admin', userPwd='123456'}

传参时候注意与实体类的字段一一对应即可。

④、集合类型

以List类型为例子,Set、Map使用较少,和List使用相似.
重新定义两个类来举例子:

public class Phone {
    
    private String num;
    // getter、setter、toString等方法
}

public class User {
        private String id;
        private String userName;
        private String userPwd;
        List<Integer> ids;
        List<Phone> phones;
         // getter、setter、toString等方法
}

webapp目录下添加一个test.jsp文件:

<%--
  Created by IntelliJ IDEA.
  User: suxuchao
  Date: 2025/4/28
  Time: 17:06
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试集合类型数据</title>
</head>
<body>
    <form action="data08" method="post">
      <input name="ids[0]" value="123456"><br>
      <input name="ids[1]" value="4567"><br>
      <input name="phones[0].num" value="123321"><br>
      <input name="phones[1].num" value="789987"><br>
      <button type="submit">提交</button>
    </form>
</body>
</html>

测试后台代码:

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

访问界面:
在这里插入图片描述
打印结果:

User{id='null', userName='null', userPwd='null', ids=[123456, 4567], phones=[phone{num='123321'}, phone{num='789987'}]}

谨记集合类型是封装到java Bean中进行访问的。

5. SpringMVC请求转发与重定向

5.1 请求域对象设置API

@RequestMapping("/model01")
    public ModelAndView model01() {
        ModelAndView modelAndView = new ModelAndView();
        // 设置数据模型(请求域对象)
        modelAndView.addObject("hello", "Hello Model01");
        // 设置视图
        modelAndView.setViewName("hello");
        return modelAndView;
    }

    @RequestMapping("/model02")
    public String model02(ModelMap modelMap) {
        modelMap.addAttribute("hello", "Hello Model02");
        return "hello";
    }

    @RequestMapping("/model03")
    public String model03(Model model) {
        model.addAttribute("hello", "Hello Model02");
        return "hello";
    }

    @RequestMapping("/model04")
    public String model04(Map map) {
        map.put("hello", "Hello Model02");
        return "hello";
    }

    @RequestMapping("/model05")
    public String model05(HttpServletRequest servletRequest) {
        servletRequest.setAttribute("hello", "Hello Model02");
        return "hello";
    }

访问结果:
在这里插入图片描述
最后结果都是成功的,以model02举例。

方法域对象类型作用域数据存储位置使用方式是否需要手动创建数据可见范围典型场景
model01ModelAndView请求域(隐式)请求域通过 addObject() 添加数据,需显式设置视图是(需手动实例化)仅当前请求及转发链有效需同时传递数据和指定视图
model02ModelMap请求域请求域通过 addAttribute() 添加数据,自动绑定否(Spring 自动注入)仅当前请求及转发链有效简化数据传递,无需处理视图逻辑
model03Model请求域请求域通过 addAttribute() 添加数据,自动绑定否(Spring 自动注入)仅当前请求及转发链有效轻量级数据传递,接口方法更简洁
model04Map请求域请求域直接操作 Map,Spring 自动转换否(Spring 自动注入)仅当前请求及转发链有效快速存取键值对,无需接口约束
model05HttpServletRequest请求域请求域通过 setAttribute() 直接操作是(需注入或获取)仅当前请求及转发链有效需直接使用 Servlet API 的场景

前四种方式底层还是调用的HttpServletRequest方法,只不过被框架封装了。

5.2 请求重定向

重定向是发一个302状态码给服务器,浏览器自己去请求跳转的页面,地址栏发生改变。

重定向以redirect:开头:

@Controller
public class ViewController {
    @RequestMapping("/view01")
    public String view01() {
        return "redirect:view.jsp";
    }
    @RequestMapping("/view02")
    public String view02() {
        return "redirect:view.jsp?uName=admin&uPwd=123456";
    }
    @RequestMapping("/view03")
    public String view03() {
        return "redirect:view.jsp?uName=张三&uPwd=我去";
    }
    @RequestMapping("/view04")
    public String view04(RedirectAttributes attributes) {
        //设置参数
        attributes.addAttribute("uName", "张三");
        attributes.addAttribute("uPwd", "我去");
        return "redirect:view.jsp";
    }
    @RequestMapping("/view05")
    public ModelAndView view05(ModelAndView modelAndView) {
        //设置模型数据
        modelAndView.addObject("uName", "张三");
        modelAndView.addObject("uPwd", "123456");
        //设置视图
        modelAndView.setViewName("redirect:view.jsp");
        return modelAndView;
    }
    @RequestMapping("/view06")
    public ModelAndView view06(ModelAndView modelAndView) {
        //设置模型数据
        modelAndView.addObject("uName", "张三");
        modelAndView.addObject("uPwd", "123456");
        //设置视图
        modelAndView.setViewName("redirect:test");
        return modelAndView;
    }
}
  1. 请求view01:
http://localhost:8080/springmvc01/view01

测试结果:
在这里插入图片描述
可以看到,地址栏的url发生了变化,同时也成功跳转到了页面。

  1. 请求view02:
http://localhost:8080/springmvc01/view02

运行结果:
在这里插入图片描述
3. 请求view03:

http://localhost:8080/springmvc01/view03

运行结果(中文传递乱码):
在这里插入图片描述
4. 请求view04

http://localhost:8080/springmvc01/view04

运行结果(通过RedirectAttributes传递中文参数):
在这里插入图片描述
5. 请求view05

http://localhost:8080/springmvc01/view05

运行结果(通过ModelAndView传递中文参数):
在这里插入图片描述
6. 请求view06

http://localhost:8080/springmvc01/view06

最后会映射到:
http://localhost:8080/springmvc01/test路径,这说明不仅可以映射到视图文件,也可以重定向到其它控制器。

补充:
之所以要引入RedirectAttributes和ModelAndView等对象来存储重定向后的数据,是因为重定向之后,新的url无法共享之前旧请求域的数据,因此需要通过这些类来实现数据的传递。

5.3 请求转发

请求转发,直接调用跳转的页面,让它返回。对于浏览器来说,它无法感受到服务器有没有forward,地址栏不发生改变。可以获取请求域中的数据。

添加/text接口

@Controller
public class TestController {

    @RequestMapping("/test")
    public void test(String uName, String uPwd) {
        System.out.println("uName: " + uName + " uPwd: " + uPwd);
    }
}

测试用例:

@RequestMapping("/view07")
   public String view07() {
       return "forward:view.jsp";
   }
   @RequestMapping("/view08")
   public String view08() {
       return "forward:view.jsp?uName=张三&uPwd=123456";
   }

   @RequestMapping("/view09")
   public String view09(Model model) {
       model.addAttribute("name", "李四");
       return "forward:view.jsp?uName=张三&uPwd=123456";
   }
   @RequestMapping("/view10")
   public String view10(Model model) {
       model.addAttribute("name", "管理员");
       return "/../../view";
   }
   @RequestMapping("/view11")
   public ModelAndView view11(ModelAndView modelAndView) {
       modelAndView.setViewName("forward:test");
       return modelAndView;
   }
  1. 访问view07:
http://localhost:8080/springmvc01/view07

运行结果:
在这里插入图片描述
2. 访问view08:

http://localhost:8080/springmvc01/view08

运行结果:
在这里插入图片描述

  1. 访问view09:
http://localhost:8080/springmvc01/view09

运行结果:
在这里插入图片描述
4. 访问view10:

http://localhost:8080/springmvc01/view10

运行结果:
在这里插入图片描述
5. 访问view11:

http://localhost:8080/springmvc01/view11

打印运行结果:

uName: null uPwd: null

6. JSON请求

JSON是企业开发中的通用接口参数类型,在客户端解析很方便。SpringMVC对于json提供了很好的支持。

6.1配置JSON环境

pom.xml:
添加一下依赖:

<!-- 添加 Jackson JSON 依赖 -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.10.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.10.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.10.0</version>
    </dependency>

修改servlet-context.xml文件:

<!-- 开启注解驱动 -->
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter" />
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
        </mvc:message-converters>
    </mvc:annotation-driven>
  • StringHttpMessageConverter​​:处理纯文本数据,适合简单 API。
  • ​MappingJackson2HttpMessageConverter​​:处理 JSON 数据,是 RESTful API 的核心组件。

6.2 @ResponseBody注解

该注解的含义是,表明返回的数据是JSON格式的。
该注解既可以作用于方法上,也可以作用与返回值上:

@Controller
@RequestMapping("/user")
public class userController {
    @RequestMapping("/query01")
    @ResponseBody
    public User queryUser01() {
        User user = new User();
        user.setId(1);
        user.setUserName("zhangsan");
        user.setUserPwd("123456");
        // 返回User对象
        return user;
    }
    @RequestMapping("/query02")
    public @ResponseBody User queryUser02() {
        User user = new User();
        user.setId(2);
        user.setUserName("lisi");
        user.setUserPwd("567765");
        // 返回User对象
        return user;
    }
    @RequestMapping("/query03")
    @ResponseBody
    public List<User> queryUser03() {
        User user = new User();
        user.setId(1);
        user.setUserName("zhangsan");
        user.setUserPwd("123456");
        User user2 = new User();
        user.setId(2);
        user.setUserName("lisi");
        user.setUserPwd("567765");
        List<User> list = new ArrayList<>();
        list.add(user); list.add(user2);
        // 返回User对象
        return list;
    }
}

访问query01:
在这里插入图片描述
访问query02:
在这里插入图片描述
访问query03:

在这里插入图片描述

6.3 @RequestBody注解

@RequestBody注解常用来处理content-type是application/json或者application/xml等格式的内容。一般来说是处理application/json 用的比较多。@RequestBody接受的是一个字符串,一定是一个字符串。

通过@RequestBody可以将请求体中的JSON字符串绑定到相应的bean上,当然也可绑定到对应的字符串上。

@RequestMapping("/query05")
    @ResponseBody
    public User query05(@RequestBody User user) {
        System.out.println(user);
        return user;
    }

需要注意的是,添加该注解之后,发送请求的时候也需要是JSON格式,否则会报错。
报错:
在这里插入图片描述
通过ApiPost进行测试成功结果:
在这里插入图片描述

7. 拦截器

SpringMVC中的Intercepter拦截器主要用于拦截用户请求并进行相应的处理,比如通过它来进行权限验证,或判断用户是否登录等操作。实现方式有两种

实现接口: org.springframework.web.servlet.HandlerInterceptor

继承适配器: org.springframework.web.servlet.handler.HandlerInterceptorAdapter

测试接口:

@Controller
public class HelloController {

    /**
     * 请求地址的映射
     *
     * @return
     */
    @RequestMapping("/hello")
    public ModelAndView hello() {
        System.out.println("拦截的方法....");
        ModelAndView modelAndView = new ModelAndView();
        // 设置数据
        modelAndView.addObject("hello", "Hello SpringMvc");
        // 设置视图名称
        modelAndView.setViewName("hello");
        return modelAndView;
    }

}

7.1 实现HandlerInterceptor接口

public class MyInterceptor01 implements HandlerInterceptor {
    /**
     * 在目标方法(handler)执行前执行的方法
     *      返回true: 执行handler方法
     *      返回false: 组织目标handler执行
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("handler方法执行前打印结果....");
        return true;
    }
    /**
     * 在目标方法(handler)执行后,视图生成前执行的方法
     *      返回true: 执行handler方法
     *      返回false: 组织目标handler执行
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("handler方法执行后,视图生成前打印结果....");
    }
    /**
     * 在目标方法(handler)执行后,视图生成后执行的方法
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("视图生成后打印结果....");
    }
}

配置映射文件(servlet-context.xml):

<!-- 配置拦截器 -->
    <!-- 1、拦截所有资源 -->
    <mvc:interceptors>
        <!--
            使用一个bean标签定义一个interceptor
            直接定义在mvc:interceptors标签中,表示拦截器会拦截所有请求
         -->
        <bean class="com.xxxx.springmvc.interceptor.MyInterceptor01"/>
    </mvc:interceptors>
    <!-- 2、具体配置路径 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 通过 mvc:mapping 配置需要被拦截的资源,支持通配符,可配置多个 -->
            <mvc:mapping path="/**"/>
            <!-- 配置不需要拦截的资源,表示/url路径下的所有资源不需要拦截 -->
            <mvc:exclude-mapping path="/url/*"/>
            <bean class="com.xxxx.springmvc.interceptor.MyInterceptor01"/>
        </mvc:interceptor>
    </mvc:interceptors>

上述有两种拦截方式,其中第一种就是全局拦截,后面是可以配置不需要拦截的,还是很方便的。
运行结果:
访问:

http://localhost:8080/springmvc01/hello

打印结果:

handler方法执行前打印结果....
拦截的方法....
handler方法执行后,视图生成前打印结果....
视图生成后打印结果....

7.2 继承HandlerInterceptorAdapter类

HandlerInterceptorAdapter类本质上还是实现了AsyncHandlerInterceptor接口,而AsyncHandlerInterceptor接口继承了HandlerInterceptor接口。

public class MyInterceptor02 extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("handler方法执行前打印结果....");
        return true;
    }
}

配置xml文件:

<mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.xxxx.springmvc.interceptor.MyInterceptor02"/>
        </mvc:interceptor>
    </mvc:interceptors>

打印结果:

handler方法执行前打印结果....
拦截的方法....

7.3 拦截器链

简单来说,就是让多个拦截器都生效,这些拦截器就是拦截器链。
优先级则是先配置的拦截器先执行:
servlet-context.xml配置:

<mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.xxxx.springmvc.interceptor.MyInterceptor01"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.xxxx.springmvc.interceptor.MyInterceptor02"/>
        </mvc:interceptor>
    </mvc:interceptors>

打印运行结果:

MyInterceptor01类的handler方法执行前打印结果....
MyInterceptor02类的handler方法执行前打印结果....
拦截的方法....
MyInterceptor01类的handler方法执行后,视图生成前打印结果....
MyInterceptor01类的视图生成后打印结果....

8. 文件上传

8.1 环境配置

pom.xml:

<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.2</version>
</dependency>

servlet-context.xml:

	<!-- 文件上传 -->
    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 允许文件上传的最大尺寸 -->
        <property name="maxUploadSize">
            <value>104857600</value>
        </property>
        <!--
            设置文件进入临时文件夹的最大大小限制
            该值为阈值,高于此值,保存在内存中,否则生成硬盘上的临时文件
        -->
        <property name="maxInMemorySize">
            <value>4096</value>
        </property>
    </bean>

添加前端upload.jsp文件:

<%--
  Created by IntelliJ IDEA.
  User: suxuchao
  Date: 2025/4/30
  Time: 15:30
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上传</title>
</head>
<body>
    <form method="post" action="uploadFile" enctype="multipart/form-data">
        <input type="file" name="file">
        <button>上传</button>
    </form>
</body>
</html>

8.2 单文件上传

添加result.jsp用于呈现结果:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>result界面</title>
</head>
<body>
<h3>${msg}</h3>
</body>
</html>

文件上传代码实现:

@Controller
public class fileController {
    /**
     * 单文件上传
     * @return
     */
    @RequestMapping("/uploadFile")
    public String uploadFile(HttpServletRequest request, @RequestParam("file") MultipartFile file) {

        if(!file.isEmpty()) {
            // 获取项目所在的路径(绝对路径 )
            String path = request.getServletContext().getRealPath("/");
            // 设置上传文件存放的目录
            File uploadFile = new File(path + "/upload");
            //判断文件目录是否存在,不存在就先创建
            if(!uploadFile.exists()) {
                // 新建目录
                uploadFile.mkdir();
            }

            try {
                //获取上传文件的文件名
                String originalFilename = file.getOriginalFilename();
                //获取文件后缀名
                String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
                // 通过系统当前的毫秒数,生成随机文件名
                String fileName = System.currentTimeMillis() + suffix;
                // 上传文件(转存文件到指定目录)
                file.transferTo(new File(uploadFile.getPath(), fileName));

                // 如果上传成功,设置作用域
                request.setAttribute("msg", "文件上传成功!");

            } catch (IOException e) {
                request.setAttribute("msg", "文件上传失败");
                throw new RuntimeException(e);
            }
        } else {
            // 如果上传文件不存在
            request.setAttribute("msg", "上传文件不存在");
        }

        return "result";
    }
}

前端界面:
在这里插入图片描述

运行结果:
在这里插入图片描述

8.3 多文件上传

与多文件上传类似,但是需要改动一些小的点。

upload.jsp改动:

	<form method="post" action="uploadFiles" enctype="multipart/form-data">
        <input type="file" name="files">
        <input type="file" name="files">
        <button>上传</button>
    </form>

后台代码:

	@RequestMapping("/uploadFiles")
    public String uploadFiles(HttpServletRequest request, @RequestParam("files")List<MultipartFile> files)  {
        if(!files.isEmpty() && files.size() > 0) {
            for (MultipartFile file : files) {
                // 上传文件
                // 获取项目所在的路径(绝对路径 )
                String path = request.getServletContext().getRealPath("/");
                // 设置上传文件存放的目录
                File uploadFile = new File(path + "/upload");
                //判断文件目录是否存在,不存在就先创建
                if(!uploadFile.exists()) {
                    // 新建目录
                    uploadFile.mkdir();
                }
                try {
                    //获取上传文件的文件名
                    String originalFilename = file.getOriginalFilename();
                    //获取文件后缀名
                    String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
                    // 通过系统当前的毫秒数,生成随机文件名
                    String fileName = System.currentTimeMillis() + suffix;
                    // 上传文件(转存文件到指定目录)
                    file.transferTo(new File(uploadFile.getPath(), fileName));
                    // 如果上传成功,设置作用域
                    request.setAttribute("msg", "文件上传成功!");
                } catch (IOException e) {
                    request.setAttribute("msg", "文件上传失败");
                    throw new RuntimeException(e);
                }
            }
        }
        return "result";
    }

9. 全局异常

在JavaEE项目开发过程中,会遇到很多各种各样的bug,每个过程单独处理,系统的代码耦合度会太高。因此,SPringleMVC对于异常处理这里提供了支持,通过其提供的全局异常处理机制将异常信息从处理过程抽离出来,既保证了相关处理过程的单一,也实现了异常信息的统一和维护。

  1. 使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver
  2. 实现Spring的异常处理接口 HandlerExceptionResolver定义自己的异常处理器
  3. 使用@ExceptionHandler注解

9.1 SimpleMappingExceptionResolver处理

首先在/hello触发器上添加一个简单的错误:

System.out.println(1/0);

访问url:

http://localhost:8080/springmvc01/hello

未配置全局异常处理之前:
在这里插入图片描述
配置servlet-context.xml之后:

    <!-- 全局异常处理配置 -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!-- 页面跳转时出现异常,设置默认错误页面 -->
        <property name="defaultErrorView" value="error"/>
        <!-- 异常发生时,设置异常的变量名 -->
        <property name="exceptionAttribute" value="ex"/>
    </bean>

访问结果:
在这里插入图片描述

成功跳转到了错误处理页面 error.jsp。
这个也可以捕获自己的自定义异常,想将不同的异常映射到不同的页面也可以在xml进行配置。
servlet-context.xml 文件配置:

<!-- 设置自定义义异常映射和页面映射 -->
        <property name="exceptionMappings">
            <props>
                <!-- key代表的是自定义异常常用的路径;括案中设置的是具体的页面 -->
                <prop key="com.xxxx.springmvc.exception.ParamsException">params_error</prop>
                <prop key="com.xxxx.springmvc.exception.BusinessException">business_error</prop>
            </props>
        </property>

9.2 实现接口 HandlerExceptionResolver定义自己的异常处理器

@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        ModelAndView mv = new ModelAndView("error");
        mv.addObject("ex", "默认的错误信息");

        return mv;
    }
}

这样实现接口之后就可以生效了。
访问/hello接口触发器,成功跳转:

在这里插入图片描述

9.3 @ExceptionHandler注解处理

页面处理程序继承 BaseController

public class BaseController {

    @ExceptionHandler
    public String exc(HttpServletRequest request, HttpServletResponse response, Exception ex){
        request.setAttribute("ex", ex);

        if(ex instanceof ParamsException){
            return "error_param";
        }

        if(ex instanceof BusinessException){
            return "error_business";
        }

        return "error";
    }
}

使用 @ExceptionHandler 注解须知异常处理,具有继承特性。有扩展性好(只需要要将要异常处理的 Controller 类继承于 BaseController 即可),不需要附加 Spring 配置等优点,但该方法还已有有个码存储入缓存(需要修改已有个码,使相关类继承于 BaseController),在异常处理时不建议取顶级条件外的数括。

9.4 未捕获异常的处理

对于 Unchecked Exception 而言,由于不强制使用捕获技术,往往在接爱隐藏,如果运行期产生 Unchecked Exception,屏幕中又没有进行相应的捕获和处理,则我们可能看不到页面上的 404,500……等服务端内部错误提示页面。

此需要一个全局的异常处理机制。目前大多数据服务端都支持在 web.xml 中通过(Websphere/Weblogic)或者 (Tomcat)中点配置将定义异常映射显示页面。修改 web.xml 文件,增加以下内容:

<!-- 全局页面定义 -->
<error-page>
    <exception-type>java.lang.Throwable</exception-type>
    <location>/500.jsp</location>
</error-page>
<error-page>
    <error-code>500</error-code>
    <location>/500.jsp</location>
</error-page>
<error-page>
    <error-code>404</error-code>
    <location>/404.jsp</location>
</error-page>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值