当前位置: 首页 > news >正文

《学会 SpringMVC 系列 · 基础篇》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

    • 写在前面的话
    • 搭建 SpringMVC
      • Step1、创建 Maven 项目
      • Step2、引入 Maven 依赖
      • Step3、配置 web.xml
      • Step4、创建 spring-mvc.xml
      • Step5、编写控制器类
      • Step6、部署 Tomcat
    • 搭建 SpringMVC 复盘
      • JSON 类型数据传递
      • 关于静态资源处理
      • 对比 SpringBoot
    • 知识补充
    • 总结陈词

CSDN.gif

写在前面的话

博主所在公司早期后端采用 SSM(Spring + SpringMVC + MyBatis),2020年进行了微服务拆分,顺带技术栈升级,后端调整为 SpringCloud 和 SpringBoot,深刻体会到了SpringBoot为我们带来的遍历。
经常会听到新人之间在议论,“还是新框架好,旧框架已过时了,不要浪费时间学习旧框架”,那么 SpringBoot 是否完全取代了 SSM 技术栈呢?

那么,有了 SpringBoot,是否还需要学习 SpringMVC?
毋庸置疑,SpringBoot 的出现确实简化了 SpringMVC 的配置,但 SpringMVC 作为 Spring 生态中重要的组成部分,其底层原理和核心概念依然值得深入学习。

  • 更深入理解:SpringBoot 虽然简化了配置,但它本质上还是基于 SpringMVC 的。深入理解 SpringMVC 可以让你更好地掌握 SpringBoot 的底层原理,从而更灵活地解决问题。
  • 定制化开发:SpringBoot 提供了大量的自动配置,但有时我们可能需要进行高度定制化的开发。这时,对 SpringMVC 的底层原理的掌握就显得尤为重要。
  • 面试:很多公司的面试都会考察 SpringMVC 的基础知识,深入了解 SpringMVC 可以帮助你更好地应对面试。
  • 旧项目维护:如果需要维护一些基于 SpringMVC 的老旧项目,了解 SpringMVC 的知识也是必不可少的。

总结一下:
SpringBoot 和 SpringMVC 并不是对立的关系,而是相辅相成的。初学者建议优先学习 SpringBoot,可以让你快速上手,提高开发效率。接着深入学习 SpringMVC 的底层原理,更深入得使用其定制化能力,从而写出更高质量的代码。
接下来的本系列文章,将从 SpringMVC 的搭建、常见用法、源码分析、扩展点分析、企业实战等方面展开。
好,先开始基础篇的介绍!


搭建 SpringMVC

Step1、创建 Maven 项目

开发环境:
IDE:IDEA 2022
构建工具:Maven 3.x
服务器:Tomcat 8
Spring版本:5.3.15

创建 Maven 项目:
使用 IDEA 创建一个Maven 子模块, 可以使用模板方式创建剩下不少功夫,具体如下图:
image.png

先展示Demo全貌:
圈出来的文件也是下面几个步骤需要交互的,后续就不一一截图了。
image.png


Step2、引入 Maven 依赖

<dependencies><!-- SpringMVC --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.15</version></dependency><!-- 日志 --><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency><!-- ServletAPI --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><!-- Spring5和Thymeleaf整合包 --><dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf-spring5</artifactId><version>3.0.12.RELEASE</version></dependency></dependencies>

引入后查看 Maven 依赖:
image.png


Step3、配置 web.xml

【知识扫盲】
web.xml 是一个传统的 Java Web 应用的部署描述文件,它用于配置 Web 应用的一些基本信息,比如:监听器、过滤器、过滤器、Servlet、欢迎页等。
在 Spring MVC 项目中,web.xml 主要负责:
1、加载 Spring 容器,通过 ContextLoaderListener 监听器加载 Spring 配置文件(applicationContext.xml 等),初始化 Spring 容器。
2、配置前端控制器 DispatcherServlet,DispatcherServlet 是 Spring MVC 的核心,负责拦截请求,分发给相应的 Controller 处理。
Tomcat 容器启动的时候,会加载 web.xml,这是一个复杂的过程,这边先不展开。
web.xml 内部元素的加载顺序通常是:context-param -> listener -> filter -> servlet。
值得一提的是,Spring Boot 内嵌了 Tomcat,不需要额外的部署描述文件。

【关键代码】

