环境准备工作等均省略,可详见快速入门,此处只写非共有部分代码
该部分示例项目SpringMvcThree
已上传至Gitee,可自行下载
客户端请求参数的格式为:name=value&password=value... ...
服务端想要获取请求参数有时需要对数据进行封装,SpringMVC可接收如下类型的参数
注意
Controller中的业务方法的参数名称要与请求参数的名称一致,如图所示
<img alt="image-20240722173311114">
在该截图中返回值为void
,代表controller控制器中的该业务方法quickMethod9
不回写数据,但仍然需要@ResponseBody
注解,此时代表ResponseBody的响应体为空,也就是说前端页面为空。
针对获取普通数据类型来说,参数未自动映射匹配有两种方法:
在pom.xml文件中添加maven插件
使用@RequestParam
注解
以上两种方式详见代码示例部分
此处只写controller控制器类,其余部分搭建可详见快速入门以及数据响应部分内容
在controller包下创建UserController
类,代码如下
//将Usercontroller放到Spring容器中
@Controller
@RequestMapping("/user")
public class UserController {
@ResponseBody
@RequestMapping(value = "/quick1")
public void save1(String username, int age) {
System.out.println("name:" + name);
System.out.println("age:" + age);
}
}
此时运行截图如下,会报错
<img alt="image-20241121222247734">
报错原因:错误说明 Spring MVC 无法推断参数名称,通常是因为编译时没有启用参数名的保留功能,或者方法参数缺少显式绑定的注解
==解决方法==
方案一: 启用参数名的保留功能,即在pom.xml文件中加上maven插件,插件代码如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!-- maven插件版本 -->
<version>3.13.0</version>
<configuration>
<!-- 所使用的Java版本 -->
<source>21</source>
<target>21</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
<img alt="image-20241121223131756">
方案二: 为方法参数添加 @RequestParam
注解,明确指定参数名称,更改后的UserController
类,代码如下
//将Usercontroller放到Spring容器中
@Controller
@RequestMapping(value = "/user")
public class UserController {
@RequestMapping(value = "quick1")
@ResponseBody
public void save1(@RequestParam("username")String username, @RequestParam("age")int age) throws IOException {
System.out.println(username);
System.out.println(age);
}
}
运行后前端页面为空白,但是控制台会有输出,如图所示
<img alt="image-20241121223411922">
注意
Controller中业务方法的参数为pojo类对象,且该pojo类中的的属性名要与请求参数的名称一致,如图所示
<img alt="image-20241122132859810">
获取POJO类型时,不需要去添加maven插件或使用@RequestParam注解来使参数自动映射匹配,在本代码示例中的获取普通数据类型使用的是注解方式来进行参数匹配,以此来区别说明POJO类型不需要去添加maven插件或使用@RequestParam注解。
创建pojo包,并在该包下创建一个User类,代码如下
package at.guigu.pojo;
public class User {
private String name;
private int age;
public User() {}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
在controller包下的UserController
类中添加save2
方法,完整代码如下:
package at.guigu.controller;
import at.guigu.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
//将Usercontroller放到Spring容器中
@Controller
@RequestMapping(value = "/user")
public class UserController {
// 获取普通数据类型
@RequestMapping(value = "/quick1")
@ResponseBody
public void save1(@RequestParam("username")String username, @RequestParam("age")int age) throws IOException {
System.out.println(username);
System.out.println(age);
}
// 获取pojo类型
@ResponseBody
@RequestMapping("/quick2")
public void save2(User user) throws IOException {
System.out.println(user);
}
}
<img alt="image-20241122134859145">
注意:
Controller中业务方法的参数的数组名称要与请求参数的名称一致,如图所示
<img alt="image-20241122135326923">
针对获取数组类型来说,参数未自动映射匹配有两种方法:
在pom.xml文件中添加maven插件
使用@RequestParam
注解
以上两种方法的操作与获取获取普通数据类型一致,可详见获取普通数据类型部分的代码示例,此处仅以注解形式示例
在controller包下的UserController
类中添加save3
方法,完整代码如下:
package at.guigu.controller;
import at.guigu.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.util.Arrays;
//将Usercontroller放到Spring容器中
@Controller
@RequestMapping(value = "/user")
public class UserController {
// 获取普通数据类型
@RequestMapping(value = "/quick1")
@ResponseBody
public void save1(@RequestParam("username")String username, @RequestParam("age")int age) throws IOException {
System.out.println(username);
System.out.println(age);
}
// 获取POJO类型
@ResponseBody
@RequestMapping("/quick2")
public void save2(User user) throws IOException {
System.out.println(user);
}
// 获取数组类型
@ResponseBody
@RequestMapping("/quick3")
public void save3(@RequestParam("strs")String[] strs) throws IOException {
// 由于直接打印数组时只会打印出其地址,所以将其转为List集合输出到控制台
System.out.println(Arrays.asList(strs));
}
}
<img alt="image-20241122140622192">
Step1:POJO类中创建User类(代码详见获取POJO类型),创建VO类,代码如下
package at.guigu.pojo;
import java.util.List;
public class VO {
// 将想要获取的集合封装到对象中
private List<User> userList;
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
@Override
public String toString() {
return "VO{" +
"userList=" + userList +
'}';
}
}
Step2:在controller包下的UserController
类中添加save4
方法,完整代码如下:
package at.guigu.controller;
import at.guigu.pojo.User;
import at.guigu.pojo.VO;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.util.Arrays;
//将Usercontroller放到Spring容器中
@Controller
@RequestMapping(value = "/user")
public class UserController {
// 获取普通数据类型
@RequestMapping(value = "/quick1")
@ResponseBody
public void save1(@RequestParam("username")String username, @RequestParam("age")int age) throws IOException {
System.out.println(username);
System.out.println(age);
}
// 获取POJO类型
@ResponseBody
@RequestMapping("/quick2")
public void save2(User user) throws IOException {
System.out.println(user);
}
// 获取数组类型
@ResponseBody
@RequestMapping("/quick3")
public void save3(@RequestParam("strs")String[] strs) throws IOException {
// 由于直接打印数组时只会打印出其地址,所以将其转为List集合输出到控制台
System.out.println(Arrays.asList(strs));
}
// 获取集合类型
@ResponseBody
@RequestMapping("/quick4")
public void save4(VO vo) throws IOException {
System.out.println(vo);
}
}
此时相等于获取POJO类型:所以请求参数名称要与该POJO类(即VO类)中的属性名一致,而在VO类中该属性userList
为一个集合,所以不仅要与userList
一致,更要进一步与该集合里面存储的对象User的属性名样一致
Step3:以页面为例(post方式提交)web项目核心目录(即webapp
)下创建一个form.jsp
页面,代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--将表单提交到控制器映射地址下的/user/quick4,也就是UserController类下的save4业务方法中--%>
<form action="/SpringMvcThree/user/quick4" method="post">
<%--将第一个请求参数提交到userList集合中第一个元素的name属性中--%>
<input type="text" name="userList[0].name"><br/>
<%--将第一个请求参数提交到userList集合中第二个元素的age属性中--%>
<input type="text" name="userList[0].age"><br/>
<input type="text" name="userList[0].name"><br/>
<input type="text" name="userList[0].age"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
注意:因为请求参数名称要与VO类中的集合属性名一致,又因为集合中存储的是POJO类对象(即User),而User类中有两个属性name
和age
,所以请求参数名称要与集合名.POJO类属性名
一致,所以表单中name属性值为userList[0].name
,它的含义就是集合中第一个元素的name属性。
运行后截图如下所示:
<img alt="image-20241122145555021">
@RequestBody
注解就可以直接接收集合数据而不需要使用POJO进行封装Step1:在web项目核心目录(即webapp
)下创建js目录,引入jquery源码文件jquery-3.7.1.js
(官网自行下载)
Step2:web项目核心目录(即webapp
)下创建一个ajax.jsp
文件,并在该文件中引入jquery的源码文件,最终代码如下:
<%--
Created by IntelliJ IDEA.
User: 10195
Date: 2024/11/22
Time: 16:04
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<script src="js/jquery-3.7.1.js"></script>
<script>
// 创建核心对象
var userList = new Array();
userList.push({name:"zhangsna", age:15});
userList.push({name:"lisi", age:16});
$.ajax({
type:"POST",
url:"user/quick5",
data:JSON.stringify(userList),
contentType:"application/json;charset=utf-8"
});
</script>
</body>
</html>
Step3:在springMVC的核心配置文件中添加如下代码:
原因:在SpringMVC中默认会拦截对所有资源的请求,包括静态资源;若不单独配置则会使静态资源请求被误认为是需要交给核心前端控制器处理的业务请求,此时由于前端控制器无法找到与之对应的业务方法,从而导致资源无法正确加载,所以需要在SpringMVC的核心配置文件中对这些资源进行开放访问。
<!--配置静态资源的路径映射,开放某些资源的访问-->
<mvc:resources mapping="/js/**" location="/js/"/>
springMVC的核心配置文件完整代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--配置Controller层的注解的组件扫描-->
<context:component-scan base-package="at.guigu.controller"></context:component-scan>
<!--等同于
<context:component-scan base-package="at.guigu">
type指定要扫描的内容为注解,expression指定要扫描的对应注解的全限定名
只扫描at.guigu包下有@Controller注解的类
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
-->
<!--配置内部资源视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--将InternalResourceViewResolver类中的前缀属性prefix的值设为/jsp/-->
<property name="prefix" value="/user/"></property>
<!--将InternalResourceViewResolver类中的前缀属性suffix的值设为.jsp-->
<property name="suffix" value=".jsp"></property>
</bean>
<!--mvc的注解驱动-->
<mvc:annotation-driven/>
<!--等同于配置处理器适配器-->
<!--<bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>-->
<mvc:resources mapping="/js/**" location="/js/"/>
</beans>
Step4:在controller包下的UserController
类中添加save5
方法,完整代码如下:
package at.guigu.controller;
import at.guigu.pojo.User;
import at.guigu.pojo.VO;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
//将Usercontroller放到Spring容器中
@Controller
@RequestMapping(value = "/user")
public class UserController {
// 获取普通数据类型
@RequestMapping(value = "/quick1")
@ResponseBody
public void save1(@RequestParam("username")String username, @RequestParam("age")int age) throws IOException {
System.out.println(username);
System.out.println(age);
}
// 获取POJO类型
@ResponseBody
@RequestMapping("/quick2")
public void save2(User user) throws IOException {
System.out.println(user);
}
// 获取数组类型
@ResponseBody
@RequestMapping("/quick3")
public void save3(@RequestParam("strs")String[] strs) throws IOException {
// 由于直接打印数组时只会打印出其地址,所以将其转为List集合输出到控制台
System.out.println(Arrays.asList(strs));
}
// 获取集合类型方式一
@ResponseBody
@RequestMapping("/quick4")
public void save4(VO vo) throws IOException {
System.out.println(vo);
}
// 获取集合类型方式二
@ResponseBody
@RequestMapping("/quick5")
public void save5(@RequestBody List<User> userList) throws IOException {
System.out.println(userList);
}
}
<img alt="image-20241122162512241">
注意
通过<mvc:resources>
标签进行资源路径配置标签常用属性如下:
<mvc:resources> 标签属性 |
解释 |
---|---|
mapping | 告诉 Spring MVC,当用户请求某些特定 URL 时,这些请求是访问静态资源,而不是交给控制器处理。/js/** 代表js后可以是多级url地址 |
location | 定义资源在服务器上的实际存放位置 |
由于每开放一种静态资源就要写一个该标签代码,所以可用如下代码代替
<mvc:default-servlet-handler/>
解释: 在SpringMVC中默认会拦截对所有资源的请求,包括静态资源;若不单独配置则会使静态资源请求被误认为是需要交给核心前端控制器处理的业务请求,此时由于前端控制器无法找到与之对应的业务方法,从而导致资源无法正确加载,此时就会交由Tomcat来找对应的静态资源,从而使得资源正确加载
简要总结: SpringMVC框架无法找到对应资源时就会让原始容器Tomcat去找静态资源
开放对图片、jquery文件等静态资源访问的代码示例如下
<!--配置静态资源的路径映射,开放某些资源的访问-->
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/img/**" location="/img/"/>
等同于
<mvc:default-servlet-handler/>
当使用POST请求时,数据会出现乱码问题(可详见使用POJO类进行集合封装的代码示例),解决方法:
在web项目核心目录(即webapp
)下的WEB-INF
中的web.xml中配置一个全局过滤器来进行编码的过滤,代码如下
注意:<filter>
以及<filter-mapping>
标签需要写到<linstener>
标签前,因为web.xml中有严格的标签顺序
<!--全局过滤器-->
<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>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
此时再次运行使用POJO类进行集合封装的代码示例后就不会出现乱码问题了,运行截图如下
<img alt="image-20241122170210417">
@requestParam注解属性 | 解释 |
---|---|
value |
请求参数的名称 |
required |
该注解指定的请求参数是否必须存在,默认为true,提交时若该参数不存在则会报错 |
defaultValue |
当该注解没有指定请求参数时,则使用指定默认值 |
定义:当请求参数的名称与Controller控制器中对应的业务方法的参数名称不一致时,将它们显式的绑定到一块
<img alt="image-20241122170730432">
代码示例如下:
在controller包下的UserController
类中添加save6
方法,完整代码如下:
package at.guigu.controller;
import at.guigu.pojo.User;
import at.guigu.pojo.VO;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
//将Usercontroller放到Spring容器中
@Controller
@RequestMapping(value = "/user")
public class UserController {
// 获取普通数据类型
@RequestMapping(value = "/quick1")
@ResponseBody
public void save1(@RequestParam("username")String username, @RequestParam("age")int age) throws IOException {
System.out.println(username);
System.out.println(age);
}
// 获取POJO类型
@ResponseBody
@RequestMapping("/quick2")
public void save2(User user) throws IOException {
System.out.println(user);
}
// 获取数组类型
@ResponseBody
@RequestMapping("/quick3")
public void save3(@RequestParam("strs")String[] strs) throws IOException {
// 由于直接打印数组时只会打印出其地址,所以将其转为List集合输出到控制台
System.out.println(Arrays.asList(strs));
}
// 获取集合类型方式一
@ResponseBody
@RequestMapping("/quick4")
public void save4(VO vo) throws IOException {
System.out.println(vo);
}
// 获取集合类型方式二
@ResponseBody
@RequestMapping("/quick5")
public void save5(@RequestBody List<User> userList) throws IOException {
System.out.println(userList);
}
// 测试@RequestParam注解
@RequestMapping(value = "/quick1")
@ResponseBody
public void save6(@RequestParam(value = "name", required = false, defaultValue = "zhangzhang")String username) throws IOException {
System.out.println(username);
}
}
请求参数名与业务方法的参数名不一致
<img alt="image-20241122172006564">
不写请求参数,则会将指定默认值赋值给业务方法对应的参数
<img alt="image-20241122172241307">
Restful定义
Restful时一种软件架构风格、设计风格,而不是标准
它只是提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件
基于该风格的软件会更简洁更有层次,更易实现缓存机制等
可以利用Restful风格来省略请求参数名的书写,比如
http://localhost:8080/SpringMvcThree/user/quick6?name=zhangsan
可改写为
http://localhost:8080/SpringMvcThree/user/quick6/zhangsan
当使用Restful风格来获取请求参数时,需要在方法的@RequestMapping
注解中用占位符指明映射地址后的为请求参数;同时用@PathVariable
注解来修饰业务方法中的参数,且该注解的value值要与@RequestMapping
注解中的占位符名称一致
<img alt="image-20241122174110784">
Restful风格请求使用的是url+请求方式
来表示一次请求目的,Http协议中有4种操作方式:
GET
:用于获取资源
/user/1 GET
:获取id=1的userPOST
:用于新建资源
/user POST
:新增userPUT
:用于更新资源
/user/1 PUT
:更新id=1的userDELETE
:用于删除资源
/user/1 DELETE
:删除id=1的user在controller包下创建UserControllerTwo
类,并在该类中添加save1
方法,完整代码如下:
package at.guigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/user2")
public class UserControllerTwo {
// Restful风格的请求参数
@ResponseBody
@RequestMapping("/quickk1/{name}")
public void save1(@PathVariable(value = "name") String userName) {
System.out.println(userName);
}
}
<img alt="image-20241122175202893">
Converter
接口的转换器类<annotation-driven>
(即mvc的注解驱动)标签中引用转换器类@RequestParam
注解本代码示例以日期类型为例:将日期封装为yyyy-MM-dd形式
注意:日期默认封装格式为yyyy/MM/dd形式,若请求参数中为yyyy-MM-dd形式则会报错,所以本示例是将其转换为常用的yyyy-MM-dd形式
Step1: 创建一个与三层架构包同级的converter包,然后定义一个实现Converter
接口的类DataConverter
,并定义该类的泛型为<String, Date>
,然后重写其中的convert
方法,在该方法中将日期转换成指定格式的日期对象并返回
org.springframework.core.convert.converter.Converter
包下的<String, Date>
中第一个参数String
为请求参数字符串;第二个参数Date
为要转换到的类型convert
方法的参数为客户端的请求参数package at.guigu.converter;
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DataConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
// 将日期字符串转换成指定的日期对象并返回
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = format.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
Step2: 在SpringMVC的核心配置文件中声明转换器类,核心配置文件代码如下
<!--声明转换器类-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="at.guigu.converter.DataConverter"></bean>
</list>
</property>
</bean>
Step3: 在SpringMVC的核心配置文件中用<annotation-driven>
标签中的conversion-service
属性引用转换器类的id,代码如下
<mvc:annotation-driven conversion-service="conversionService"/>
SpringMVC的核心配置文件代码如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--配置Controller层的注解的组件扫描-->
<context:component-scan base-package="at.guigu.controller"></context:component-scan>
<!--等同于
<context:component-scan base-package="at.guigu">
type指定要扫描的内容为注解,expression指定要扫描的对应注解的全限定名
只扫描at.guigu包下有@Controller注解的类
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
-->
<!--配置内部资源视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--将InternalResourceViewResolver类中的前缀属性prefix的值设为/jsp/-->
<property name="prefix" value="/user/"></property>
<!--将InternalResourceViewResolver类中的前缀属性suffix的值设为.jsp-->
<property name="suffix" value=".jsp"></property>
</bean>
<!--mvc的注解驱动-->
<!--利用conversion-service来引用转换器类,属性值为转换器对应的id-->
<mvc:annotation-driven conversion-service="conversionService"/>
<!--等同于配置处理器适配器-->
<!--<bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>-->
<!--配置静态资源的路径映射,让 Spring MVC 可以处理静态文件-->
<mvc:default-servlet-handler/>
<!--声明转换器类-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="at.guigu.converter.DataConverter"></bean>
</list>
</property>
</bean>
</beans>
Step4: 在controller包下创建UserControllerThree
类并添加save1
方法,完整代码如下:
package at.guigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.util.Date;
@Controller
@RequestMapping("/user3")
public class UserControllerThree {
@ResponseBody
@RequestMapping("/quickk1")
public void save1(@RequestParam("date") Date date) throws IOException {
System.out.println(date);
}
}
运行截图如下
<img alt="image-20241122212829005">
SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用对象有
HttpServletRequest
HttpServletResponse
HttpSession
获取方式: 只需要将想要的Servlet的API作为控制器对应方法的参数即可
测试示例
在controller包下创建UserControllerFour
类并添加save1
方法,完整代码如下:
package at.guigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@Controller
@RequestMapping("/user4")
public class UserControllerFour {
@ResponseBody
@RequestMapping("/quick1")
public void save1(HttpServletRequest req, HttpServletResponse res, HttpSession hs) throws Exception{
System.out.println(req);
System.out.println(res);
System.out.println(hs);
}
}
<img alt="image-20241122215345240">
利用@RequestHeader
注解来获取请求头信息,相当于web阶段所学的request.getHeader(name)
方法,可详见会话跟踪技术部分内容
@RequestHeader 注解属性 |
解释 |
---|---|
value |
请求头名称 |
required |
是否必须携带该请求头,默认为true,即必须携带该请求头才能访问这个资源 |
测试示例
在controller包下创建UserControllerFive
类并添加save1
方法,代码如下:
package at.guigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/user5")
public class UserControllerFive {
@ResponseBody
@RequestMapping("/quick1")
public void save1() throws Exception{
}
}
运行后通过开发者工具可看到请求头信息,如图所示
<img alt="image-20241122220250237">
假设现在获取请求头user-agent
的信息,则代码如下:
package at.guigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/user5")
public class UserControllerFive {
@ResponseBody
@RequestMapping("/quick1")
public void save1(@RequestHeader(value = "User-Agent", required = false) String user_agent) throws Exception{
System.out.println(user_agent);
}
}
<img alt="image-20241122220715639">
@RequestHeader
只能根据如图所示1中的请求头来获取2中的信息,而有些请求头后的信息有键值对,比如Cookie中又有很多键值对Cookie,此时若想获取Cookie里面的小Cookie的话@RequestHeader
注解就会失效。
<img alt="image-20241122221937185">
Cookie不只有一个,所以属于特殊请求头,如图所示
<img alt="image-20241122221229221">
@CookieValue
获取指定Cookie的值
@CookieValue 注解属性 |
解释 |
---|---|
value |
Cookie名称 |
required |
是否必须携带该Cookie,默认为true,即必须携带该Cookie才能访问这个资源 |
代码示例(此处以JSESSIONID
这个Cookie为例)
在UserControllerFive
类中添加save2
方法,代码如下:
package at.guigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/user5")
public class UserControllerFive {
// 获取普通请求头
@ResponseBody
@RequestMapping("/quick1")
public void save1(@RequestHeader(value = "User-Agent", required = false) String user_agent) throws Exception{
System.out.println(user_agent);
}
// 获取特殊请求头:获取指定Cookie
@ResponseBody
@RequestMapping("/quick2")
public void save2(@CookieValue(value = "JSESSIONID", required = false) String jessionid) throws Exception{
System.out.println(jessionid);
}
}
<img alt="image-20241122221638052">
注意:除以上注解之外,也可使用通用方式(即使用HttpServletRequest
接口中的方法)来获取请求头信息,可详见会话跟踪技术部分内容
文件上传客户端三要素
type="file"
post
enctype="mulipart/form-data"
<img alt="image-20241123125316316">
注意
当form表单修改为多部分表单时,request.getParameter(String name)
会失效,因为该方法只能获取单个参数值
默认情况下,enctype="application/x-www-form-urlencoded"
,此时form表单的正文内容是key=value&key=value&key=value
当enctype="mulipart/form-data"
时,请求正文内容就会变成多部分形式,此时能够获取表单的所有数据,如图所示
<img alt="image-20241123133207163">
文件上传步骤
在pom.xml文件中导入坐标:fileupload和io两个坐标
<!--fileupload坐标-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.5</version>
</dependency>
<!--io坐标-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.17.0</version>
</dependency>
在SpringMVC的核心配置文件中配置文件上传解析器
编写文件上传代码
单文件上传和多文件上传的公共步骤(后续代码演示不在演示公共步骤)
在pom.xml文件中导入fileupload和io两个坐标,文件完整代码如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.example</groupId>
<artifactId>SpringMvcDemo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>SpringMvcThree</artifactId>
<packaging>war</packaging>
<name>SpringMvcThree Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!--===================Spring相关坐标=======================-->
<!--spring坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.6</version>
</dependency>
<!--spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.25.RELEASE</version>
</dependency>
<!--spring-test坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.1.6</version>
<scope>test</scope>
</dependency>
<!--Annotation坐标-->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!--jsp-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<!--===================SpringMVC相关坐标=======================-->
<!--spring-webmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.25.RELEASE</version>
</dependency>
<!--jackson-core-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.17.1</version>
</dependency>
<!--jackson-databind-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.1</version>
</dependency>
<!--jackson-annotations-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.17.1</version>
</dependency>
<!--=====================数据库相关坐标=========================-->
<!--mysql坐标-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!--druid坐标-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.18</version>
</dependency>
<!--c3p0坐标-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<!--=====================MyBatis相关坐标=========================-->
<!--spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.1.10</version>
</dependency>
<!--mybatis-spring-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>
<!--MyBatis坐标-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.16</version>
</dependency>
<!--=====================文件上传相关坐标=========================-->
<!--fileupload坐标-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.5</version>
</dependency>
<!--io坐标-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.17.0</version>
</dependency>
</dependencies>
<build>
<finalName>SpringMvcThree</finalName>
<plugins>
<!-- Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
</plugin>
<!--<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!– maven插件版本 –>
<version>3.13.0</version>
<configuration>
<!– Java版本 –>
<source>21</source>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>-->
</plugins>
</build>
</project>
在SpringMVC的核心配置文件中配置文件上传解析器,代码如下
<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--所上传文件的编码类型-->
<property name="defaultEncoding" value="UTF-8"/>
<!--所上传的单个文件的大小-->
<property name="maxUploadSizePerFile" value="500000"/>
<!--所上传的总文件的大小-->
<property name="maxUploadSize" value="5000000"/>
</bean>
SpringMVC核心配置文件代码如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--配置Controller层的注解的组件扫描-->
<context:component-scan base-package="at.guigu.controller"></context:component-scan>
<!--等同于
<context:component-scan base-package="at.guigu">
type指定要扫描的内容为注解,expression指定要扫描的对应注解的全限定名
只扫描at.guigu包下有@Controller注解的类
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
-->
<!--配置内部资源视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--将InternalResourceViewResolver类中的前缀属性prefix的值设为/jsp/-->
<property name="prefix" value="/user/"></property>
<!--将InternalResourceViewResolver类中的前缀属性suffix的值设为.jsp-->
<property name="suffix" value=".jsp"></property>
</bean>
<!--mvc的注解驱动-->
<mvc:annotation-driven conversion-service="conversionService"/>
<!--等同于配置处理器适配器-->
<!--<bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>-->
<!--配置静态资源的路径映射,让 Spring MVC 可以处理静态文件-->
<mvc:default-servlet-handler/>
<!--声明转换器类-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="at.guigu.converter.DataConverter"></bean>
</list>
</property>
</bean>
<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--所上传文件的编码类型-->
<property name="defaultEncoding" value="UTF-8"/>
<!--所上传的单个文件的大小-->
<property name="maxUploadSizePerFile" value="500000"/>
<!--所上传的总文件的大小-->
<property name="maxUploadSize" value="5000000"/>
</bean>
</beans>
在web项目核心目录(即webapp
)下创建文件upload.jsp
,文件代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/SpringMvcThree/user6/quick1" method="post" enctype="multipart/form-data">
名称<input type="text" name = "username"><br/>
文件<input type="file" name = "uploadFile"><br/>
<input type="submit" value="提交"><br/>
</form>
</body>
</html>
在controller包下创建UserControllerSix
类并添加save1
方法,完整代码如下:
package at.guigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
@Controller
@RequestMapping("/user6")
public class UserControllerSix {
@ResponseBody
@RequestMapping("/quick1")
public void save1(@RequestParam("username") String username, @RequestParam("uploadFile") MultipartFile uploadFile) throws IOException {
System.out.println(username);
System.out.println(uploadFile);
// 获取上传文件名
String originalFilename = uploadFile.getOriginalFilename();
// 保存文件
uploadFile.transferTo(new File("F:\\node\\idea\\test\\" + originalFilename));
}
}
<img alt="image-20241123142718400">
注意
业务方法的参数名要与请求参数名一致,由于表单上传的文件会被SpringMVC封装成一个MultipartFile对象,且对象名为表单中所定义的文件的name属性值,所以对应业务方法中第二个参数的名为uploadFile
单文件上传时,参数不会自动映射匹配,解决方法有两种:
在pom.xml文件中添加maven插件
使用@RequestParam
注解
以上两种方式详见获取普通数据类型的代码示例部分
方式一:文件name属性的属性名不一样
在web项目核心目录(即webapp
)下创建文件upload2.jsp
,文件代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/SpringMvcThree/user6/quick2" method="post" enctype="multipart/form-data">
名称<input type="text" name = "username"><br/>
文件1<input type="file" name = "uploadFile1"><br/>
文件2<input type="file" name = "uploadFile2"><br/>
<input type="submit" value="提交"><br/>
</form>
</body>
</html>
在UserControllerSix
类中添加save2
方法,完整代码如下:
package at.guigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
@Controller
@RequestMapping("/user6")
public class UserControllerSix {
// 单文件上传
@ResponseBody
@RequestMapping("/quick1")
public void save1(@RequestParam("username") String username, @RequestParam("uploadFile") MultipartFile uploadFile) throws IOException {
System.out.println(username);
System.out.println(uploadFile);
// 获取上传文件名
String originalFilename = uploadFile.getOriginalFilename();
// 保存文件
uploadFile.transferTo(new File("F:\\node\\idea\\test\\" + originalFilename));
}
//多文件上传 方式一
@ResponseBody
@RequestMapping("/quick2")
public void save2(@RequestParam("username") String username, @RequestParam("uploadFile1") MultipartFile uploadFile1, @RequestParam("uploadFile2") MultipartFile uploadFile2) throws IOException {
System.out.println(username);
System.out.println(uploadFile1);
System.out.println(uploadFile2);
// 获取上传文件1的文件名
String originalFilename1 = uploadFile1.getOriginalFilename();
// 保存文件1
uploadFile1.transferTo(new File("F:\\node\\idea\\test\\" + originalFilename1));
// 获取上传文件2的文件名
String originalFilename2 = uploadFile2.getOriginalFilename();
// 保存文件2
uploadFile2.transferTo(new File("F:\\node\\idea\\test\\" + originalFilename2));
}
}
<img alt="image-20241123144117411">
方式二:文件name属性的属性名一样,此时用MultipartFile对象数组
在web项目核心目录(即webapp
)下创建文件upload3.jsp
,文件代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/SpringMvcThree/user6/quick3" method="post" enctype="multipart/form-data">
名称<input type="text" name = "username"><br/>
文件1<input type="file" name = "uploadFiles"><br/>
文件2<input type="file" name = "uploadFiles"><br/>
<input type="submit" value="提交"><br/>
</form>
</body>
</html>
在UserControllerSix
类中添加save2
方法,完整代码如下:
package at.guigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
@Controller
@RequestMapping("/user6")
public class UserControllerSix {
// 单文件上传
@ResponseBody
@RequestMapping("/quick1")
public void save1(@RequestParam("username") String username, @RequestParam("uploadFile") MultipartFile uploadFile) throws IOException {
System.out.println(username);
System.out.println(uploadFile);
// 获取上传文件名
String originalFilename = uploadFile.getOriginalFilename();
// 保存文件
uploadFile.transferTo(new File("F:\\node\\idea\\test\\" + originalFilename));
}
//多文件上传 方式一
@ResponseBody
@RequestMapping("/quick2")
public void save2(@RequestParam("username") String username, @RequestParam("uploadFile1") MultipartFile uploadFile1, @RequestParam("uploadFile2") MultipartFile uploadFile2) throws IOException {
System.out.println(username);
System.out.println(uploadFile1);
System.out.println(uploadFile2);
// 获取上传文件1的文件名
String originalFilename1 = uploadFile1.getOriginalFilename();
// 保存文件1
uploadFile1.transferTo(new File("F:\\node\\idea\\test\\" + originalFilename1));
// 获取上传文件2的文件名
String originalFilename2 = uploadFile2.getOriginalFilename();
// 保存文件2
uploadFile2.transferTo(new File("F:\\node\\idea\\test\\" + originalFilename2));
}
// 多文件上传 方式二
@ResponseBody
@RequestMapping("/quick3")
public void save3(@RequestParam("username") String username, @RequestParam("uploadFiles") MultipartFile[] uploadFiles) throws IOException {
System.out.println(username);
for (MultipartFile file : uploadFiles) {
System.out.println(file);
// 获取上传文件名
String originalFilename = file.getOriginalFilename();
// 保存文件
file.transferTo(new File("F:\\node\\idea\\test\\" + originalFilename));
}
}
}
<img alt="image-20241123144951578">
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。