《学会 SpringMVC 系列 · 基础篇》

avatar
作者
猴君
阅读量:0

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

文章目录

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.xsd 		http://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 {          @Override     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {         //预处理,返回true则继续执行。如果需要登录校验,校验不通过返回false即可,通过则返回true。         System.out.println("执行preHandle()方法");         return true;     }      @Override     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {         //后处理         System.out.println("执行postHandle()方法");     }      @Override     public 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

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!