<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
<servlet><servlet-name>springMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 --><init-param><!-- contextConfigLocation为固定值 --><param-name>contextConfigLocation</param-name><!-- 使用classpath:表示从类路径查找配置文件,例如maven工程中的src/main/resources --><param-value>classpath:spring-mvc.xml</param-value></init-param><!--作为框架的核心组件,在启动过程中有大量的初始化操作要做而这些操作放在第一次请求时才执行会严重影响访问速度因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时--><load-on-startup>1</load-on-startup>
</servlet><servlet-mapping><servlet-name>springMVC</servlet-name><!--设置springMVC的核心控制器所能处理的请求的请求路径/所匹配的请求可以是/login或.html或.js或.css方式的请求路径但是/不能匹配.jsp请求路径的请求--><url-pattern>/</url-pattern>
</servlet-mapping>

Step4、创建 spring-mvc.xml

web.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-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"><!-- 自动扫描包 --><context:component-scan base-package="com.lw.mvc.web"/></beans>

Step5、编写控制器类

如下所示,一个方法返回视图,一个方法返回数据。

Tips:index.jsp是Maven模板自带的,如果要显示中文,注意加上编码设置。
Tips:目前基本都是前后端分离模式,因此主要关注返回数据的场景。

@Controller
public class HelloController {@RequestMapping("/")public String index() {return "index";}@ResponseBody@RequestMapping(value = {"/data", "/test"})public String data() {return "12345";}
}
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<body>
<h2>Hello 战神!</h2>
</body>
</html>

Step6、部署 Tomcat

这步比较简单,直接看图:
image.png
image.png
点击启动后,会自动访问:http://localhost:8085/study_mvc/
image.png
修改访问地址:http://localhost:8085/study_mvc/data
也可以正常输出数据,搞定收工!


搭建 SpringMVC 复盘

JSON 类型数据传递

接下来,测试一下JSON类型数据的传递。
先定义一个实体类,并修改控制层代码,@RequestBody 接收参数,@ResponseBody 响应结果,如下所示。

public class Student implements Serializable {private static final long serialVersionUID = 3227472127782930834L;private Integer id;private String name;private String email;private Integer age;...
}@Controller
public class HelloController {@ResponseBody@RequestMapping(value = "stu")public Student stu(@RequestBody Student student) {student.setName("战神");return student;}
}

启动代码试试看,Postman测试如下:
image.png
解决方案也简单,引入一下依赖:

<!-- jackson -->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.0</version>
</dependency>

重启一下服务,再试试:
image.png

Tips:很多文章介绍需要额外的步骤,其实引入依赖才是核心。


关于静态资源处理

背景前提:
如果你的 DispatcherServlet 拦截的是 *.do 这样的URL,就不存在访问不到静态资源的问题,但要多书写.do。
如果你的 DispatcherServlet 拦截的是 /,代表拦截了所有的请求,同时对 .js、.jpg 的访问也就被拦截了。
但 DispatcherServlet 不具备静态文件的处理能力,所以需要额外的配置。
映射规则参考一下知识延伸 - url-pattern 映射规则

目前有三种方案,那么如何选择,主要需求是可以访问静态文件,不要报404,当然效率也要注意。

方案一:激活Tomcat的defaultServlet来处理静态文件(推荐)

<servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.jpg</url-pattern><url-pattern>*.gif</url-pattern><url-pattern>*.png</url-pattern><url-pattern>*.js</url-pattern><url-pattern>*.css</url-pattern><url-pattern>*.html</url-pattern>
</servlet-mapping> 

说明:要配置多个,每种文件配置一个,并且要写在 DispatcherServlet 的前面,让 defaultServlet 先拦截,这样就不会进入Spring了,我想性能是最好的吧。

补充说明:
这里的 default,其实是在$tomcat/conf/web.xml文件中配置的:
该 web.xml 的执行优先级低于项目自带的 web.xml,但如果项目的没注册这个Servlet,则以 Tomcat 的为准。

<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>listings</param-name><param-value>false</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet><servlet-mapping><servlet-name>default</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>

如果某个 Servlet 的映射路径仅仅是一个/,那么这个Servlet就成为当前web应用的默认Servlet它可以处理其它所有Servlet都不处理的请求。开发时最好不要出现这种情况,否则web应用的静态资源无法被访问,从而被此Servlet 拦截处理。

 <!-- ================配置SpringMVC核心调度器================ -->
