SpringMVC简介
MVC是什么
MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分。
- M:Model模型层,指工程中的 JavaBean,作用是处理数据
- V:View视图层,指工程中的 html、jsp等页面,作用是与用户进行交互、展示数据
- C:Controller控制层,指工程中的 servlet,作用是接收请求和响应浏览器
JavaBean分为两类:
- 一类称为实体类 Bean,专门存储业务数据,如 Student、User等
- 一类称为业务处理 Bean,指 Service、Dao对象,专门处理业务逻辑和数据访问
MVC的工作流程:
用户通过视图层发送请求到服务器,在服务器中请求被 Controller接收,Controller调用相应的 Model层处理请求,处理完毕将结果返回到 Controller,Controller再根据请求处理的结果找到相应的 View视图,渲染数据后最终响应给浏览器。
SpringMVC是什么
SpringMVC是 Spring的一个后续产品,是 Spring的一个子项目。
SpringMVC是 Spring为表述层开发提供的一套完备的解决方案。在表述层框架历经 Strust等诸多产品的更迭之后,目前业界普遍选择了 SpringMVC作为 JavaEE项目表述层开发的首选方案。
三层架构:
- 表述层:表示前端页面和后端 Servlet
- 业务逻辑层:Service层,主要是对业务的逻辑处理
- 数据访问层:Dao层,对数据的增删改查
三层架构处理流程:
前端页面 -发送请求-> 后端 Servlet接收 -调用 Service方法-> Service类 -根据不同的方法调用 Dao-> Dao类 -增删改查数据-> Service类 -处理数据-> Servlet -获取数据并跳转页面-> 前端页面
SpringMVC的特点
- Spring家族原生产品,与 IOC容器等基础设施无缝对接
- 基于原生的 Servlet,通过了功能强大的
前端控制器 DispatcherServlet,对请求和响应统一处理 - 表述层各细分领域需要解决的问题全方位覆盖
- 代码清新简洁,大幅提升开发效率
- 内部组件化程度高,可插拔式组件即插即用,根据需求配置相应的组件即可
- 性能卓越,尤其适合现代大型、超大型互联网项目
HelloWord(搭建环境)
创建 maven工程
Module -> Maven -> next -> GroupId -> Module name -> Finish
- 添加 web模块
- file -> Project Structure -> Modules -> 选择添加 web模块的项目 ->
+-> 选择对应的目录添加 web.xml
- file -> Project Structure -> Modules -> 选择添加 web模块的项目 ->
- 打包方式:war
- <packaging>war</packaging>:包含 web模块需要打包方式为 war包
- 引入依赖
<dependencies>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
// ...
</dependencies>
配置 maven工程
注册 SpringMVC的前端控制器 DispatcherServlet
默认配置方式:
此配置作用下,SpringMVC的配置文件默认位于 WEB-INF下,默认名为 -servlet.xml,例如,以下配置所对应 SpringMVC的配置文件位于 WEB-INF下,文件名为 SpringMVC-servlet.xml
- <servlet-name> 配置 SpringMVC的前端控制器,对请求进行统一处理
/,表示浏览器发送的请求可以是 /login、.html、.css,但不会匹配 .jsp请求路径的相求/*,表示所有请求
<?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>
<!--
/,表示浏览器发送的请求可以是 /login、.html、.css,
但不会匹配 .jsp请求路径的相求
/*,表示所有请求
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
扩展配置方式:(推荐)
可通过 init-param标签设置 SpringMVC配置文件的位置和名称,通过 load-on-startup标签设置 SpringMVC前端控制器 DispatcherServlet的初始化时间
- 通过初始化参数指定 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:springMVC.xml</param-value>
</init-param>
<!--
Servlet:初始化是在第一次访问时才初始化,
作为框架的核心组件,在启动过程中有大量的初始化操作要做,
而这些操作如果放在第一次请求时才执行会严重影响访问速度,
因此需要通过此标签将前端控制器 DispatcherServlet的初始化时间提前到服务器启动时
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
创建请求控制器
由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要创建具体请求的类,即请求控制器。 请求控制器中每一个请求的方法称为控制器方法。 因为 SpringMVC的控制器由一个 POJO(普通的 Java类)担任,因此需要通过 @Controller注解将其标识为一个控制层组件,交给 Spring的 IoC容器管理,此时 SpringMVC才能够识别控制器的存在。
- HelloController.java
package com.atjava.mvc.controller;
/**
* @author lv
* @create 2022-01-27 21:30
*/
import org.springframework.stereotype.Controller;
/**
* 将此类作为 SpringIoC容器中的一个组件时,才是一个控制器,
* 将一个类变为 IoC容器中的一个组件:
* 1. 使用 Bean标签配置
* 2. 注解(@Component、@Controller、@Repository、@Service) + 扫描
*/
@Controller
public class HelloController {
// ...
}
- 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:aop="http://www.springframework.org/schema/aop"
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/aop https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--配置扫描组件-->
<context:component-scan base-package="com.atjava.mvc.controller"></context:component-scan>
</beans>
创建 SpringMVC的配置文件
- 配置 Thymeleaf视图解析器
- 处理静态资源
- 开启mvc注解驱动
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<!--
order:配置视图解析器的优先级,存在多个视图解析器时配置当前解析器的优先级
-->
<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>
<!--
处理静态资源,例如html、js、css、jpg
若只设置该标签,则只能访问静态资源,其他请求则无法访问
此时必须设置<mvc:annotation-driven/>解决问题
-->
<mvc:default-servlet-handler/>
<!-- 开启mvc注解驱动 -->
<mvc:annotation-driven>
<mvc:message-converters>
<!-- 处理响应中文内容乱码 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="defaultCharset" value="UTF-8" />
<property name="supportedMediaTypes">
<list>
<value>text/html</value>
<value>application/json</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
测试 HelloWorld
- 配置 Tomca:Run/Debug Configurations -> Tomcat Server -> Local -> Name、Deployment...
实现对首页的访问:
- 在请求控制器中创建处理请求的方法
- @RequestMapping(value = "/")将当前请求和控制器方法创建映射关系
package com.atjava.mvc.controller;
/**
* @author lv
* @create 2022-01-27 21:30
*/
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 将此类作为 SpringIoC容器中的一个组件时,才是一个控制器,
* 将一个类变为 IoC容器中的一个组件:
* 1. 使用 Bean标签配置
* 2. 注解(@Component、@Controller、@Repository、@Service) + 扫描
*/
@Controller
public class HelloController {
// http://localhost:8080/springMVC-demo1/
// "/" -> /WEB-INF/templates/index.html
// 控制器中的方法才是真正处理请求的方法
@RequestMapping(value = "/") // 将当前的请求和控制器方法创建映射关系
/**
* .RequestMappingHandlerMapping
* - Mapped to com.atjava.mvc.controller.HelloController#index()
*/
public String index() {
// 返回要跳转的视图名称 index.html
return "index";
}
}
通过超链接跳转到指定页面:
- 在主页面 index.html中设置超链接
- "
/",以斜线开头的路径称为绝对路径,分为浏览器解析和服务器解析两种 - 超链接中的绝对路径就是浏览器解析:localhost:8080/就是绝对路径的上下文路径
- 可以使用 Thmeleaf语法来灵活获取 Tomcat服务器设置的上下文路径,@{/...}
- <a
th:href="@{/target}">访问目标页面 target.html</a>
- <a
- index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<!--
xmlns:th="http://www.thymeleaf.org" 表示 Thymeleaf的命名空间,用于使用 Thymeleaf的语法
-->
<head>
<meta charset="UTF-8">
<title>Index</title>
</head>
<body>
<h1>首页</h1>
<p>http://localhost:8080/springMVC-demo1/</p>
<!--
"/",以斜线开头的路径称为绝对路径,分为浏览器解析和服务器解析两种,
超链接中的绝对路径就是浏览器解析:localhost:8080/就是绝对路径的上下文路径。
此 demo的上下文路径是创建 Tomcat时设置的:/springMVC-demo1。
可以使用 Thmeleaf语法来灵活获取上下文路径,@{/...},
当发现其中内容是绝对路径时,th会自动添加上下文路径
-->
<a th:href="@{/target}">访问目标页面 target.html</a>
</body>
</html>
- HelloController.java
package com.atjava.mvc.controller;
/**
* @author lv
* @create 2022-01-27 21:30
*/
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 将此类作为 SpringIoC容器中的一个组件时,才是一个控制器,
* 将一个类变为 IoC容器中的一个组件:
* 1. 使用 Bean标签配置
* 2. 注解(@Component、@Controller、@Repository、@Service) + 扫描
*/
@Controller
public class HelloController {
// http://localhost:8080/springMVC-demo1/
// "/" -> /WEB-INF/templates/index.html
// 控制器中的方法才是真正处理请求的方法
@RequestMapping(value = "/") // 将当前的请求和控制器方法创建映射关系
/**
* org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
* - Mapped to com.atjava.mvc.controller.HelloController#index()
*/
public String index() {
// 返回要跳转的视图名称 index.html
return "index";
}
@RequestMapping("/target")
public String target() {
return "target";
}
}
请求流程总结:
- 获取请求
- 找到请求控制器
- 通过 @RequestMapping(请求映射)匹配请求与对应的处理方法
- 处理方法的返回值被视图解析器解析,加上前后缀就表示请求要跳转的页面
总结:
工程的搭建步骤:
- 创建 Maven工程,添加 web模块、添加依赖
- 配置 web.xml、配置前端控制器
- 创建请求控制器、方法,配置 SpringMVC的配置文件
- 扫描组件、配置视图解析器
1. 创建 webapp并注册前端控制器:DispatcherServlet
2. 创建控制器类 @Controller + springMVC.xml配置文件扫描
3. 在 springMVC.xml中配置视图解析器 Thymeleaf
请求处理流程:
- 浏览器发送请求,若请求地址符合前端控制器 SpringMVC的 url-pattern,该请求会被前端控制器 DispatcherServlet处理
- 前端控制器读取 SpringMVC的核心配置文件,通过扫描组件找到控制器(标有 @Controller注解的类)
- 将请求地址和控制器中标有 @RequestMapping(value值)注解的方法进行匹配
- 匹配成功的方法会返回一个字符串类型的视图名称,此视图名称会被视图解析器解析,添加前后缀组成视图的路径
- 通过 Thymeleaf对视图进行渲染,最终
转发到视图所对应的页面
@RequestMapping注解
@RequestMapping注解的功能
从注解名称上我们可以看到,@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。
SpringMVC接收到指定的请求,就会来找到在映射关系中对应的控制方法来处理这个请求。
@RequestMapping注解的位置
- @RequestMapping
标识一个类(主要用于区分模块):设置映射请求的请求路径的初始信息 - @RequestMapping
标识一个方法:设置映射请求的请求路径的具体信息
package com.atjava.mvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author lv
* @create 2022-02-07 23:28
*/
@Controller
@RequestMapping("/test")
public class RequestMappingController {
// 此时请求映射所映射的请求路径为:/test/testRequestMapping
@RequestMapping("/testRequestMapping")
public String testRequestMapping() {
return "success";
}
}
@RequestMapping注解的 value属性
- @RequestMapping注解的 value属性通过请求的请求地址匹配请求映射
- @RequestMapping注解的 value属性可以是一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求
- @RequestMapping注解的
value属性必须设置,至少通过请求地址匹配请求映射
<h1>index</h1>
<a th:href="@{/test/testRequestMapping}">测试 RequestMapping注解的位置</a><br/>
<a th:href="@{/test/test1}">测试 RequestMapping注解的 value属性 /test1</a><br/>
@Controller
@RequestMapping("/test")
public class RequestMappingController {
// 此时请求映射所映射的请求路径为:/test/testRequestMapping
@RequestMapping(
value = {"/testRequestMapping", "/test1"}
)
public String testRequestMapping() {
return "test";
}
}
@RequestMapping注解的 method属性
- @RequestMapping注解的 method属性通过请求地址的请求方式(get、post)匹配请求映射
- @RequestMapping注解的 method属性是一个 RequestMethod类型的数组,表示该请求映射能够匹配多种请求方式的请求
- 若当前请求的请求地址满足请求映射的 value属性,但是请求方式不满足 method属性,则浏览器报错 HTTP Status
405Request method 'POST' not supported - method属性值如下(枚举):
- RequestMethod.GET,
- RequestMethod.POST
- RequestMethod.DELETE
- RequestMethod.PUT
对于处理指定请求方式的控制器方法,SpringMVC提供了 @RequestMapping的派生注解:
- 处理 get请求的映射 -> @GetMapping
- 处理 post请求的映射 -> @PostMapping
- 处理 delete请求的映射 -> @DeleteMapping
- 处理 put请求的映射 -> @PutMapping
常用的请求方式有 get、post、delete、put
- 目前浏览器只支持 get、post,若在 form表单提交时,为 method设置了其它请求方式的字符串(put、delete),则按照默认的请求方式 get处理
- 若要发送 put、delete请求,则需要通过 spring提供的过滤器 HiddenHttpMethodFilter,在 Restfull部分会讲到
@Controller
//@RequestMapping("/test")
public class RequestMappingController {
// 此时请求映射所映射的请求路径为:/test/testRequestMapping
@RequestMapping(
value = {"/testRequestMapping", "/test1"},
method = {RequestMethod.GET}
//method = {RequestMethod.GET, RequestMethod.POST}
)
// HTTP Status 405 - Request method 'POST' not supported
public String testRequestMapping() {
return "test";
}
@GetMapping("/testGetMapping")
public String testGetMapping() {
return "test";
}
}
<form th:action="@{/test/test1}" method="post">
<!--<button type="submit">test method</button>-->
<input type="submit" value="test mothod" />
</form>
<a th:href="@{/testGetMapping}">testGetMapping</a>
注意:
- @RequestMapping必须要有 value属性,之后才会去匹配其它属性
@RequestMapping注解的 params属性(了解)
- @ReqquestMapping注解的 params属性通过请求的请求参数匹配请求映射
- @RequestMapping注解的 params属性是一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系
- param:要求请求映射所匹配的请求必须携带 param请求参数
- !param:要求请求映射所匹配的请求必须不能携带 param请求参数
- param=value:要求请求映射所匹配的请求必须携带 param请求参数且 param=value
- param!=value:要求请求映射所匹配的请求必须携带 param请求参数但 param!=value
- 请求参数必须同时满足 params属性中的值的所有要求
- 若当前请求满足 @RequestMapping注解的 value和 method属性,但是不满足params属性,此时页面
报错400
@RequestMapping(
value = {"/testParamsAndHeaders"},
params = {"username", "password!=123456"}
)
public String testParamsAndHeaders() {
return "test";
}
@PostMapping(
value = {"/testParamsAndHeaders2"},
params = {"username", "password!=123456"}
)
public String testParamsAndHeaders2() {
return "test";
}
<!--
使用括号(...)或问号?进行传参:
-->
<a th:href="@{/testParamsAndHeaders(username='admin', password=1234567)}">testParams</a><br/>
<a th:href="@{/testParamsAndHeaders?username=admin&password=1234567}">testParams?</a><br/>
<form th:action="@{/testParamsAndHeaders2}" method="post">
<input name="username" value="admin" /><br />
<input name="password" value="123456" /><br />
<input type="submit" value="testPostParams" />
</form>
@RequestMapping注解的 headers属性(了解)
- @RequestMapping注解的 headers属性通过请求的请求头信息匹配请求映射
- @RequestMapping注解的 headers属性是一个字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系
- header:要求请求映射所匹配的请求必须携带 header请求头信息
- !header:要求请求映射所匹配的请求必须不能携带 header请求头信息
- header=value:要求请求映射所匹配的请求必须携带 header请求头信息且 header=value
- header!=value:要求请求映射所匹配的请求必须携带 header请求头信息且 header!=value
- 若当前请求满足 @RequestMapping注解的 value和 method属性,但是不满足 headers属性,此时页面显示
404错误,即资源未找到
@GetMapping(
value = {"/testParamsAndHeaders3"},
headers = {"Host=localhost:8080"}
)
public String testParamsAndHeaders3() {
return "test";
}
<a th:href="@{/testParamsAndHeaders3}">testHeaders</a><br />
SpringMVC支持 ant(模糊匹配)风格的请求路径
- ?:表示任意的单个字符,此字符不能为空
- *:表示任意的 0个或多个字符
- **:表示任意的一层或多层目录,且 **的周围不能出现其它字符
注意:在使用 **时,只能使用
/**/xxx的方式
// ?
@RequestMapping("/a?a/testAnt")
public String testAnt() {
return "test";
}
// *
@RequestMapping("/a*a/testAnt")
public String testAnt2() {
return "test";
}
// **
@RequestMapping("/**/testAnt")
public String testAnt3() {
return "test";
}
<a th:href="@{/a1a/testAnt}">testAnt1,?:表示任意的单个字符</a><br />
<a th:href="@{/aaa/testAnt}">testAnt2,?:表示任意的单个字符</a><br />
<a th:href="@{/aa/testAnt}">testAnt3,?:表示任意的单个字符</a><br />
<a th:href="@{/aa/testAnt}">testAnt4,*:表示任意的 0个或多个字符</a><br />
<a th:href="@{/a1a/testAnt}">testAnt5,*:表示任意的 0个或多个字符</a><br />
<a th:href="@{/a12a/testAnt}">testAnt6,*:表示任意的 0个或多个字符</a><br />
<a th:href="@{/testAnt}">testAnt7,**:表示任意的一层或多层目录</a><br />
<a th:href="@{/a/testAnt}">testAnt8,**:表示任意的一层或多层目录</a><br />
<a th:href="@{/a/a/a/testAnt}">testAnt9,**:表示任意的一层或多层目录</a><br />
SpringMVC支持路径中的占位符(重点)
- 原始方式:/deleteUser?id=1&xxx=xxx
- rest方式:/deleteUser/1
- @PathVariable(xxx):获取请求地址中占位符所代表的值
SpringMVC路径中的占位符常用于 RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的 @RequestMapping注解的 value属性中通过占位符 {xxx}表示传输的数据,再通过 @PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参
注意:匹配路径中有占位符时,则实际请求地址中就必须有占位符这一层路径的数据
@RequestMapping("/test/{id}/{username}")
public String testTab(@PathVariable("id")Integer id, @PathVariable("username")String username) {
// http://localhost:8080/springMVC-demo2/test/1/lver
System.out.println("id: " + id + ", username: " + username); // id: 1, username: lver
return "test";
}
<a th:href="@{/test/1/lver}">@RequestMapping支持路径中的占位符{xxx}</a><br />
SpringMVC获取请求参数(重要)
通过 ServletAPI获取
将 HttpServletRequest作为控制器方法的形参,此时 HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象。
@RequestMapping("/testServletAPI")
// http://localhost:8080/springMVC-demo2/testServletAPI?id=123&username=admin
public String testServletAPI(HttpServletRequest request) {
System.out.println(request.toString());
Integer id = Integer.parseInt(request.getParameter("id"));
String username = request.getParameter("username");
System.out.println("id: " + id + ", username: " + username); // id: 123, username: admin
return "test";
}
<h2>测试请求参数 params</h2>
<a th:href="@{/testServletAPI(id=123, username='admin')}">测试 ServletAPI</a>
通过控制器方法的形参获取请求参数
在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在 DispatcherServlet中就会将请求参数赋值给相应的形参。
- 控制器方法的形参名必须与请求参数的参数名保持一致
- 对于同名的请求参数,可以通过:
- 字符串参数获取,属性值使用逗号分隔
- 字符串数组获取
@RequestMapping("/testParams")
public String testParams(int id, String username) {
System.out.println("id: " + id + ", username: " + username);
return "test";
}
/**
* 对于同名的请求参数,可以通过:
* 1. 字符串获取
* 2. 字符串数组获取
* @param username
* @param hobby
* @return
*/
@GetMapping("/testFormParams")
public String testFormParams(String username, String hobby) {
System.out.println("username: " + username + ", hobby: " + hobby);
// username: lver, hobby: 123,456,789
return "test";
}
@PostMapping("/testFormParams")
public String testFormParams2(String username, String[] hobby) {
System.out.println("username: " + username + ", hobbyArr: " + Arrays.asList(hobby));
// username: lver, hobbyArr: [123, 456, 789]
return "test";
}
<a th:href="@{/testParams(id=123, username='admin')}">测试 控制器的形参</a><br />
<form th:action="@{/testFormParams}" method="post">
<!--<form th:action="@{/testFormParams}" method="get">-->
<label>
name: <input name="username" value="lver" />
</label><br />
<label>
hobby:<br />
<input type="checkbox" name="hobby" value="123" />123<br />
<input type="checkbox" name="hobby" value="456" />456<br />
<input type="checkbox" name="hobby" value="789" />789<br />
</label>
<input type="submit" value="submit" />
</form>
@RequestParam
- @RequestParam是将请求参数和控制器方法的形参创建映射关系
@RequestParam注解一共有三个属性:
- value:指定为形参赋值的请求参数的参数名
- required:设置是否必须传输此请求参数,默认值 true(若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错400:Required String parameter 'xxx' is not present;若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null)
- defaultValue:不管 required属性值为 true或 false,当 value所指定的请求参数没有传输或传输的值为空字符串时,则使用默认值为形参赋值
@PostMapping("/testFormParams")
public String testFormParams2(
@RequestParam(value = "user_name", required = false, defaultValue = "xxxName") String username,
String[] hobby
) {
System.out.println("username: " + username + ", hobbyArr: " + Arrays.asList(hobby));
// username: lver, hobbyArr: [123, 456, 789]
return "test";
}
<form th:action="@{/testFormParams}" method="post">
<label>
name: <input name="user_name" value="lver" />
</label><br />
<label>
hobby:<br />
<input type="checkbox" name="hobby" value="123" />123<br />
<input type="checkbox" name="hobby" value="456" />456<br />
<input type="checkbox" name="hobby" value="789" />789<br />
</label>
<input type="submit" value="submit" />
</form>
@RequestHeader
- 如果想要获取请求中请求头的信息,那必须使用 @RequestHeader
- @RequestHeader是将请求头信息和控制器方法的形参创建映射关系
@RequestHeader注解一共有三个属性(用法同 @RequestParam):
- value
- required
- defaultValue
@PostMapping("/testFormParams")
public String testFormParams2(
@RequestParam(value = "user_name", required = false, defaultValue = "xxxName") String username,
String[] hobby,
@RequestHeader(value = "Host", required = true, defaultValue = "localhost:8080") String host
) {
System.out.println("username: " + username + ", hobbyArr: " + Arrays.asList(hobby));
// username: lver, hobbyArr: [123, 456, 789]
System.out.println("host: " + host);
// host: localhost:8080
return "test";
}
<form th:action="@{/testFormParams}" method="post">
<label>
name: <input name="user_name" value="lver" />
</label><br />
<label>
hobby:<br />
<input type="checkbox" name="hobby" value="123" />123<br />
<input type="checkbox" name="hobby" value="456" />456<br />
<input type="checkbox" name="hobby" value="789" />789<br />
</label>
<input type="submit" value="submit" />
</form>
@CookieValue
- @CookieValue是将 cookie数据和控制器方法的形参创建映射关系
@CookieValue注解一共有三个属性(用法同 @RequestParam):
- value
- required
- defaultValue
@PostMapping("/testFormParams")
public String testFormParams2(
@RequestParam(value = "user_name", required = false, defaultValue = "xxxName") String username,
String[] hobby,
@RequestHeader(value = "Host", required = true, defaultValue = "localhost:8080") String host,
@RequestHeader(value = "Origin", required = true, defaultValue = "localhost:8080") String origin,
@CookieValue(value = "JSESSIONID") String JSESSIONID
) {
System.out.println("username: " + username + ", hobbyArr: " + Arrays.asList(hobby));
// username: lver, hobbyArr: [123, 456, 789]
System.out.println("host: " + host + ", Origin: " + origin);
// host: localhost:8080, Origin: http://localhost:8080
System.out.println("JSESSIONID: " + JSESSIONID);
// JSESSIONID: 3B956BC0E844CA910D8742A1B06D68B6
return "test";
}
<form th:action="@{/testFormParams}" method="post">
<label>
name: <input name="user_name" value="lver" />
</label><br />
<label>
hobby:<br />
<input type="checkbox" name="hobby" value="123" />123<br />
<input type="checkbox" name="hobby" value="456" />456<br />
<input type="checkbox" name="hobby" value="789" />789<br />
</label>
<input type="submit" value="submit" />
</form>
通过 POJO获取请求参数
可以通过控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值。
- 类属性名必须与请求参数名一致
@PostMapping("/testPOJO")
public String testFormParams3(User user) {
System.out.println("User: " + user);
// User: User{id=null, username='lver', password='123', gender='female', age=456, email='784683810@qq.com', hobby=[123, 456]}
return "test";
}
<form th:action="@{/testPOJO}" method="post">
<label>
name: <input name="username" value="lver" />
</label><br />
<label>
password: <input name="password" value="" />
</label><br />
<label>
gender:
<input type="radio" name="gender" value="male" />男
<input type="radio" name="gender" value="female" />女
</label><br />
<label>
age: <input type="text" name="age" value="" />
</label><br />
<label>
email: <input name="email" value="" />
</label><br />
<label>
hobby:<br />
<input type="checkbox" name="hobby" value="123" />123<br />
<input type="checkbox" name="hobby" value="456" />456<br />
<input type="checkbox" name="hobby" value="789" />789<br />
</label><br />
<input type="submit" value="使用 POJO实体类参数接收请求参数" />
</form>
package com.atjava.mvc.bean;
import java.util.Arrays;
/**
* @author lv
* @create 2022-02-15 23:14
*/
public class User {
private Integer id;
private String username;
private String password;
private String gender;
private Integer age;
private String email;
private String[] hobby;
public User() {
}
public User(Integer id, String username, String password, String gender, Integer age, String email, String[] hobby) {
this.id = id;
this.username = username;
this.password = password;
this.gender = gender;
this.age = age;
this.email = email;
this.hobby = hobby;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + ''' +
", password='" + password + ''' +
", gender='" + gender + ''' +
", age=" + age +
", email='" + email + ''' +
", hobby=" + Arrays.toString(hobby) +
'}';
}
}
解决获取请求参数的乱码问题
解决获取请求参数的乱码问题,可以使用 SpringMVC提供的编码过滤器 CharacterEncodingFilter,但是必须在 web.xml中进行注册。
SprigMVC中处理编码的过滤器一定要配置到其它过滤器之前,否则无效。
- get请求方式的乱码是可以在 Tomcat的 server.xml配置文件中一次性解决的
- <Connector URLEncoding="UTF-8" />
- 其它请求方式的乱码解决,使用 SpringMVC提供的编码过滤器 CharacterEncodingFilter
- web.xml
<!--配置 SpringMVC的编码过滤器-->
<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>
@PostMapping("/testPOJO")
public String testFormParams3(User user) {
System.out.println("User: " + user);
// User: User{id=null, username='六lver', password='123456', gender='male', age=12, email='aweiweier192@163.com', hobby=[123, 456]}
return "test";
}
域对象共享数据
- request一次请求
- Session浏览器开启到关闭的过程(只与浏览器开启关闭有关)
- 钝化:服务器关闭,浏览器没有关闭,Session中的数据序列化到磁盘上
- 活化:服务器关闭后又开启,浏览器一直没关闭,服务器会将钝化到磁盘中的 Session数据重新读取到 Session中
- Application(ServletContext)服务器的开启到关闭的过程
使用 ServletAPI向 request域对象共享数据
request设域数据操作:
- request.setAttribute("testRequestScope", "Hello, requestScope");设置共享数据
- request.getAttribute("scopeName"); 获取共享数据
- request.removeAttribute("scopeName"); 删除共享数据
页面获取域数据的方式:
- 语法:
th:xxx="${xxx}"
- request域中的数据,直接写键名
- Session域中的数据,使用 session.xxx的方式
- Application域中的数据,使用 application.xxx的方式
// 使用 servletAPI向 request域对象共享数据
@RequestMapping("/testRequestByServletAPI")
public String testRequestByServletAPI(HttpServletRequest request) {
request.setAttribute("testRequestScope", "Hello, requestScope"); // 设置共享数据
// request.getAttribute(); 获取共享数据
// request.removeAttribute(); 删除共享数据
return "test";
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>test</h2>
<!--
1.request域中的数据,直接写键名
2.Session域中的数据,使用 session.xxx的方式
3.Application域中的数据,使用 application.xxx的方式
-->
<p th:text="${testRequestScope}"></p>
</body>
</html>
使用 SpringMVC的 ModelAndView(重点)向 request域对象共享数据
- 在控制器方法中创建 ModelAndView对象
- addObject(xxx),向域中设置共享数据
- setViewName(),设置视图,实现页面跳转
- 控制器方法返回值必须是 ModelAndView
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
/**
* ModelAndView又 Model和 View的功能
* Model主要用于向 请求域中共享数据
* View主要用于设置视图,实现页面跳转
*/
ModelAndView mv = new ModelAndView();
// 向域中设置共享数据
mv.addObject("testModelAndView", "Hello, ModelAndView");
// 设置视图,实现页面跳转
mv.setViewName("test");
return mv;
}
<a th:href="@{/testModelAndView}">使用 ModelAndView向 request域对象共享数据</a>
<p th:text="${testModelAndView}"></p>
使用 Model向 request域对象共享数据
- 控制器参数是 Model类
- addAttribute(),向 request域中添加共享数据
- 控制器返回值是 String类型
@RequestMapping("/testModel")
public String testModel(Model model) {
model.addAttribute("testModel", "Hello, testModel");
return "test";
}
<a th:href="@{/testModel}">使用 Model向 request域对象共享数据</a><br />
<p th:text="${testModel}"></p>
使用 Map向 request域对象共享数据
- 将 Map作为控制器的参数使用
- 向 Map中存放的数据,就是向 request域对象共享的数据
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map) {
map.put("testMap", "Hello, testMap");
return "test";
}
<a th:href="@{/testMap}">使用 Map向 request域对象共享数据</a><br />
<p th:text="${testMap}"></p>
使用 ModelMap向 request域对象共享数据
- 与 Model类似
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap mmp) {
mmp.addAttribute("testModelMap", "Hello, testModelMap");
return "test";
}
<a th:href="@{/testModelMap}">使用 ModelMap向 request域对象共享数据</a><br />
<p th:text="${testModelMap}"></p>
Model、ModelMap、Map的关系
Model、ModelMap、Map类型的参数其本质上都是 BindingAwareModelMap类型的。
public interface Model {}
public class ModelMap extends LinkedHashMap<String, Object> {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class BindingAwareModelMap extends ExtendedModelMap {}
向 Session域中共享数据
- 使用 ServletAPI中的 HttpSession作为控制器的参数,向 Session域中共享数据
- html页面获取 session域中的数据
${session.xxx}
@RequestMapping("/testSession")
public String testSession(HttpSession httpSession) {
httpSession.setAttribute("testSession", "Hello, session");
return "test";
}
<a th:href="@{/testSession}">使用 HttpSession向 session域中共享数据</a>
<p th:text="${session.testSession}"></p>
向 application域共享数据
- 使用 HttpSession获取 ServletContext实例
- html页面获取 application域中的数据
${application.xxx}
@RequestMapping("/testApplication")
public String testApplication(HttpSession httpSession) {
// ServletContext表示整个应用
ServletContext servletContext = httpSession.getServletContext();
servletContext.setAttribute("testApplication", "Hello, Application");
return "test";
}
<a th:href="@{/testApplication}">使用 HttpSession向 application域中共享数据</a><br />
<p th:text="${application.testApplication}"></p>
ServletContext、HttpSession、HttpServletRequest
- ServletContext:范围最大,应用程序级别的,整个应用程序都能访问;
- HttpSession–次之,会话级别的,在当前的浏览器中都能訪问[不论是在同一浏览器开多少窗体,都能够访问],可是换个浏览器就不行了,就必须又一次创建session;
- HttpServletRequest–范围最小,请求级别,请求结束,变量的作用域也结束【也就是仅仅是一次访问,访问结束,这个也结束】。
@RequestMapping("/testServlet")
public ResponseBean testServlet(HttpServletRequest request, HttpServletResponse response) {
// 1
ServletContext sc = request.getServletContext();
sc.setAttribute("sc_name", "sc_value");
// 2
HttpSession session = request.getSession();
session.setAttribute("session_name", "session_value");
// 3
request.setAttribute("request_name", "request_value");
String sc_value = (String) sc.getAttribute("sc_name");
String session_value = (String) session.getAttribute("session_name");
String request_value = (String) request.getAttribute("request_name");
log.debug(sc_value + ":" + session_value + ":" + request_value);
// request.getRequestDispatcher("MyServlet2").forward(request, response);
return ResponseBean.buildSuccessResponse();
}
SpringMVC的视图
SpringMVC中的视图是 View接口,视图的作用是渲染数据,将模型 Model中的数据展示给用户。
SpringMVC视图的种类很多,默认有转发视图(InternalResourceView)和重定向视图(RedirectView)。
当工程中引入 jstl的依赖,转发视图会自动转换为 JstlView。
若使用的视图技术为 Thymeleaf,在 SpringMVC的配置文件中配置了 Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是 ThymeleafView。
ThymeleafView
当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被 SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转。
当返回的视图名称没有任何前缀时,此时创建的就是 ThymeleafView视图
@RequestMapping("/testThymeleafView")
public String testThymeleafView() {
return "test"; // ThymeleafView
}
InternalResourceView 转发视图
- 转发发生在服务器内部,不能跨域只能访问服务器内部的资源 SpringMVC中默认的转发视图是 InternalRourceView。
SpringMVC中创建转发视图的情况:
当控制器方法中所设置的视图名称以 forward:为前缀时,创建 InternalResourceView视图,此时的视图名称不会被 SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀 forward:去掉,剩余部分作为最终路径通过转发的方式实现跳转;如 "forward:/viewName"。
- forward:/xxx,其中 xxx必须是一个控制器方法对应的请求地址
@RequestMapping("/testForward")
public String testForward() {
return "forward:/testThymeleafView";
}
<a th:href="@{/testForward}">视图 InternalResourceView</a>
RedirectView 重定向视图
- 重定向是浏览器发起两次请求,通过浏览器可以访问任何资源,故可以跨域
SpringMVC中默认的重定向视图是 RedirectView。
当控制器方法中设置的视图名称为 redirect:为前缀时,创建 RedirectView视图,此时的视图名称不会被 SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀 redirect:去掉,剩余部分作为最终路径通过重定向的方式实现跳转;如 "redirect:/xxx"。
- redirect:/xxx,redirect: 后有
/时 xxx是其它控制器方法的请求地址 - redirect:http://xxx123,redirect:后没有
/时,跳转到其它服务器
@RequestMapping("/testRedirect")
public String testRedirect() {
return "redirect:/testThymeleafView";
}
@RequestMapping("/testRedirectBaidu")
public String testRedirectBaidu() {
return "redirect:http://www.baidu.com";
// https://fanyi.youdao.com/
}
<a th:href="@{/testRedirect}">视图 RedirectView</a><br />
<a th:href="@{/testRedirectBaidu}">重定向 百度</a><br />
视图控制器 view-controller
当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用 view-controller标签来表示。
- <mvc:view-controller path="xxx" view-name="xxx" />
- path: 设置处理的请求地址
- view-name: 设置请求地址所对应的视图名称
- <mvc:annotation-driven /> 开启 mvc的注解驱动
- 注: 当 SpringMVC中设置任何一个 view-controller时,所有的控制器将全部失效, 此时需在 SpringMVC核心配置文件中设置开启 mvc注解驱动的标签 <mvc:annotation-driven />
<!--视图控制器:view-controller -->
<!--
path: 设置处理的请求地址
view-name: 设置请求地址所对应的视图名称
注: 当 SpringMVC中设置任何一个 view-controller时,所有的控制器将全部失效,
此时需在 SpringMVC核心配置文件中设置开启 mvc注解驱动的标签 <mvc:annotation-driven />
-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
<!--开启 mvc的注解驱动-->
<mvc:annotation-driven />