SpringMVC入门
SpringMVC 概述
什么是 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 视图,渲染数据后最终响应给浏览器。
什么是 SpringMVC?
- SpringMVC 是 Spring 的一个后续产品,是 Spring 的一个子项目
- SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案。在表述层框架历经 Struts、WebWork、Strust2 等诸多产品的历代更迭之后,目前业界普遍选择了 SpringMVC 作为 Java EE 项目表述层开发的首选方案。
注意:三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台 servlet
SpringMVC 的特点
-
Spring 家族原生产品,与 IOC 容器等基础设施无缝对接
-
基于原生的 Servlet,通过了功能强大的前端控制器 DispatcherServlet,对请求和响应进行统一处理
-
表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
-
代码清新简洁,大幅度提升开发效率
-
内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
-
性能卓著,尤其适合现代大型、超大型互联网项目要求
HelloWorld
开发环境
- IDEA 2020.1
- Maven 3.6.3
- Tomcat 8(IEDA配置教程:(https://blog.csdn.net/Cicci_/article/details/122611300)
- Spring 5.3.1
创建 Maven 工程
-
创建 Maven 工程
-
设置打包方式:war
<packaging>war</packaging>
-
引入相关依赖
<dependencies> <!-- SpringMVC --> <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> <!-- ServletAPI --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <!-- 依赖范围 provided:表示打成 war 包的时候,这个依赖不会一起打包 --> <scope>provided</scope> </dependency> <!-- Spring5和Thymeleaf整合包 --> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.12.RELEASE</version> </dependency> </dependencies>
配置 web.xml
-
默认配置方式
-
位置和名称默认,就叫做默认配置(位置默认:
/webapp/WEB-INF/web.xml
,名称默认就是这些路径目录的名称和 web.xml)
-
如果不想手动创建,那么也是可以通过 IDEA 自动生成的:
-
web.xml 代码
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 配置 SpringMVC 的前端控制器,对浏览器发送的请求进行统一处理 --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <!-- 设置 SpringMVC 的核心控制器所能处理的请求路径 /:表示当前浏览器发送的所有请求(不包括 jsp 请求路径) 原因:因为 jsp 的本质就是一个 Servlet(jsp 请求路径会直接找到对应的 jsp 页面然后进行跳转),所以不需要使用 DispatcherServlet 进行处理 如果 jsp 的请求被 SpringMVC 的 DispatcherServlet 进行处理,那么 SpringMVC 就会把 jsp 请求当做一个普通的请求进行处理,而不会去找对应的 jsp 页面 --> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
-
-
扩展配置方式
- 可通过
init-param
标签设置 SpringMVC 配置文件的位置和名称,通过load-on-startup
标签设置 SpringMVC 前端控制器DispatcherServlet 的初始化时间(我这里将它的初始化时间提前到服务器启动的时候)
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 配置 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 的初始化时间提前到服务器启动时,然后在启动服务的时候就可以直接进行使用了 > Spring 默认是在第一次使用的时候进行初始化 > 如果不设置初始化时间,假设服务启动时有很多的配置需要加载,那么就会影响访问速度 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <!-- 设置 SpringMVC 的核心控制器所能处理的请求路径 /:表示当前浏览器发送的所有请求(不包括 jsp 请求路径) 原因:因为 jsp 的本质就是一个 Servlet(jsp 请求路径会直接找到对应的 jsp 页面然后进行跳转),所以不需要使用 DispatcherServlet 进行处理 如果 jsp 的请求被 SpringMVC 的 DispatcherServlet 进行处理,那么 SpringMVC 就会把 jsp 请求当做一个普通的请求进行处理,而不会去找对应的 jsp 页面 --> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
- 可通过
-
标签中使用
/
和/*
的区别:/
所匹配的请求可以是 /login 或 .html 或 .js 或 .css 方式的请求路径,但是 / 不能匹配 .jsp 请求路径的请求,因此就可以避免在访问 jsp 页面时,该请求被 DispatcherServlet 处理,从而找不到相应的页面。/*
则能够匹配所有请求,例如在使用过滤器时,若需要对所有请求进行过滤,就需要使用/*
的写法。
创建请求控制器
- 由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要创建处理具体请求的类,即请求控制器(也就是 Controller)
- 请求控制器中的每一个处理请求的方法都被称为控制器方法
- 因为 SpringMVC 的控制器由一个 POJO(普通的 Java 类)担任,因此需要通过
@Controller
注解将其标识为一个控制层组件,交给 Spring 的 IOC 容器管理,此时 SpringMVC 才能够识别控制器的存在
package com.laoyang.mvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
}
创建 SpringMVC 配置文件
- 在
resources
目录下创建 SpingMVC 的配置文件,写法和 Spring 的差不多
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启组件扫描,需要在头部引入 context 名称空间 -->
<context:component-scan base-package="com.laoyang.mvc.controller" />
<!-- 配置 Thymeleaf 视图解析器 -->
<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>
测试 HelloWorld
案例一:实现对首页的访问
-
在
/WEB-INF/templates
下创建 index.html 页面(名称没要求,如果没有 templates 文件夹,就先创建一个)<!DOCTYPE html> <!-- 引入 thymeleaf 的命名空间,如果不引用则无法使用 thymeleaf 的语法 --> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>HelloWorld首页</h1> </body> </html>
-
创建对应的控制器,用来实现页面的跳转
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class HelloController { /** * 访问 /WEB-INF/templates下的 index.html 页面 */ @RequestMapping("/") public String index() { // 返回视图名称(这个名称就是我们要跳转的页面名称,前缀和后缀在配置文件中已经配置好了,所以只需要返回视图名称即可) return "index"; } }
-
测试效果
案例二:通过超链接跳转到指定页面
-
在
/WEB-INF/templates
下创建 taget.html 页面<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>主页</title> </head> <body> <h1>Hello World!</h1> </body> </html>
-
在 index.html 中添加一个超链接,用于测试该案例的效果
<!DOCTYPE html> <!-- 引入 thymeleaf 的命名空间,如果不引用则无法使用 thymeleaf 的语法 --> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>HelloWorld首页</h1> <h2> <!-- 如果直接使用 href="/taget",是不会加上上下文名称的 比如我们正常要访问的格式为:localhost:8080/上下文名称/taget 而使用 href="/taget" 就会变成 localhost:8080/taget 最后导致 404 --> <a th:href="@{/taget}">访问 taget 页面</a> </h2> </body> </html>
-
创建对应的控制器方法
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class HelloController { /** * 访问 /WEB-INF/templates下的 index.html 页面 */ @RequestMapping("/") public String index() { // 返回视图名称 return "index"; } @RequestMapping("/taget") public String taget() { return "taget"; } }
-
测试效果
鼠标悬浮在超链接上的时候我们可以看到左下角的路径,springmvc 就是我设置的上下文名称,点击链接后就会跳转至对应的页面:
运行流程(小结)
浏览器发送请求到服务器,如果该请求符合前端控制器的 url-pattern
规则, 那么该请求就会被前端控制器 DispatcherServlet
处理。前端控制器会读取 SpringMVC 的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中 @RequestMapping
注解的 value 属性进行匹配,如果匹配成功,则该注解所标识的控制器方法就是处理请求的方法,处理请求的方法需要返回一个字符串类型的视图名称,该视图会被视图解析器进行解析,然后加上配置好的前缀和后缀组成视图的路径,通过 Thymeleaf
对视图进行渲染,最后转发到视图锁对应的页面。
@RequestMapping 注解
-
为了整洁,创建一个新的工程来进行演示,和之前一样的东西:
web.xml
、spring-mvc.xml
、pom.xml
-
HelloController:
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class HelloController { /** * 跳转至首页 */ @RequestMapping("/") public String doIndex() { return "index"; } }
-
index.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> </body> </html>
@RequestMapping 注解的功能
- @RequestMapping 注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。
- SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。
@RequestMapping 注解的位置
- @RequestMapping 标识一个类:设置映射请求的请求路径的初始信息
- @RequestMapping 标识一个方法:设置映射请求请求路径的具体信息
// RequestMapping部分源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
}
简单理解:该注解加在类上表示当前类中的所有方法都被进行映射;添加在方法上表示当前方法被映射。
案例
-
在创建一个 Controller,代码和 HelloController 差不多
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class RequestMappingController { /** * 跳转至主页面 */ @RequestMapping("/") public String doTaget() { return "taget"; } }
-
修改首页(index.html)
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <h1> <a th:href="@{/test/}">测试 @RequestMapping 注解的位置</a><br> <a th:href="@{/test/success}">测试 @RequestMapping 注解的位置</a><br> </h1> </body> </html>
-
创建要跳转的页面(taget.html)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>主页面</title> </head> <body> <h1>这是主页面</h1> </body> </html>
-
启动 Tomcat,进行测试(此时会报错)
因为 HelloController 控制器中也有一个处理 “/” 请求的控制器方法,所以启动 Tomcat 的时候就会导致报错(因为有重复的,导致冲突了) 如果想在重复请求地址上正常进行访问,可以在类上使用 @RequestMapping 注解进行对应设置 > 比如在 HelloController 上设置value为 "/hello",在 RequestMappingController 上设置地址为 "/test" > 此时再次启动 Tomcat 就不会报错了 访问格式为:localhost:8080/上下文名称/hello/ 和 localhost:8080/上下文名称/test/ > 我这里就只在 RequestMappingController 类设置了
-
在 RequestMappingController 类上设置 @RequestMapping 注解,value 属性为 “/test”
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/test") public class RequestMappingController { /** * 跳转至主页面 */ @RequestMapping("/") public String doTaget() { return "taget"; } }
-
再次启动 Tomcat 进行测试(此时就不会报错了),效果如下:
-
在点击链接测试 RequestMappingController 之前也可以先 F12 看一下链接地址是什么样的
如果想要测试访问的效果,可以在
RequestMappingController
控制器中设置对应的控制器方法
@RequestMapping 注解的属性
红色的比较重要的,蓝色的是了解就可以了
value 属性
- @RequestMapping 注解的 value 属性通过请求的请求地址匹配请求映射
- @RequestMapping 注解的 value 属性是一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求
- @RequestMapping 注解的 value 属性必须设置,至少通过请求地址匹配请求映射
在前面的案例中,我们都是使用的单个值作为请求地址进行映射,下面就给大家演示一下多个请求地址如何进行映射
案例
-
修改 index.html 页面
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <h1> <a th:href="@{/test/success}">测试 @RequestMapping 注解的位置</a><br> <!-- 因为我们在RequestMappingController类上配置了@RequestMapping注解的value属性,所以这里使用的时候也需要加上 否则点击链接进行跳转的时候就会报 404 --> <a th:href="@{/test/success}">测试 @RequestMapping 注解的value属性</a><br> <a th:href="@{/test/hello}">测试 @RequestMapping 注解的value属性</a><br> </h1> </body> </html>
-
创建 success.html 页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>RequestMapping注解</title> </head> <body> <h1>Hello @RequestMapping</h1> </body> </html>
-
编写对应控制器,设置多个请求地址
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /** * @ClassName RequestMappingController * @Description: @RequestMapping 注解说明 * @Author Laoyang * @Date 2022/1/6 9:47 */ @Controller @RequestMapping("/test") public class RequestMappingController { @RequestMapping("/") public String doTaget() { return "taget"; } /** * value:是一个String数组,可设置多个请求路径,当满足其中任何一个路径的时候请求映射就能直接进行匹配。 */ @RequestMapping( value = {"/success", "/hello"} ) public String doSuccess() { return "success"; } }
-
启动 Tomcat 进行测试
点击红框中的两个超链接,分别查看效果
由此可见,两个超链接都可以映射到 RequestMappingController 控制器对应的控制器方法中
method 属性
- @RequestMapping 注解的 method 属性通过请求的请求方式(get 或 post)匹配请求映射
- @RequestMapping 注解的 method 属性是一个 RequestMethod 类型的数组,表示该请求映射能够匹配多种请求方式的请求
- 若当前请求的请求地址满足请求映射的 value 属性,但是请求方式不满足 method 属性,则浏览器报错
405:Request method 'POST' not supported
,如果想要映射成功,就需要满足这两个属性才行!
案例
-
修改 index.html 页面
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <h1> <a th:href="@{/test/success}">测试 @RequestMapping 注解的位置</a><br> <a th:href="@{/test/success}">测试 @RequestMapping 注解的value属性</a><br> <a th:href="@{/test/hello}">测试 @RequestMapping 注解的value属性</a><br> </h1> <form method="post" th:action="@{/test/success}"> <input type="submit" value="提交"> </form> </body> </html>
-
启动 Tomcat,测试效果
以上是我们没有添加 method 属性的效果。我们可以看到,这里的表单提交是不满足我们的需求的,如果我们没有在 @RequestMapping 注解中设置 method 属性,则提交的请求格式只会是 GET 类型(表单中虽然设置了 post,但是提交后还是会去找控制层中的映射方法,默认就是 GET 类型)
-
设置 method 属性
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/test") public class RequestMappingController { /** * method: 设置请求方式,也是一个数组,所以可以定义多个请求方式 * 可使用 GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE */ @RequestMapping( value = {"/success", "/hello"}, method = {RequestMethod.GET} ) public String doSuccess() { return "success"; } }
-
再次启动 Tomcat,测试效果
因为前端页面中设置的是 post 提交,而后端 method 设置的是 GET 方式,虽然可以找到 /success ,但是因为请求方式不同,所以就会导致映射方式不正确,最后报 405
-
将 method 属性设置为支持 GET 和 POST
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/test") public class RequestMappingController { /** * method: 设置请求方式,参数是一个枚举类型的数组,所以可以定义多个请求方式 * 可使用 GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE */ @RequestMapping( value = {"/success", "/hello"}, method = {RequestMethod.GET, RequestMethod.POST} ) public String doSuccess() { return "success"; } }
-
再次启动 Tomcat,测试效果
此时点击提交按钮后,就可以正常进入到指定的页面了
派生注解说明
- 对于处理指定请求方式的控制器方法,SpringMVC 中提供了 @RequestMapping 的派生注解:
- 处理 get 请求的映射 --> @GetMapping
- 处理 post 请求的映射 --> @PostMapping
- 处理 put 请求的映射 --> @PutMapping
- 处理 delete 请求的映射 --> @DeleteMapping
- 常用的请求方式有 get,post,put,delete
- 但是目前浏览器只支持 get 和 post,若在 form 表单提交时,给 method 属性设置了其他请求方式的字符串(put 或 delete),则按照默认的请求方式 get 处理
- 若要发送 put 和 delete 请求,则需要通过 spring 提供的过滤器
HiddenHttpMethodFilter
,如果硬要直接使用,就会报 405
案例
-
在 inde.html 添加一个超链接用来测试效果
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <h1> <a th:href="@{/test/success}">测试 @RequestMapping 注解的位置</a><br> <!-- 测试 value 属性 --> <a th:href="@{/test/success}">测试 @RequestMapping 注解的value属性</a><br> <a th:href="@{/test/hello}">测试 @RequestMapping 注解的value属性</a><br> </h1> <!-- 测试 method 属性 --> <form method="post" th:action="@{/test/success}"> <input type="submit" value="提交"> </form> <!-- 测试派生注解 --> <a th:href="@{/test/doGetMapping}">测试@GetMapping注解</a> </body> </html>
-
编写控制器,使用 @GetMapping 注解实现 get 方式的页面跳转
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/test") public class RequestMappingController { /** * @RequestMapping 结合请求方式的派生注解 * 比如:@GetMapping、@PostMapping、PutMapping、DeleteMapping */ @GetMapping("/doGetMapping") public String doGetMapping() { return "success"; } }
-
启动Tomcat,查看效果
为了简洁,这里我就不截 index.html 页面的效果了,以上是点击超链接跳转之后的效果
-
测试使用 put 方式提交的效果,先在 index.html 页面添加一个表单
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <h1> <a th:href="@{/test/success}">测试 @RequestMapping 注解的位置</a><br> <!-- 测试 value 属性 --> <a th:href="@{/test/success}">测试 @RequestMapping 注解的value属性</a><br> <a th:href="@{/test/hello}">测试 @RequestMapping 注解的value属性</a><br> </h1> <!-- 测试 method 属性 --> <form method="post" th:action="@{/test/success}"> <input type="submit" value="提交"> </form> <!-- 测试派生注解 --> <a th:href="@{/test/doGetMapping}">测试@GetMapping注解</a> <!-- 测试put请求方式--> <form method="put" th:action="@{/test/testPut}"> <input type="submit" value="put提交"> </form> </body> </html>
-
创建对应的控制器
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/test") public class RequestMappingController { @RequestMapping(value = "/testPut", method = RequestMethod.POST) public String testPut() { return "success"; } }
-
测试效果
可能有人会杠了,你为什么不用 @PutMapping?那是因为浏览器只支持 post 和 get 请求,而表单中设置的是 put 提交,在提交的时候就会使用 get 方式进行处理,后端就算用了 @PutMapping,也无法进行映射(put != get)
如果大家有兴趣,可以把控制器中的 POST 换成 GET,再次测试,就可以走通了,因为表单是使用 get 方式进行提交的(注意:这种方式只是让程序走通了,并没有实现我们最终的效果!!!后面会告诉大家如何实现 put 方式提交)。
params 属性(了解)
- @RequestMapping 注解的 params 属性通过请求的请求参数匹配请求映射
- @RequestMapping 注解的 params 属性是一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系
"param"
:要求请求映射所匹配的请求必须携带 param 请求参数"!param"
:要求请求映射所匹配的请求必须不能携带 param 请求参数"param=value"
:要求请求映射所匹配的请求必须携带 param 请求参数且 param=value"param!=value"
:要求请求映射所匹配的请求必须携带 param 请求参数但是 param!=value
注意:若当前请求满足 @RequestMapping 注解的 value 和 method 属性,但是不满足 params 属性,此时页面会报 400
案例
-
编写控制器
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/test") public class RequestMappingController { /** * params = {"username"}:表示请求地址必须带一个 username 的参数 * params = {"!username"}:表示请求地址不能带名为 username 的参数 * params = {"username=admin"}:表示请求地址中必须带 username 参数,并且值必须是 admin * params = {"username!=admin"}:表示请求地址中不能带 username 参数,并且值不能是 admin */ @RequestMapping( value = "/testParams", params = {"username"}) public String testParams() { return "success"; } }
-
在前端编写超链接或表单用来测试
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <!-- 测试params属性 --> <a th:href="@{/test/testParams}">测试params属性</a> <form method="get" th:action="@{/test/testParams}"> <input type="submit" value="提交"> </form> </body> </html>
-
启动 Tomcat,查看效果
因为表单和超链接在提交和跳转的时候都没有传 username 参数,所以在控制器中不能正常进行映射
-
给表单和超链接都添加对应的参数
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <!-- 测试params属性 --> <a th:href="@{/test/testParams(username='admin', password='123456')}">测试params属性</a> <form method="get" th:action="@{/test/testParams}"> 用户名:<input type="text" name="username"><br/> <input type="submit" value="提交"> </form> </body> </html>
-
再次测试
我这里只测试了超链接的效果,表单的效果也是一样的,就不给大家额外演示了
headers 属性(了解)
-
@RequestMapping 注解的 headers 属性通过请求的请求头信息匹配请求映射
-
@RequestMapping 注解的 headers 属性是一个字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系
-
"header"
:要求请求映射所匹配的请求必须携带 header 请求头信息 -
"!header"
:要求请求映射所匹配的请求必须不能携带 header 请求头信息 -
"header=value"
:要求请求映射所匹配的请求必须携带 header 请求头信息且 header=value -
"header!=value"
:要求请求映射所匹配的请求必须携带 header 请求头信息且 header!=value -
若当前请求满足 @RequestMapping 注解的 value 和 method 属性,但是不满足 headers 属性,此时页面显示 404 错误,即资源未找到
-
了解:可以在页面中使用 F12 查看对应的头信息
因为我们这里是获取的是请求头信息,所以我们使用页面请求头信息中的数据,比如 Host
案例
-
编写控制器
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/test") public class RequestMappingController { /** * headers 属性通过请求的请求头信息匹配请求映射 * headers = {"header"}:表示请求地址必须带一个 header 头信息 * headers = {"!header"}:表示请求地址不能带名为 header 的头信息 * headers = {"header=localhost"}:表示请求地址中必须带 header 头信息,并且值必须是 localhost * headers = {"header!=localhost"}:表示请求地址中不能带 header 头信息,并且值不能是 localhost */ @RequestMapping( value = "/testHeaders", headers = {"Host=localhost:8081"}) public String testHeaders() { return "success"; } }
Host 默认是 localhost:8080,但是我们这里为了测试不符合要求的效果,就先设置为 localhost:8081
-
在 index.html 页面创建一个超链接来测试
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <!-- 测试 handers 属性--> <a th:href="@{/test/testHeaders}">测试 handers 属性</a> </body> </html>
-
启动 Tomcat,进行测试
因为我们的请求是 localhost:8080,但是控制器中设置的值是 8081,所以这里肯定是找不到的
-
将 headers 属性设置为 8080
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/test") public class RequestMappingController { /** * headers 属性通过请求的请求头信息匹配请求映射 * headers = {"header"}:表示请求地址必须带一个 header 头信息 * headers = {"!header"}:表示请求地址不能带名为 header 的头信息 * headers = {"header=localhost"}:表示请求地址中必须带 header 头信息,并且值必须是 localhost * headers = {"header!=localhost"}:表示请求地址中不能带 header 头信息,并且值不能是 localhost */ @RequestMapping( value = "/testHeaders", headers = {"Host=localhost:8080"}) public String testHeaders() { return "success"; } }
-
再次测试
SpringMVC 支持 ant 风格的路径
-
?
:表示任意的单个字符 -
*
:表示任意的0个或多个字符 -
**
:表示任意的一层或多层目录注意:在使用
**
时,只能使用/**/xxx
的方式
案例
? 通配符的使用
-
在 index.html 添加相关超链接
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <!-- 测试 ant 风格路径 --> <a th:href="@{/test/aba/testAnt}">测试?通配符</a><br/> </body> </html>
-
编写对应控制器
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/test") public class RequestMappingController { /** * Ant 风格的路径测试 * ?:表示任意一个字符,比如 /a?a,我们访问的时候就可以是 /ava、/aqa、/awa 等... * 注意: * 1、路径中的 "?" 不能不写(错误写法:/aa),也不能写多个字符(错误写法:/assa) * 2、路径中的 "?" 不能是 /、? 字符(错误写法:/a/a、/a?a,这种格式在浏览器访问就会报 404,因为这些字符是一个特殊符号) * 3、"?" 前后的路径名称必须要加上(错误写法:/acc、/zsa、/qqq...等) */ @RequestMapping("/a?a/testAnt") public String testAnt() { return "success"; } }
-
启动Tomcat进行测试
* 通配符的使用
-
在 index.html 添加相关超链接
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <!-- 测试 ant 风格路径 --> <a th:href="@{/test/aa/testAnt2}">测试*通配符</a><br/> <a th:href="@{/test/accca/testAnt2}">测试*通配符</a><br/> </body> </html>
-
编写对应控制器
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/test") public class RequestMappingController { /** * Ant 风格的路径测试 * *:表示任意的0个或多个字符,我们访问的路径可以是 /aa、/ava、/aqqa、/awera 等... * 注意: * 1、路径中的 "*" 不能是 /、? 字符(错误写法:/a/a、/a?a,这种格式在浏览器访问就会报 404,因为这些字符是一个特殊符号) * 2、"*" 前后的路径名称必须要加上(错误写法:/acc、/zsa、/qqq...) */ @RequestMapping("/a*a/testAnt2") public String testAnt2() { return "success"; } }
-
启动Tomcat进行测试
** 通配符的使用
-
在 index.html 添加相关超链接
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <!-- 测试 ant 风格路径 --> <a th:href="@{/test/testAnt3}">测试**通配符</a><br/> <a th:href="@{/test/aaa/testAnt3}">测试**通配符</a><br/> <a th:href="@{/test/aaa/bbb/testAnt3}">测试**通配符</a><br/> </body> </html>
-
编写对应控制器
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/test") public class RequestMappingController { /** * Ant 风格的路径测试 * **:表示任意的一层或多层目录,我们访问的路径可以是 /** /testAnt(中间的空格不用在意,只是为了不触发注释格式加的) * 注意: * 1、路径中的 "**" 不能是 ? 字符(错误写法:/a?a,这种格式在浏览器访问就会报 404,因为这个字符是一个特殊符号) * 2、不能再 "**" 的前后写其它字符(错误写法:/a**a、/a**、/**a),如果在其它字符前后使用了 "**",那么就会把这两个 * 当成一个一个的 * 来进行使用(效果就和一个 * 是一样的了) */ @RequestMapping("/**/testAnt3") public String testAnt3() { return "success"; } }
-
启动Tomcat进行测试
SpringMVC 支持路径中的占位符(重点)
- 原始方式:/deleteUser?id=1
- rest 方式:/deleteUser/1
- SpringMVC 路径中的占位符常用于 RESTful 风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping 注解的 value 属性中通过占位符 {xxx} 表示传输的数据,在通过 @PathVariable 注解,将占位符所表示的数据赋值给控制器方法的形参。
案例
-
在 index.html 页面编写超链接
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <!-- 测试rest方式的占位符 --> <a th:href="@{/test/testPath/admin/123456}">测试rest方式的占位符</a> </body> </html>
-
创建对应控制器
package com.laoyang.mvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @Controller @RequestMapping("/test") public class RequestMappingController { /** * {}: * 注意:如果请求路径中有占位符,那么匹配地址的时候就必须有占位符,否则就会访问不到对应的请求 */ @RequestMapping("/testPath/{username}/{password}") public String testPath(@PathVariable("username") String username, @PathVariable("password") String password) { System.out.println(username + "---->" + password); return "success"; } }
-
启动Tomcat测试效果
-
正确效果
-
错误效果
因为控制器中我们设置的是两个占位符,所以请求路径中也必须是两个参数,这样才能正常映射
-