<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*:mvc-config.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>

这种情况会覆盖由 tomcat 提供的默认的 Servlet,该Serlvet是为静态资源提供访问服务的。
所有要额外配置其他高优先级处理器,也就是按方案一的设置,指定类型的文件,访问的时候,先使用默认Servlet处理,找不到才使用 MVC 的。

方案二: 在spring3.0.4以后版本提供了mvc:resources **
mvc:resources 的使用方法:
<mvc:resources location=“/images/” mapping="/images/
“/>
<mvc:resources location=”/js/" mapping=“/js/“/>
<mvc:resources location=”/css/" mapping="/css/
”/>
/images/**映射到 ResourceHttpRequestHandler进行处理,location指定静态资源的位置,可以是web application根目录下、jar包里面,这样可以把静态资源压缩到jar包中。cache-period 可以使得静态资源进行web cache
如果出现下面的错误,可能是没有配置<mvc:annotation-driven />的原因。
报错WARNING: No mapping found for HTTP request with URI [/mvc/user/findUser/lisi/770] in DispatcherServlet with name ‘springMVC’
使用mvc:resources/元素,把mapping的URI注册到SimpleUrlHandlerMapping的urlMap中,key为mapping的URI pattern值,而value为ResourceHttpRequestHandler,
这样就巧妙的把对静态资源的访问由HandlerMapping转到ResourceHttpRequestHandler处理并返回,所以就支持classpath目录,jar包内静态资源的访问。
另外需要注意的一点是,不要对SimpleUrlHandlerMapping设置defaultHandler.因为对static uri的defaultHandler就是ResourceHttpRequestHandler,
否则无法处理static resources request。

方案三,使用mvc:default-servlet-handler/
Xml代码 :mvc:default-servlet-handler/
会把"/**" url 注册到SimpleUrlHandlerMapping的urlMap中,把对静态资源的访问由HandlerMapping转到 org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler 处理并返回。
DefaultServletHttpRequestHandler使用就是各个Servlet容器自己的默认Servlet。
需要搭配: mvc:annotation-driven/

补充说明1:
多个HandlerMapping的执行顺序问题:
DefaultAnnotationHandlerMapping的order属性值是:0
<mvc:resources/ >自动注册的 SimpleUrlHandlerMapping的order属性值是: 2147483646
mvc:default-servlet-handler/自动注册的SimpleUrlHandlerMapping的order属性值是:2147483647
spring会先执行order值比较小的。当访问一个a.jpg图片文件时,先通过DefaultAnnotationHandlerMapping 来找处理器,一定是找不到的,我们没有叫a.jpg的Action。再按order值升序找,由于最后一个SimpleUrlHandlerMapping 是匹配"/**"的,所以一定会匹配上,再响应图片。

补充说明2:
访问一个图片,还要走层层匹配。真不知性能如何?改天做一下压力测试,与Apache比一比。
最后再说明一下,如何你的DispatcherServlet拦截 *.do这样的URL,就不存上述问题了。


对比 SpringBoot

玩过 SpringBoot 的人,看到上面步骤,一定感觉十分繁琐(对熟练的人另说)。
就像这篇《 搭建拥有数据交互的 SpringBoot 》介绍的,可能两三下就可以获取一个 SpringBoot 项目。
前面提到的,返回JSON数据也不需要额外的配置(依赖都导入了),静态资源也有默认约定。
果然还是便捷啊,不过还是前面说的,先使用 SpringBoot,再深入理解 SpringMVC。


知识补充

@RequestMapping

Tips:控制层 @Controller 的核心注解,设置接口的访问路径,类和方法上可以添加。

@RequestMapping 注解的功能
从注解名称上我们可以看到,@RequestMapping 注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。

@RequestMapping 注解的位置
@RequestMapping 标识一个类:设置映射请求的请求路径的初始信息。
@RequestMapping 标识一个方法:设置映射请求请求路径的具体信息。

Tips:@Target({ElementType.TYPE, ElementType.METHOD})

@RequestMapping 注解的 value 属性
@RequestMapping 注解的 value 属性通过请求的请求地址匹配请求映射。
@RequestMapping 注解的 value 属性是一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求,通常数组类型的注解属性,值可以是如下两种形式:

@RequestMapping(value = {"/data", "/test"})
@RequestMapping("/testParam")

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

@RequestMapping 注解的 method 属性
method 属性通过请求的请求方式(get或post)匹配请求映射
method 属性是一个 RequestMethod 类型的数组,表示该请求映射能够匹配多种请求方式的请求
若当前请求的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则浏览器报错405:Request method ‘POST’ not supported
写法如下:method = {RequestMethod.GET, RequestMethod.POST}

补充:@RequestMapping 的派生注解
对于处理指定请求方式的控制器方法,SpringMVC中提供了@RequestMapping的派生注解
处理get请求的映射–>@GetMapping
处理post请求的映射–>@PostMapping
处理put请求的映射–>@PutMapping
处理delete请求的映射–>@DeleteMapping

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

Tips:form 不支持发送这个method设置,但 ajax 和 axios 支持。

@RequestMapping 注解的 params属性

Tips:这个属性设置的多个值必须同时满足才能触发,本属性基本不用,了解即可。

params 属性通过请求的请求参数匹配请求映射,是一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系。
“param”:要求请求映射所匹配的请求必须携带param请求参数
“!param”:要求请求映射所匹配的请求必须不能携带param请求参数
“param=value”:要求请求映射所匹配的请求必须携带param请求参数且param=value
“param!=value”:要求请求映射所匹配的请求必须携带param请求参数但是param!=value
来一段Demo:

@RequestMapping(value = {"/testRequestMapping", "/test"},method = {RequestMethod.GET, RequestMethod.POST},params = {"username","password!=123456"}
)

若当前请求满足 value和method属性,但不满足params属性,此时页面回报错400:
Parameter conditions “username, password!=123456” not met for actual request parameters: username={admin}, password={123456}

@RequestMapping 注解的 headers 属性

基本同上,用的很少!

headers 属性通过请求的请求头信息匹配请求映射是一个字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系。
“header”:要求请求映射所匹配的请求必须携带header请求头信息
“!header”:要求请求映射所匹配的请求必须不能携带header请求头信息
“header=value”:要求请求映射所匹配的请求必须携带header请求头信息且header=value
“header!=value”:要求请求映射所匹配的请求必须携带header请求头信息且header!=value

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

SpringMVC 支持ant风格的路径

即模糊匹配用法

?:表示任意的单个字符,有且仅能1个,不能是“/”这类型特殊字符
*:表示任意的0个或多个字符
:表示任意的一层或多层目录,只能使用//xxx的方式

SpringMVC 支持路径中的占位符
原始方式:/deleteUser?id=1
rest方式:/deleteUser/1
SpringMVC 路径中的占位符常用于 RESTful 风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的 @RequestMapping 注解的 value 属性中通过占位符 {xxx} 表示传输的数据,再通过@PathVariable 注解,将占位符所表示的数据赋值给控制器方法的形参。
来一段Demo:

///testRest/1/admin
//最终输出的内容为-->id:1,username:admin
@RequestMapping("/testRest/{id}/{username}")
public String testRest(@PathVariable("id") String id, @PathVariable("username") String username){System.out.println("id:"+id+",username:"+username);return "success";
}

获取请求参数

接收参数的方式,与请求传递的 ContentType 紧密有关,常见的有 application/x-www-form-urlencoded 和 application/json 两种类型,后续专栏介绍。

1、普通参数
可以使用 @RequestParam 注解接收,或者HttpServletRequest#getParameter接收,详细细节后续篇章介绍。
这里只能处理 x-www-form-urlencoded 类型的请求,get和post方式都可以,但 json 不行。

@ResponseBody
@RequestMapping("/testParam")
public String testParam(HttpServletRequest request, String xxx, @RequestParam("xxx2") String xxx2) {System.out.println("xxx:" + xxx);System.out.println("xxx2:" + xxx2);String xxx3 = request.getParameter("xxx2");System.out.println("xxx3:" + xxx3);return "hello";
}

image.png

2、POJO 类型参数
这种情况,不能使用 @RequestParam 接收,直接写实体即可,效果如下。
这里是 x-www-form-urlencoded 的示例,如果是 json 请求,直接参考上面章节,使用 @RequestBody。

@ResponseBody
@RequestMapping("/testParam2")
public Student testParam2(Student student) {student.setName("战神");return student;
}

image.png

Tips:SpringMVC会使用构造器实例化出一个pojo类对象,即Student,然后使用setXxx()方法进行赋值,如果没有构造器会报错,没有set方法那么这个成员变量就为空null值。

3、其他类型
还有数组、List、Map,以及相应的复杂结构,后续章节展开介绍。



报文信息转换

HttpMessageConverter,报文信息转换器,将请求报文转换为Java对象,或将Java对象转换为响应报文。
HttpMessageConverter提供了两个注解和两个类型:
@RequestBody,@ResponseBody,RequestEntity,ResponseEntity

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

<form th:action="@{/testRequestBody}" method="post">用户名:<input type="text" name="username"><br>密码:<input type="password" name="password"><br><input type="submit">
</form>
// 输出结果:requestBody:username=admin&password=123456
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){System.out.println("requestBody:"+requestBody);return "success";
}

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

@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity){System.out.println("requestHeader:"+requestEntity.getHeaders());System.out.println("requestBody:"+requestEntity.getBody());return "success";
}

输出结果:
requestHeader:[host:“localhost:8080”, connection:“keep-alive”, content-length:“27”, cache-control:“max-age=0”, sec-ch-ua:“” Not A;Brand";v=“99”, “Chromium”;v=“90”, “Google Chrome”;v=“90"”, sec-ch-ua-mobile:“?0”, upgrade-insecure-requests:“1”, origin:“http://localhost:8080”, user-agent:“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36”]
requestBody:username=admin&password=123

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

@ResponseBody 处理 json
a>导入jackson的依赖

<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.1</version>
</dependency>

b>在SpringMVC的核心配置文件中开启mvc的注解驱动,此时在HandlerAdaptor中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将响应到浏览器的Java对象转换为Json格式的字符串
<mvc:annotation-driven />
c>在处理器方法上使用@ResponseBody注解进行标识
d>将Java对象直接作为控制器方法的返回值返回,就会自动转换为Json格式的字符串

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

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


文件上传下载

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

@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {//获取ServletContext对象ServletContext servletContext = session.getServletContext();//获取服务器中文件的真实路径String realPath = servletContext.getRealPath("/static/img/1.jpg");//创建输入流InputStream is = new FileInputStream(realPath);//创建字节数组byte[] bytes = new byte[is.available()];//将流读到字节数组中is.read(bytes);//创建HttpHeaders对象设置响应头信息MultiValueMap<String, String> headers = new HttpHeaders();//设置要下载方式以及下载文件的名字headers.add("Content-Disposition", "attachment;filename=1.jpg");//设置响应状态码HttpStatus statusCode = HttpStatus.OK;//创建ResponseEntity对象ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);//关闭输入流is.close();return responseEntity;
}

文件上传
文件上传要求form表单的请求方式必须为post,并且添加属性enctype=“multipart/form-data”
SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息。

1、添加依赖:

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

2、在SpringMVC的配置文件中添加配置(注意ID命名一定是 multipartResolver):

<!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><property name="defaultEncoding" value="utf-8"></property><property name="maxUploadSize" value="2097152"></property><!--限制文件上传2M内  --><property name="maxInMemorySize" value="40960"></property>
</bean>

3、编写控制层代码:

@RequestMapping("/testUp")
public String testUp(MultipartFile photo, HttpSession session) throws IOException {//获取上传的文件的文件名String fileName = photo.getOriginalFilename();//处理文件重名问题String hzName = fileName.substring(fileName.lastIndexOf("."));fileName = UUID.randomUUID().toString() + hzName;//获取服务器中photo目录的路径ServletContext servletContext = session.getServletContext();String photoPath = servletContext.getRealPath("photo");File file = new File(photoPath);if(!file.exists()){file.mkdir();}String finalPath = photoPath + File.separator + fileName;//实现上传功能photo.transferTo(new File(finalPath));return "success";
}

拦截器的配置

SpringMVC 中的拦截器用于拦截控制器方法的执行。
SpringMVC 中的拦截器需要实现 HandlerInterceptor,必须在SpringMVC的配置文件中进行配置:

<bean class="com.atguigu.interceptor.FirstInterceptor"></bean>
<ref bean="firstInterceptor"></ref>
<!-- 以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截 --><mvc:interceptor><mvc:mapping path="/**"/><mvc:exclude-mapping path="/testRequestEntity"/><ref bean="firstInterceptor"></ref>
</mvc:interceptor>
<!-- 以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,通过mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求
--><!-- Demo项目使用自定义Spring拦截器 -->
<mvc:interceptors><mvc:interceptor><mvc:mapping path="/**"/><bean class="com.demo.common.interceptor.SpringMVCInterceptor"></bean></mvc:interceptor>
</mvc:interceptors>

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

拦截器的执行顺序
浏览器 - 过滤器 - DispatcherServlet - preHandle - 控制层 - postHandle - 视图渲染 - afterComplation

多个拦截器的执行顺序
a>若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:
preHandle()会按照配置的顺序执行,而postHandle()和afterComplation()会按照配置的反序执行
b>若某个拦截器的preHandle()返回了false
preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行

拦截器源码分析
参考:DispatcherServlet#doDispatch

部分代码如下,关于执行顺序也从源码里面可以看出来,执行流程其实围绕着
处理执行链 HandlerExecutionChain 进行。

if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;
}// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = 0; i < interceptors.length; i++) {HandlerInterceptor interceptor = interceptors[i];if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}}return true;
}

具体示例:

//Step1. 自定义的拦截器,实现的接口HandlerInterceptor
public class CustomInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//预处理,返回true则继续执行。如果需要登录校验,校验不通过返回false即可,通过则返回true。System.out.println("执行preHandle()方法");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {//后处理System.out.println("执行postHandle()方法");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//在DispatcherServlet完全处理完请求后被调用System.out.println("执行afterCompletion()方法");}
}/*** Step2. 注册拦截器到容器中,/**代表所有路径* @param registry 拦截器注册表对象*/
@Override
public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new CustomInterceptor()).addPathPatterns("/**");
}

