SpringMVC基础学习笔记 视频:尚硅谷 学习时间:2022年1月14日
1 SpringMVC简介 1.1 什么是MVC MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分
M:Model,模型层,指工程中的JavaBean,作用是处理数据。JavaBean分为两类:
一类称为实体类Bean:专门存储业务数据的,如 Student、User 等
一类称为业务处理 Bean:指 Service 或 Dao 对象,专门用于处理业务逻辑和数据访问。
V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据
C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器
MVC的工作流程
用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器。
1.2 什么是SpringMVC SpringMVC是Spring的一个后续产品,是Spring的一个子项目
SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案。在表述层框架历经 Strust、WebWork、Strust2 等诸多产品的历代更迭之后,目前业界普遍选择了 SpringMVC 作为 Java EE 项目表述层开发的首选方案 。
注:三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台servlet
1.3 SpringMVC的特点
Spring 家族原生产品 ,与 IOC 容器等基础设施无缝对接
基于原生的Servlet ,通过了功能强大的前端控制器DispatcherServlet ,对请求和响应进行统一处理
表述层各细分领域需要解决的问题全方位覆盖 ,提供全面解决方案
代码清新简洁 ,大幅度提升开发效率
内部组件化程度高,可插拔式组件即插即用 ,想要什么功能配置相应组件即可
性能卓著 ,尤其适合现代大型、超大型互联网项目要求
2 入门案例 2.1 创建maven工程
maven工程的archetype选择webapp
工程架构:
打包方式为war
:
引入相关依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 5.3.1</version > </dependency > <dependency > <groupId > ch.qos.logback</groupId > <artifactId > logback-classic</artifactId > <version > 1.2.3</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > javax.servlet-api</artifactId > <version > 3.1.0</version > <scope > provided</scope > </dependency > <dependency > <groupId > org.thymeleaf</groupId > <artifactId > thymeleaf-spring5</artifactId > <version > 3.0.12.RELEASE</version > </dependency > </dependencies >
注:由于 Maven 的传递性,我们不必将所有需要的包全部配置依赖,而是配置最顶端的依赖,其他靠传递性导入。
2.2 配置web.xml 注册SpringMVC的前端控制器DispatcherServlet
2.2.1 默认配置方式 此配置作用下,SpringMVC的配置文件默认位于WEB-INF下,默认名称为\-servlet.xml,例如,以下配置所对应SpringMVC的配置文件位于WEB-INF下,文件名为springMVC-servlet.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <web-app > <display-name > Archetype Created Web Application</display-name > <servlet > <servlet-name > springMVC</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > springMVC</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > </web-app >
2.2.2 扩展配置方式 可通过init-param
标签设置SpringMVC配置文件的位置和名称,通过load-on-startup
标签设置SpringMVC前端控制器DispatcherServlet的初始化时间,将其提前到服务器启动时就初始化前端控制器。
在resource
下创建一个spring配置文件springMVC.xml
:
web.xml
代码:指定SpringMVC配置文件的位置和名称
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <web-app > <display-name > Archetype Created Web Application</display-name > <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:springMVC.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 >
<url-pattern>
标签中使用/和/*的区别:
/
所匹配的请求可以是/login或.html或.js或.css方式的请求路径,但是/不能匹配.jsp请求路径的请求 。因此就可以避免在访问jsp页面时,该请求被DispatcherServlet处理,从而找不到相应的页面。/*
则能够匹配所有请求,例如在使用过滤器时,若需要对所有请求进行过滤,就需要使用/*的写法。
2.3 创建请求控制器 由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要创建处理具体请求的类,即请求控制器 。
请求控制器中每一个处理请求的方法称为控制器方法 。
因为SpringMVC的控制器由一个POJO(普通的Java类)担任 ,因此需要通过@Controller注解将其标识为一个控制层组件,交给Spring的IoC容器管理,此时SpringMVC才能够识别控制器的存在。
新建包com.hongyi.mvc.controller
,并新建类HelloController
:
1 2 3 4 @Controller public class HelloController { }
2.4 创建springMVC的配置文件
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?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" xmlns:mvc ="http://www.springframework.org/schema/mvc" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd" > <context:component-scan base-package ="com.hongyi.mvc.controller" /> <bean id ="viewResolver" class ="org.thymeleaf.spring5.view.ThymeleafViewResolver" > <property name ="order" value ="1" /> <property name ="characterEncoding" value ="UTF-8" /> <property name ="templateEngine" > <bean class ="org.thymeleaf.spring5.SpringTemplateEngine" > <property name ="templateResolver" > <bean class ="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver" > <property name ="prefix" value ="/WEB-INF/templates/" /> <property name ="suffix" value =".html" /> <property name ="templateMode" value ="HTML5" /> <property name ="characterEncoding" value ="UTF-8" /> </bean > </property > </bean > </property > </bean > </beans >
在WEB-INF
下创建文件夹templates
,并新建html文件index.html
2.5 测试HelloWorld 2.5.1 实现对首页的访问
1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html > <html lang ="en" xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > 首页</title > </head > <body > <h1 > Hello World!</h1 > </body > </html >
1 2 3 4 5 6 7 8 9 10 @RequestMapping("/") public String index () { return "index" ; }
部署tomcat服务器:注意版本选择9以下的,版本太高有bug!
执行结果
2.5.2 通过超链接跳转到指定页面 在主页index.html中设置超链接:
1 2 3 4 5 6 7 8 9 10 11 <!DOCTYPE html > <html lang ="en" xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > 首页</title > </head > <body > <h1 > Hello World123!</h1 > <a th:href ="@{/target}" > 访问目标页面target.html</a > </body > </html >
在请求控制器中创建处理请求的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Controller public class HelloController { @RequestMapping("/") public String index () { return "index" ; } @RequestMapping("/target") public String toTarget () { return "target" ; } }
测试结果
2.6 总结 浏览器发送请求,若请求地址符合前端控制器的url-pattern
,该请求就会被前端控制器DispatcherServlet
处理。前端控制器会读取SpringMVC的核心配置文件springmvc.xml
,通过扫描组件找到控制器,将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器 解析,加上前缀和后缀组成视图的路径,通过Thymeleaf
对视图进行渲染,最终转发到视图所对应页面。
3 @RequestMapping注解
源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping { String name () default "" ; @AliasFor("path") String[] value() default {}; @AliasFor("value") String[] path() default {}; RequestMethod[] method() default {}; String[] params() default {}; String[] headers() default {}; String[] consumes() default {}; String[] produces() default {}; }
3.1 注解功能 从注解名称上我们可以看到,@RequestMapping注解的作用就是将请求和处理请求的控制器方法 关联起来,建立映射关系。
SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。
3.2 注解位置
示例
新增一个类RequestMappingController
1 2 3 4 5 6 7 8 @Controller @RequestMapping("/hello") public class RequestMappingController { @RequestMapping("/testRequestMapping") public String success () { return "success" ; } }
新增一个html页面success.html
:
1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html > <html lang ="en" xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Success</title > </head > <body > <h1 > Success!</h1 > </body > </html >
修改主页面index.html
:
1 2 3 4 5 6 7 8 9 10 11 <!DOCTYPE html > <html lang ="en" xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > 首页</title > </head > <body > <h1 > Hello World!</h1 > <a th:href ="@{/hello/testRequestMapping}" > 测试RequestMapping注解的位置</a > <br > </body > </html >
测试结果:
3.3 注解属性 3.3.1 value
@RequestMapping注解的value属性通过请求的请求地址 匹配请求映射
@RequestMapping注解的value属性是一个字符串类型的数组 ,表示该请求映射能够匹配多个请求地址所对应的请求
@RequestMapping注解的value属性必须设置 ,至少通过请求地址匹配请求映射
RequestMappingController
1 2 3 4 5 6 7 8 @Controller @RequestMapping("/hello") public class RequestMappingController { @RequestMapping(value = {"/testRequestMapping", "/testValue"}) public String success () { return "success" ; } }
index.html
1 2 3 <h1 > Hello World!</h1 > <a th:href ="@{/hello/testRequestMapping}" > 测试value:testRequestMapping</a > <br > <a th:href ="@{/hello/testValue}" > 测试value:testValue</a > <br >
测试结果:
3.3.2 method
@RequestMapping注解的method属性通过请求的请求方式 (get或post)匹配请求映射
@RequestMapping注解的method属性是一个RequestMethod类型的数组 ,表示该请求映射能够匹配多种请求方式的请求
若当前请求的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则浏览器报错405:Request method ‘POST’ not supported,即两个属性要求均要满足
正常示例
RequestMappingController
1 2 3 4 5 6 7 8 9 10 11 @Controller @RequestMapping("/hello") public class RequestMappingController { @RequestMapping( value = {"/testRequestMapping", "/testValue"}, method = {RequestMethod.GET, RequestMethod.POST} ) public String success () { return "success" ; } }
index.html
1 2 3 4 5 <h1 > Hello World!</h1 > <a th:href ="@{/hello/testValue}" > 测试method属性GET</a > <br > <form th:action ="@{/hello/testValue}" method ="post" > <input type ="submit" value ="测试method属性POST" > </form >
测试结果
异常示例
例如删除POST方式后的执行结果:
1 2 3 4 @RequestMapping( value = {"/testRequestMapping", "/testValue"}, method = RequestMethod.GET )
结合请求方式的派生注解
对于处理指定请求方式的控制器方法,SpringMVC中提供了@RequestMapping的派生注解
示例:
1 2 3 4 @GetMapping("/testGetMapping") public String testGetMapping () { return "success" ; }
常用的请求方式有get,post,put,delete
但是目前浏览器只支持get和post,若在form表单提交时,为method设置了其他请求方式的字符串(put或delete),则按照默认的请求方式get处理。
若要发送put和delete请求,则需要通过spring提供的过滤器HiddenHttpMethodFilter,在RESTful部分会讲到。
3.3.3 param
@RequestMapping注解的params属性通过请求的请求参数匹配请求映射
@RequestMapping注解的params属性是一个字符串类型的数组 ,可以通过四种表达式设置请求参数和请求映射的匹配关系
"param"
:要求请求映射所匹配的请求必须携带param请求参数
"!param"
:要求请求映射所匹配的请求必须不能携带param请求参数
"param=value"
:要求请求映射所匹配的请求必须携带param请求参数且param=value
"param!=value"
:要求请求映射所匹配的请求必须携带param请求参数但是param!=value
示例
1 2 3 4 5 params = {"username" } params = {"!username" } params = {"username=Mark" } params = {"username!=Mark" } params = {"username=admin" , "password=123456" }
程序示例
1 2 3 4 5 6 7 @RequestMapping( value = "/testParams", params = {"username=admin", "password=123456"} ) public String testParams () { return "success" ; }
1 <a th:href ="@{/hello/testParams(username='admin', password=123456)}" > 测试params属性</a >
1 <a th:href ="@{/hello/testParams(username='admin', password=12345678)}" > 测试params属性</a >
若当前请求满足@RequestMapping注解的value和method属性,但是不满足headers属性,此时页面显示404错误,即资源未找到。
3.4 SpringMVC支持ant风格的路径 即SpringMVC支持通配符形式的路径匹配。
?:表示任意的单个字符
*:表示任意的0个或多个字符
**:表示任意的一层或多层目录
注意 :在使用*时,只能使用/* /xxx的方式
使用1
1 2 3 4 5 @RequestMapping("/a?a/testAnt") public String testAnt () { return "success" ; }
1 <a th:href ="@{/hello/aka/testAnt" > 测试Ant风格的路径</a >
使用2
1 2 3 4 5 @RequestMapping("/a*a/testAnt") public String testAnt () { return "success" ; }
1 <a th:href ="@{/hello/abcdefa/testAnt" > 测试Ant风格的路径</a >
使用3
1 2 3 4 5 @RequestMapping("/**/testAnt") public String testAnt () { return "success" ; }
1 <a th:href ="@{/hello/aaa/bbb/testAnt" > 测试Ant风格的路径</a >
3.5 SpringMVC支持路径中的占位符
原始方式:/deleteUser?id=1&username=mark
rest
方式:/deleteUser/1/mark
SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}
表示传输的数据,再通过@PathVariable
注解,将占位符所表示的数据赋值给控制器方法的形参。
使用
1 2 3 4 5 @RequestMapping(value = "/testRest/{id}/{username}") public String testRest (@PathVariable("id") String id, @PathVariable("username") String username) { System.out.println("id:" + id + ",username:" + username); return "success" ; }
1 <a th:href ="@{/hello/testRest/1/admin}" > 测试路径中的占位符</a > <br >
4 获取请求参数 4.1 通过原生ServleAPI获取 例如将HttpServletRequest
作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象。
新增控制器类TestController.java
1 2 3 4 5 6 7 @Controller public class TestController { @RequestMapping("/param") public String param () { return "test_param" ; } }
1 2 3 4 5 6 7 8 9 10 11 <!DOCTYPE html > <html lang ="en" xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > 测试请求参数</title > </head > <body > <h1 > 测试请求参数</h1 > <a th:href ="@{/testServletAPI(username='admin', password='123456')}" > 测试使用servletAPI获取请求参数</a > </body > </html >
新建控制器类ParamComtroller.java
,并新写方法testServletParam
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Controller public class ParamController { @RequestMapping("/testServletAPI") public String testServletAPI (HttpServletRequest request) { String username = request.getParameter("username" ); String password = request.getParameter("password" ); System.out.println("username: " + username + ", password: " + password); return "success" ; } }
执行结果:
4.2 通过控制器方法的形参获取请求参数 在控制器方法的形参位置,设置和请求参数同名 的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参。
1 <a th:href ="@{/testServletAPI(username='admin', password='123456')}" > 测试使用控制器形参获取请求参数</a >
1 2 3 4 5 6 7 8 9 10 11 @RequestMapping("/testParam") public String testParam (String username, String password) { System.out.println("username: " + username + ", password: " + password); return "success" ; }
执行结果:
如果请求参数中出现多个同名参数(例如复选框中提交多个同名数据),可以在形参位置设置一个字符串类型 或者字符串数组类型 来接收此请求参数:
1 2 3 4 5 6 7 8 9 <form th:action ="@{/testParam}" method ="post" > 用户名:<input type ="text" name ="username" > <br > 密码:<input type ="password" name ="password" > <br > 爱好:<input type ="checkbox" name ="hobby" value ="a" > a <input type ="checkbox" name ="hobby" value ="b" > b <input type ="checkbox" name ="hobby" value ="c" > c <br > <input type ="submit" value ="测试使用控制器的形参获取请求参数" > </form >
1 2 3 4 5 @RequestMapping("/testParam") public String testParam (String username, String password, String[] hobby) { System.out.println("username: " + username + ", password: " + password + ", hobby: " + Arrays.toString(hobby)); return "success" ; }
执行结果:
注意:
若使用字符串数组类型的形参,此参数的数组中包含了每一个数据
若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果
4.3 @RequestParam注解
作用:将请求参数和控制器方法的形参创建映射关系
属性:
value
(或name):指定为形参赋值的请求参数(前端请求的)的参数名
required
:设置是否必须传输此请求参数,默认值为true。若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错400:Required String parameter ‘xxx’ is not present;若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null
defaultValue
:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为””时,则使用默认值为形参赋值
示例
例如,前端的请求参数名称改为user_name
:
1 用户名:<input type ="text" name ="user_name" > <br >
则后端相应的添加上注解:
1 @RequestParam("user_name") String username
可用的完整的形式:
1 @RequestParam(value = "user_name", required = false, defaultValue = "hongyi") String username
1 2 3 4 5 6 7 8 @RequestMapping("/testParam") public String testParam ( @RequestParam(value = "user_name", required = false, defaultValue = "hongyi") String username, String password, String[] hobby) { System.out.println("username: " + username + ", password: " + password + ", hobby: " + Arrays.toString(hobby)); return "success" ; }
@RequestHeader是将请求头 信息和控制器方法的形参创建映射关系
@RequestHeader注解一共有三个属性:value、required、defaultValue,用法同@RequestParam
示例
1 2 3 4 public String testParam (@RequestHeader("Host") String host) { System.out.println("Host: " + host); return "success" ; }
执行结果:
4.5 @CookieValue @CookieValue是将cookie数据和控制器方法的形参创建映射关系
@CookieValue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam
示例
1 2 3 4 5 @RequestMapping("/testParam") public String testParam (@CookieValue("JSESSIONID") String cookie) { System.out.println("cookie: " + cookie); return "success" ; }
执行结果:
4.6 通过POJO获取请求参数 可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致 ,那么请求参数就会为此属性赋值。
1 2 3 4 5 6 7 8 9 10 11 12 @Data @ToString @NoArgsConstructor @AllArgsConstructor public class User { private Integer id; private String username; private String password; private Integer age; private String sex; private String email; }
1 2 3 4 5 6 7 8 <form th:action ="@{/testPojo}" method ="post" > 用户名:<input type ="text" name ="username" > <br > 密码:<input type ="password" name ="password" > <br > 性别:<input type ="radio" name ="sex" value ="男" > 男<input type ="radio" name ="sex" value ="女" > 女<br > 年龄:<input type ="text" name ="age" > <br > 邮箱:<input type ="text" name ="email" > <br > <input type ="submit" > </form >
1 2 3 4 5 @RequestMapping("/testPojo") public String testPojo (User user) { System.out.println(user); return "success" ; }
执行结果:有乱码
1 User(id=null, username=admin, password=123456, age=24, sex=??·, email=123456@qq.com)
4.7 解决获取请求参数的乱码问题 对于GET请求参数的乱码问题,可以在tomcat的配置文件server.xml
中直接修改URLEncoding
为UTF-8
即可:
1 <Connector port ="8080" URLEncoding ="UTF-8" ... >
注:8版本以上的tomcat已默认url的编码格式为utf-8
对于post请求,解决获取请求参数的乱码问题,必须在dispacherServlet
获取到请求参数之前,设置好字符集。可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter
,但是必须在web.xml中进行注册。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <filter > <filter-name > CharacterEncodingFilter</filter-name > <filter-class > org.springframework.web.filter.CharacterEncodingFilter</filter-class > <init-param > <param-name > encoding</param-name > <param-value > UTF-8</param-value > </init-param > <init-param > <param-name > forceResponseEncoding</param-name > <param-value > true</param-value > </init-param > </filter > <filter-mapping > <filter-name > CharacterEncodingFilter</filter-name > <url-pattern > /*</url-pattern > </filter-mapping >
注:tomcat启动时三大组件的初始化顺序:监听器 => 过滤器 => Servlet
执行结果:
5 域对象共享数据 5.1 request域共享 5.1.1 servletAPI 使用原生的servletAPI进行域对象的数据共享。