总结陈词

此篇文章介绍了SpringMVC 项目的基础搭建和一些知识介绍,仅供参考。
后续还会继续从 SpringMVC 的常见用法、源码分析、扩展点分析、企业实战等方面展开。
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

CSDN_END.gif

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 华为OD机试 - Wonderland游乐园 - 动态规划(Java 2024 D卷 200分)
  • 遥感领域新方向!Mamba+RS论文汇总!
  • Undertow详解
  • Spring Boot:SpringBoot入门
  • DHCP与DNS的配置
  • 【屏显MCU】多媒体接口总结
  • LNMP动态网站环境部署
  • Javascript中canvas与svg详解
  • LLM评估 | 大模型评估方法调研--论文解读(持续更新ing!!!)
  • iexcel-excel 大文件读取和写入-04-order 指定列顺序
  • Spring源码学习笔记之@Async源码
  • 智能番茄成熟度评估:基于深度学习的自动检测系统
  • AI推理硬件成本分析:AMD Instinct MI300X与Nvidia GPU比较
  • 商品中心关于缓存热key的解决方案
  • web、http协议、apache服务、nginx服务
  • 深入了解以太坊
  • 【翻译】babel对TC39装饰器草案的实现
  • Java超时控制的实现
  • jQuery(一)
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • laravel 用artisan创建自己的模板
  • PAT A1092
  • python 装饰器(一)
  • spring-boot List转Page
  • Spring-boot 启动时碰到的错误
  • Web Storage相关
  • 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  • 从0实现一个tiny react(三)生命周期
  • 从tcpdump抓包看TCP/IP协议
  • 检测对象或数组
  • 前端攻城师
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 原生Ajax
  • postgresql行列转换函数
  • 昨天1024程序员节,我故意写了个死循环~
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • ​一些不规范的GTID使用场景
  • ‌‌雅诗兰黛、‌‌兰蔻等美妆大品牌的营销策略是什么?
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • # 服务治理中间件详解:Spring Cloud与Dubbo
  • ### RabbitMQ五种工作模式:
  • #### golang中【堆】的使用及底层 ####
  • #QT(TCP网络编程-服务端)
  • (poj1.3.2)1791(构造法模拟)
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (分布式缓存)Redis哨兵
  • (附源码)spring boot基于小程序酒店疫情系统 毕业设计 091931
  • (附源码)基于ssm的模具配件账单管理系统 毕业设计 081848
  • (接口封装)
  • (六)c52学习之旅-独立按键
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?
  • .net framework 4.0中如何 输出 form 的name属性。
  • .net framework4与其client profile版本的区别