目录
2.3. ServletAPI向request域对象共享数据
2.4. ModelAndView向request域对象共享数据
1. SpringMVC 获取请求参数
在SpringMVC中,获取请求参数是指从客户端(如浏览器)发送的HTTP请求中提取信息的过程。这些信息可以是用户在浏览器地址栏输入的查询字符串中的键值对,也可以是通过表单提交的数据。
当用户访问一个URL时,URL后面经常跟着一些由问号(?)开始,由一系列键值对(param1=value1¶m2=value2)组成的查询字符串。在SpringMVC中,你可以通过特定的注解或者方法来获取这些参数的值。
例如,如果你有一个查询字符串example.com?name=Kimi&age=5
,你想要获取name
的值,你可以在你的控制器方法中使用@RequestParam
注解,如下所示:
public String getUserInfo(@RequestParam("name") String name) {
// 在这里,name变量将会包含"Kimi"
// ...
}
如果参数是嵌入在URL路径中的,比如example.com/user/1
,你可以使用@PathVariable
注解来获取路径中的参数值:
public String getUserById(@PathVariable("userId") String userId) {
// 在这里,userId变量将会包含"1"
// ...
}
SpringMVC会自动将请求中相应的参数值映射到你的控制器方法的参数上。如果请求中没有相应的参数,而你又没有提供默认值,那么SpringMVC会抛出一个异常。
1.1. 通过ServletAPI获取
在SpringMVC框架中,获取客户端发送的请求参数是一项基本而重要的操作。其中一种方式是利用HttpServletRequest
对象,该对象是Servlet API的一部分,提供了对当前HTTP请求的封装和访问。
当你在控制器方法中添加HttpServletRequest
作为参数时,你可以直接调用该对象的方法来获取请求中的数据。以下是一个简化的示例,用以解释如何操作:
<a th:ref="@{/testParam}(username='admin', password='123456')">
测试ServletAPI获取请求参数 </a>
@RequestMapping("/testParam")
public String testParam(HttpServletRequest request){
// 从请求中获取名为"username"的参数值,并将其存储在局部变量username中
String username = request.getParameter("username");
// 从请求中获取名为"password"的参数值,并将其存储在局部变量password中
String password = request.getParameter("password");
// 打印获取到的用户名和密码
System.out.println("username:" + username + ", password:" + password);
// 返回视图名称,这里为"success"
return "success";
}
在这个例子中,我们使用了@RequestMapping
注解来映射一个特定的URL路径("/testParam")到控制器方法testParam
。当有请求发送到这个路径时,SpringMVC会调用这个方法。
在方法内部,我们通过调用HttpServletRequest
对象的getParameter
方法来获取请求参数。getParameter
方法接受一个参数名称作为输入,并返回与该名称对应的请求参数的值。
例如,如果我们的请求URL是http://example.com/testParam?username=admin&password=123456
,那么request.getParameter("username")
将返回字符串"admin",request.getParameter("password")
将返回字符串"123456"。
最后,方法返回一个字符串"success",这通常对应于一个视图名称,SpringMVC的视图解析器会根据这个名称来渲染相应的页面。
1.2. 控制器方法形参获取
在SpringMVC中,除了通过HttpServletRequest
对象获取请求参数外,还可以直接通过控制器方法的参数来获取。这种方式更为直观和便捷。
首先,假设你有一个HTML页面,其中包含一个链接,如下所示:
<a th:href="@{/testParam(username='admin',password=123456)}">
测试获取请求参数-->/testParam </a>
这里使用了Thymeleaf模板引擎(由th:
前缀标识)来构建一个指向/testParam
路径的链接,并且预先设置了username
和password
两个请求参数的值。当用户点击这个链接时,浏览器会向服务器发送一个包含这两个参数的GET请求。
接下来,在SpringMVC的控制器中,你可以定义一个方法来处理这个请求,如下所示:
@RequestMapping("/testParam")
public String testParam(String username, String password){
System.out.println("username:" + username + ", password: " + password);
return "success";
}
在这个例子中,@RequestMapping
注解将/testParam
路径的请求映射到了testParam
方法。方法中的两个参数username
和password
与请求中的参数同名。当请求到达时,SpringMVC会自动将请求参数的值赋给控制器方法的对应参数。
现在,如果你的请求中有多个同名参数,例如:
http://example.com/testParam?username=Kimi&username=Admin
在这种情况下,如果你想接收所有的username
参数,你可以在控制器方法中使用字符串数组来接收:
@RequestMapping("/testParam")
public String testParam(String[] usernames, String password){
System.out.println("usernames: " + Arrays.toString(usernames) + ", password: " + password);
return "success";
}
这里,usernames
是一个字符串数组,它将包含所有名为username
的请求参数的值。Arrays.toString
方法用于将数组转换为字符串,以便于打印输出。
如果你仍然只想要一个单一的字符串参数,即使有多个同名参数,你可以保持使用单个字符串类型的参数。在这种情况下,SpringMVC会取最后一个同名参数的值作为该参数的值。在上面的例子中,username
参数的值将会是"Admin"
,因为这是请求中username
参数的最后一个值。
1.3. @RequestParam
@RequestParam
是在Spring框架中使用的注解,它允许你将HTTP请求的参数映射到你的控制器方法的参数上。这个注解主要用于处理请求参数,提供了一种灵活的方式来获取和验证这些参数。
这个注解的三个主要属性是
value
:这个属性用于指定HTTP请求参数的名称。当你在方法的参数前使用@RequestParam
注解时,可以通过value
属性来明确指定你想要从请求中获取哪个参数。例如,如果你有一个名为page
的请求参数,你可以这样使用@RequestParam
:
@GetMapping("/users")
public String getUsers(@RequestParam(value = "page") int pageNumber) {
// ...
}
在这个例子中,value="page"
告诉Spring框架,你想要将请求中的page
参数映射到pageNumber
变量上。
required
:这个属性是一个布尔值,用于指示请求中是否必须包含指定的参数。默认情况下,required
属性的值是true
,这意味着请求必须包含value
属性指定的参数。如果请求中没有这个参数,或者参数的值为空,那么就会抛出一个400错误,提示参数缺失。
如果将required
属性设置为false
,那么这个参数就不再是必须提供的。在这种情况下,如果请求中没有提供该参数,或者参数值为空,那么方法的参数将会被赋予null
值。
defaultValue
:这个属性允许你为参数指定一个默认值。无论required
属性是true
还是false
,如果请求中没有提供value
指定的参数,或者参数值为空,那么方法的参数将会被赋予defaultValue
属性指定的值。
例如:
@GetMapping("/users")
public String getUsers(@RequestParam(value = "page", required = false,
defaultValue = "1") int pageNumber) {
// ...
}
在这个例子中,如果请求中没有page
参数,或者page
参数的值为空,那么pageNumber
将会被赋予默认值1
。
当然可以。以下是一个使用@RequestParam
注解的简单Spring MVC控制器方法的例子。这个例子中,我们将创建一个处理HTTP GET请求的方法,该方法接受一个可选的查询参数sort
,用于指定返回用户列表的排序方式。
举个栗子
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
// 定义一个处理"/users"路径GET请求的方法
@GetMapping("/users")
public String getUsers(
// 使用@RequestParam注解来绑定请求参数到方法的参数
@RequestParam(value = "sort", required = false,
defaultValue = "name") String sort) {
// 这个方法将会根据sort参数的值返回不同的字符串
if ("name".equals(sort)) {
return "Users are sorted by name.";
} else if ("age".equals(sort)) {
return "Users are sorted by age.";
} else {
return "Invalid sorting parameter.";
}
}
}
在这个例子中,我们定义了一个名为getUsers
的方法,它映射到HTTP GET请求的/users
路径。这个方法有一个参数sort
,我们使用@RequestParam
注解来绑定请求中的sort
查询参数到这个方法的参数上。
value = "sort"
:这指定了请求参数的名称,即我们在URL中使用的名称。例如,在浏览器中访问http://localhost:8080/users?sort=name
时,sort
就是请求参数的名称。required = false
:这表明sort
参数是可选的。如果请求中没有提供这个参数,那么方法的sort
参数将会是null
。defaultValue = "name"
:这为sort
参数提供了一个默认值。如果请求中没有sort
参数,或者参数值为空字符串,那么sort
参数将会被赋予默认值"name"
。
所以,如果你访问http://localhost:8080/users
,没有提供sort
参数,那么getUsers
方法将会返回"Users are sorted by name."
,因为sort
参数的默认值是"name"
。如果你提供了sort
参数,比如访问http://localhost:8080/users?sort=age
,那么它将会返回"Users are sorted by age."
。如果你提供了一个无效的参数,比如http://localhost:8080/users?sort=height
,那么它将会返回"Invalid sorting parameter."
。
1.4. @RequestHeader
@RequestHeader
注解在Spring框架中用于将HTTP请求头信息映射到控制器方法的参数上。这个注解允许你访问和处理请求头中的信息,例如认证令牌、客户端信息等。用法同@RequestParam。
@RequestHeader
注解三个属性的简单解释
value
:这个属性用于指定HTTP请求头的名称。当你在方法的参数前使用@RequestHeader
注解时,通过value
属性来明确指出你想要获取哪个请求头的值。例如,如果你想获取名为Authorization
的请求头,你可以这样使用@RequestHeader
:
@GetMapping("/resource")
public String getResource(
@RequestHeader(value = "Authorization") String authHeader) {
// ...
}
在这个例子中,value="Authorization"
告诉Spring框架,你想要将请求中的Authorization
请求头的值映射到authHeader
变量上。
required
:这个属性是一个布尔值,用来指示请求头是否必须存在于请求中。默认情况下,required
的值是true
,这意味着请求必须包含value
属性指定的请求头。如果请求中缺少这个请求头,那么将会抛出一个异常。
如果将required
属性设置为false
,那么这个请求头就不是必须提供的。在这种情况下,如果请求中没有提供该请求头,那么方法的参数将会被赋予null
值。
defaultValue
:这个属性允许你为请求头指定一个默认值。无论required
属性是true
还是false
,如果请求中没有提供value
指定的请求头,或者请求头的值为空,那么方法的参数将会被赋予defaultValue
属性指定的值。
举个栗子,创建一个处理HTTP GET请求的方法,该方法接受一个名为User-Agent
的请求头,这个请求头通常包含了发出请求的客户端(如浏览器)的信息。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ResourceController {
// 定义一个处理"/resource"路径GET请求的方法
@GetMapping("/resource")
public String getResource(@RequestHeader(value = "User-Agent",
required = false, defaultValue = "Unknown") String userAgent) {
// 这个方法将会根据User-Agent请求头发送不同的响应
return "The resource was requested by: " + userAgent;
}
}
在这个例子中,我们定义了一个名为getResource
的方法,它映射到HTTP GET请求的/resource
路径。这个方法有一个参数userAgent
,我们使用@RequestHeader
注解来绑定请求中的User-Agent
请求头到这个方法的参数上。
value="User-Agent"
:这指定了请求头的名称,即我们想要获取的请求头的名称。在这个例子中,我们关注的是User-Agent
请求头。required = false
:这表明User-Agent
请求头不是必须提供的。如果请求中没有这个请求头,或者请求头的值为空字符串,那么userAgent
参数将会是null
。defaultValue = "Unknown"
:这为User-Agent
请求头提供了一个默认值。如果请求中没有提供User-Agent
请求头,或者请求头的值为空,那么userAgent
参数将会被赋予默认值"Unknown"
。
所以,如果你访问/resource
路径,并且你的浏览器提供了User-Agent
请求头,那么getResource
方法将会返回包含这个请求头信息的字符串。如果你的请求没有包含User-Agent
请求头,或者请求头的值为空,那么方法将会返回一个字符串,表明请求是由一个未知的客户端发起的。
1.5. @CookieValue
@CookieValue
注解在Spring框架中用于将HTTP请求中的cookie值映射到控制器方法的参数上。这个注解允许你轻松地读取和处理客户端发送的cookie数据。
@CookieValue
注解三个属性:
value
:这个属性用来指定cookie的名称。当你使用@CookieValue
注解时,通过value
属性来明确你想要从请求的cookie中获取哪个cookie的值。例如,如果你的请求中有一个名为sessionToken
的cookie,你可以这样使用@CookieValue
:
@GetMapping("/dashboard")
public String getDashboard(
@CookieValue(value = "sessionToken") String sessionToken) {
// ...
}
在这个例子中,value="sessionToken"
告诉Spring框架,你想要将名为sessionToken
的cookie的值映射到sessionToken
变量上。
required
:这个属性是一个布尔值,用来指示请求中是否必须包含指定的cookie。默认情况下,required
的值是true
,这意味着请求必须包含value
属性指定的cookie。如果请求中缺少这个cookie,那么将会抛出一个异常。
如果将required
属性设置为false
,那么这个cookie就不是必须提供的。在这种情况下,如果请求中没有提供该cookie,那么方法的参数将会被赋予null
值。
defaultValue
:这个属性允许你为cookie指定一个默认值。无论required
属性是true
还是false
,如果请求中没有提供value
指定的cookie,或者cookie的值为空,那么方法的参数将会被赋予defaultValue
属性指定的值。
举个栗子
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CookieController {
// 定义一个处理"/dashboard"路径GET请求的方法
@GetMapping("/dashboard")
public String showDashboard(
@CookieValue(value = "sessionToken", required = false,
defaultValue = "no_session") String sessionToken) {
// 这个方法将会根据sessionToken的值来显示不同的信息
if ("admin".equals(sessionToken)) {
return "Welcome back, Admin!";
} else if ("user".equals(sessionToken)) {
return "Hello, User!";
} else {
return "You are not logged in.";
}
}
}
定义了一个名为showDashboard
的方法,它映射到HTTP GET请求的/dashboard
路径。这个方法有一个参数sessionToken
,我们使用@CookieValue
注解来绑定请求中的名为sessionToken
的cookie的值到这个方法的参数上。
value="sessionToken"
:这指定了cookie的名称,即我们想要获取的cookie的名称。required = false
:这表明sessionToken
cookie不是必须提供的。如果请求中没有这个cookie,或者cookie的值为空字符串,那么sessionToken
参数将会是null
。defaultValue = "no_session"
:这为sessionToken
cookie提供了一个默认值。如果请求中没有提供sessionToken
cookie,或者cookie的值为空,那么sessionToken
参数将会被赋予默认值"no_session"
。
所以,如果你访问/dashboard
路径,并且你的请求包含了名为sessionToken
的cookie,那么showDashboard
方法将会根据cookie的值来返回不同的欢迎信息。如果你的请求没有包含sessionToken
cookie,或者cookie的值为空,那么方法将会返回一条信息,表明用户未登录。
1.6. 通过POJO获取请求参数
在Spring框架中,可以通过POJO(Plain Old Java Object,简单的旧Java对象)来获取HTTP请求的参数。这意味着你可以创建一个普通的Java类,其属性与请求参数的名称相匹配,Spring框架会自动将请求参数的值绑定到这个类的实例上。
举个栗子
定义一个简单的Java类User
,它将代表我们要从请求中获取的数据:
public class User {
private String username;
private String password;
private String sex;
private int age;
private String email;
// 省略getter和setter方法
}
创建一个Spring控制器,它将处理一个提交到/testpojo
路径的POST请求:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class UserController {
// 处理对/testpojo路径的POST请求
@RequestMapping(value = "/testpojo", method = RequestMethod.POST)
public String testPOJO(User user) {
// 打印User对象的信息
System.out.println(user);
// 返回一个字符串表示请求已成功处理
return "success";
}
}
在这个控制器中,我们定义了一个方法testPOJO
,它接受一个User
类型的参数。当一个POST请求到达/testpojo
路径时,Spring框架会自动将请求中的参数绑定到User
对象的属性上,前提是请求参数的名称必须与User
类中属性的名称相匹配。
假设用户通过前面提供的HTML表单提交了数据,表单中的字段如下:
<form action="/testpojo" method="post">
用户名:<input type="text" name="username" value="张三"><br>
密码:<input type="password" name="password" value="123"><br>
性别:<input type="radio" name="sex" value="男" checked>男<input type="radio" name="sex" value="女">女<br>
年龄:<input type="text" name="age" value="23"><br>
邮箱:<input type="text" name="email" value="123@qq.com"><br>
<input type="submit" value="提交">
</form>
当用户填写表单并点击提交按钮时,表单数据会被发送到服务器。Spring框架会创建一个User
对象,并根据表单中的输入字段自动填充username
、password
、sex
、age
和email
属性。
在testPOJO
方法中,我们打印出User
对象的内容,结果可能如下:
User{username='张三', password='123', sex='男', age=23, email='123@qq.com'}
最后,该方法返回一个字符串"success"
,表示请求已成功处理。这个例子展示了如何使用POJO来简化请求参数的绑定和处理过程。
1.7. 解决获取请求参数的乱码问题
在Web开发中,乱码问题通常是由于客户端和服务器端在字符编码上不匹配导致的。为了解决这个问题,我们需要确保在数据传输过程中使用统一的字符编码。在Spring MVC中,可以通过配置CharacterEncodingFilter
来解决获取请求参数时出现的乱码问题。
CharacterEncodingFilter
是Spring提供的一个过滤器,它可以强制请求和响应使用指定的字符编码,通常是UTF-8
。这个过滤器需要在web.xml
文件中进行注册,以便在请求处理之前对字符编码进行正确的设置。
下面是一个配置CharacterEncodingFilter
的例子:
<!-- 配置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>
在这个配置中,我们定义了一个名为CharacterEncodingFilter
的过滤器,并指定了它的类为org.springframework.web.filter.CharacterEncodingFilter
。我们设置了两个初始化参数:
encoding
:这个参数指定了过滤器使用的字符编码,这里我们设置为UTF-8
,这是一种广泛使用的字符编码,可以表示几乎所有语言的字符。forceResponseEncoding
:这个参数设置为true
,意味着过滤器会强制响应(即服务器发送给客户端的数据)也使用UTF-8
编码。
最后,我们通过<filter-mapping>
将过滤器映射到所有的URL路径(/*
)。
注:在Spring MVC中,处理编码的CharacterEncodingFilter
应该配置在其他过滤器之前,这是因为过滤器是按它们在web.xml
中出现的顺序来执行的。如果你有其他过滤器(比如日志记录过滤器、安全过滤器等),你需要确保CharacterEncodingFilter
是第一个执行的,以便在其他过滤器处理请求之前就设置好正确的字符编码。这样可以确保整个请求处理过程中字符编码的一致性,避免乱码问题的发生。
2. 域对象共享数据
域对象共享数据是一种在Web应用程序中常见的概念,它涉及到在不同请求之间保持和共享数据的需求。在Web开发中,由于HTTP协议是无状态的,这意味着服务器默认情况下不会记住之前的请求信息。然而,在实际应用中,我们经常需要在多个页面或者多个请求之间共享某些数据,这时候就需要用到域对象。
域对象(Session Object)是一种用于存储用户特定数据的对象,它可以跨越多个页面请求和多个会话。简单来说,就像你在商场购物,你的购物车会随着你在不同商店之间走动而一直跟着你,直到你完成购物。在Web应用程序中,域对象就像这个购物车,它允许服务器跟踪用户的状态和信息,如登录信息、用户偏好设置等。
2.1. 三大域对象
域对象是一种用于存储和传递数据的对象,在不同的范围之间传递数据。不同的域对象代表不同的范围,并且共享数据的范围也不同。
在web项目中,我们通常会使用请求域、会话域和应用域这三种域对象。
举个简单的例子就是:
请求域对象是HttpServletRequest:它与单个请求关联,用于在处理一个特定请求的过程中传递数据。这个对象是临时存在的,一旦请求结束就不存在了。就像张三的工位一样,只有他可以使用工位上的东西,一旦他离开工位,其他人就无法使用了。
会话域对象是HttpSession:这个对象与用户会话关联,用于在整个会话期间传递数据,可以跨越多个请求。它与办公室的公共区有些相似,因为办公室内的所有人都可以访问这个区域,就像在同一会话中的所有人都可以访问HttpSession对象一样。
应用域对象是ServletContext:这个对象是整个Web应用程序的上下文,它可以被该应用程序中的所有用户访问。它类似于楼层走廊区,因为该楼层的所有人都可以访问这个区域,就像使用该应用程序的所有用户都可以访问ServletContext对象一样。
- 请求域
- 会话域
- 应用域
2.2. 准备工作
创建子模块SpringMVC-demo03
添加依赖
<dependencies>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.14</version>
</dependency>
<!-- 日志 -->
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-c
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.14</version>
<!--<scope>test</scope>-->
</dependency>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!--Spring5和Thymeleaf整合包-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
设置打包方式,添加 webapp,创建 web.xml 文件
设置编码过滤器
<!-- 定义web应用程序的配置 -->
<!-- 配置springMVC的编码过滤器,确保请求和响应的编码为UTF-8 -->
<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>
<!-- 指定请求和响应的编码格式为UTF-8 -->
</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>
配置springMvc的前端控制器Dispatcherservlet
<!-- 配置Spring MVC核心控制器(DispatcherServlet) -->
<servlet>
<servlet-name>springMVC</servlet-name>
<!-- 指定DispatcherServlet的全限定类名 -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 初始化参数配置 -->
<init-param>
<!-- 参数名:指定Spring MVC配置文件的位置 -->
<param-name>contextConfigLocation</param-name>
<!-- 参数值:设置Spring MVC配置文件的路径 -->
<param-value>classpath:SpringMVC-config.xml</param-value>
</init-param>
<!-- 设置DispatcherServlet在应用启动时加载的优先级,值越小,优先级越高 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置Spring MVC核心控制器的请求映射规则 -->
<servlet-mapping>
<!-- 与上述<servlet>标签中定义的<servlet-name>对应 -->
<servlet-name>springMVC</servlet-name>
<!-- URL模式:所有以'/'开头的请求都将交由该DispatcherServlet处理 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
完整 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的编码过滤器,确保请求和响应的编码为UTF-8 -->
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
<!-- 指定请求和响应的编码格式为UTF-8 -->
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
<!-- 强制响应使用指定的编码格式 -->
</init-param>
</filter>
<!-- 映射编码过滤器到所有请求 -->
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置Spring MVC核心控制器(DispatcherServlet) -->
<servlet>
<servlet-name>springMVC</servlet-name>
<!-- 指定DispatcherServlet的全限定类名 -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 初始化参数配置 -->
<init-param>
<!-- 参数名:指定Spring MVC配置文件的位置 -->
<param-name>contextConfigLocation</param-name>
<!-- 参数值:设置Spring MVC配置文件的路径 -->
<param-value>classpath:SpringMVC-config.xml</param-value>
</init-param>
<!-- 设置DispatcherServlet在应用启动时加载的优先级,值越小,优先级越高 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置Spring MVC核心控制器的请求映射规则 -->
<servlet-mapping>
<!-- 与上述<servlet>标签中定义的<servlet-name>对应 -->
<servlet-name>springMVC</servlet-name>
<!-- URL模式:所有以'/'开头的请求都将交由该DispatcherServlet处理 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
创建测试类
package com.sakurapaid.mvc.test;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class TestController {
@RequestMapping("/")
public String test()
{
return "index";
}
}
设置 Spring 配置文件--SpringMVC-config.xml
再 resource 文件夹下创建
添加组件扫描
<!-- 开启组件扫描,自动发现并注册Bean到Spring容器中 -->
<context:component-scan base-package="com.sakurapaid.mvc"/>
配置视图解析器
<!-- 配置JSP视图解析器 -->
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="1"/> <!-- 解析器的优先级 -->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
<property name="contentType" value="text/html;charset=UTF-8"/>
</bean>
<!-- 可选,配置html视图解析器 -->
<!-- 配置Thymeleaf视图解析器,用于将控制器返回的逻辑视图名解析为实际的HTML页面 -->
<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">
<!-- 设置视图的前缀,例如:如果逻辑视图名为home,则实际查找的路径为/WEB-INF/templates/home.html -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 设置视图的后缀,即视图文件的扩展名 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/> <!-- 指定模板解析模式为HTML5 -->
<property name="characterEncoding" value="UTF-8"/> <!-- 模板的字符编码 -->
</bean>
</property>
</bean>
</property>
</bean>
完整代码如下
<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring核心 beans 命名空间 -->
<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">
<!-- 开启组件扫描,自动发现并注册Bean到Spring容器中 -->
<context:component-scan base-package="com.sakurapaid.mvc"/>
<!-- 配置JSP视图解析器 -->
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="1"/> <!-- 解析器的优先级 -->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
<property name="contentType" value="text/html;charset=UTF-8"/>
</bean>
</beans>
创建前端 jsp 页面代码
根据视图解析器规则,jsp 文件存放位置应该是 WEB-INF 下的 jsp 文件夹下
test.jsp 文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>我是测试文件</h1>
</body>
</html>
配置本地服务器
注意是对应的工件部署
测试成功
2.3. ServletAPI向request域对象共享数据
2.3.1. 代码示例
index.jsp 界面代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<%--使用ServletAPI向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/RequestByServletAPI">ServletAPI</a>
</body>
</html>
Controller 控制器
package com.sakurapaid.mvc.test;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
/**
* TestController类用于处理请求。
*/
@Controller
public class TestController {
/**
* 处理"/RequestByServletAPI"请求的方法。
*
* @param request HttpServletRequest对象,用于获取请求信息和设置响应信息。
* @return 返回一个字符串"success",指示视图解析器解析成功。
*/
@RequestMapping("/RequestByServletAPI")
public String test(HttpServletRequest request)
{
// 设置请求属性"testScope"的值为"hello,servletAPI"
request.setAttribute("testScope", "hello,servletAPI");
return "success";
}
}
成功跳转界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 页面定义部分,指定页面的输出内容类型和使用的语言 -->
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Success</h1>
<!-- 显示操作成功的标题 -->
<p>从请求作用域获取的值: ${testScope}</p>
<!-- 通过表达式从请求作用域中获取名为"testScope"的属性值,并显示在页面上 -->
</body>
</html>
测试输出
2.3.2. 小总结
代码过程
- index.jsp 是一个网页文件,用户可以在浏览器中看到和与之交互。在这个页面上,有一个链接,当用户点击这个链接时,浏览器会向服务器发送一个请求。
- Controller 控制器 (TestController) 是服务器端的一个组件,它的任务是处理来自index.jsp页面的链接点击请求。当请求到来时,它会执行
test
方法。 - 在
test
方法中,使用request.setAttribute("testScope", "hello,servletAPI")
这行代码,我们实际上是在请求对象上加了一个标签(称为“属性”),这个标签的名字是 "testScope",值是 "hello,servletAPI"。你可以把这个过程想象成在信封上贴了一个标签,这样邮递员(这里的邮递员就是浏览器)就知道要把信封(请求)送到哪里,并且知道里面装的是什么。 - 然后,
test
方法返回一个字符串 "success",这个字符串告诉Spring框架,处理请求成功后,应该显示名为 "success" 的视图(在这个例子中,就是一个JSP页面)。 - 成功跳转界面 是用户在浏览器中看到的另一个网页,当请求成功处理后,浏览器会显示这个页面。在这个页面的代码中,有一段
<p>
标签,它包含了一个表达式${testScope}
。这个表达式的作用是从之前我们在请求对象上贴上的标签(属性)中取出值,并显示在这个段落中。所以,用户会在这个页面上看到 "hello,servletAPI" 这段文字。
总结来说,"使用ServletAPI向request域对象共享数据" 就是指我们使用服务器端的代码(Servlet API)来存储和传递信息(在这个例子中是 "hello,servletAPI"),然后在用户的浏览器中显示这些信息。这个过程让我们能够创建动态的、能够响应用户操作的网页。
或者举个例子
想象一下,你在玩一个接力赛跑游戏。在这个游戏中,Servlet API就像是一个接力棒,而request域对象就像是一个可以传递接力棒的盒子。当你跑到终点时,你需要把接力棒放到这个盒子里,这样下一个跑者就可以从盒子中取出接力棒,继续比赛。
在Web开发中,我们也经常需要在不同的部分(比如不同的页面或者不同的请求处理方法)之间传递信息。这就是Servlet API和request域对象发挥作用的地方。
- Servlet API:它是一组工具,可以帮助我们处理来自用户的请求,并向用户发送响应。就像接力棒一样,它是用来传递信息的媒介。
- request域对象:这是一个特殊的存储空间,它属于当前用户的请求。你可以把它想象成一个盒子,你可以在这个盒子里放入信息(比如比分、状态等),然后在需要的时候取出来。这个盒子是临时的,只在当前请求过程中有效。
现在,让我们看看这个过程是如何在代码中实现的:
- 用户在浏览器中点击一个链接,这个链接会触发服务器上的一个方法(比如
test
方法)。 - 在
test
方法中,我们使用request.setAttribute("testScope", "hello,servletAPI")
这行代码,就像是把一条信息("hello,servletAPI")放入了接力棒(request域对象)中。 - 当方法执行完毕,我们返回一个字符串 "success",这告诉系统我们已经处理完请求,并且一切顺利。
- 系统随后会加载一个新的页面(成功跳转界面),在这个页面中,我们用
${testScope}
这个表达式来取出之前放入接力棒(request域对象)中的信息,并显示在页面上。
所以,"使用ServletAPI向request域对象共享数据" 就像是在游戏中传递接力棒,我们把需要的信息放入一个临时的存储空间(request域对象),然后在适当的时候取出来,以供下一步使用。这样,不同的部分就可以共享和使用同一条信息了。
2.4. ModelAndView向request域对象共享数据
2.4.1. 代码示例
修改 index.jsp 代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<%--使用ServletAPI向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/RequestByServletAPI">ServletAPI</a> <br>
<%--使用ModelAndView向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testModelAndView">ModelAndView</a>
</body>
</html>
控制器类添加新方法
package com.sakurapaid.mvc.test;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
/**
* TestController类用于处理请求。
*/
@Controller
public class TestController {
/**
* 处理"/RequestByServletAPI"请求的方法。
*
* @param request HttpServletRequest对象,用于获取请求信息和设置响应信息。
* @return 返回一个字符串"success",指示视图解析器解析成功。
*/
@RequestMapping("/RequestByServletAPI")
public String test(HttpServletRequest request)
{
// 设置请求属性"testScope"的值为"hello,servletAPI"
request.setAttribute("testScope", "hello,servletAPI");
return "success";
}
/**
* 测试ModelAndView功能的方法。
* 该方法不接受任何参数,返回一个ModelAndView实例。
* Model主要用于向请求域共享数据,View主要用于设置视图,实现页面跳转。
*
* @return ModelAndView 包含了模型数据和视图名称的对象。
*/
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
ModelAndView mav = new ModelAndView();
// 向请求域中添加一个名为"testScope"的对象,其值为"hello,ModelAndView"。
mav.addObject("testScope", "hello,ModelAndView");
// 设置视图名称为"success",即将跳转到的页面。
mav.setViewName("success");
return mav;
}
}
success.jsp 目标页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 页面定义部分,指定页面的输出内容类型和使用的语言 -->
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Success</h1>
<!-- 显示操作成功的标题 -->
<p>从请求作用域获取的值: ${testScope}</p>
<!-- 通过表达式从请求作用域中获取名为"testScope"的属性值,并显示在页面上 -->
</body>
</html>
测试输出
2.4.2. 小总结
在Web开发中,我们经常需要在服务器端和客户端之间传递信息。为了实现这个目的,我们可以使用不同的技术。在Spring框架中,ModelAndView
是一个非常重要的工具,它可以帮助我们将数据从服务器端传递到客户端,并且实现页面的跳转。
- 理解ModelAndView:
ModelAndView
是Spring框架中的一个对象,它包含了两部分信息:模型数据(Model)和视图名称(View)。模型数据是我们要传递给客户端的数据,而视图名称则告诉系统我们要跳转到哪个页面。 - 添加数据到模型: 通过调用
ModelAndView
对象的addObject
方法,我们可以将数据添加到模型中。这些数据会被存储在请求域(request scope)中,这意味着数据只会在当前的HTTP请求中有效。 - 设置视图名称: 通过调用
ModelAndView
对象的setViewName
方法,我们可以指定一个视图名称。这个名称会被视图解析器用来找到并渲染相应的页面。 - 返回ModelAndView对象: 在控制器方法中,我们将填充好的
ModelAndView
对象返回给Spring框架。框架会根据视图名称找到对应的页面,并把模型数据传递给这个页面。
testModelAndView
方法的实现过程:
1. 创建ModelAndView
对象
在testModelAndView
方法中,我们首先创建了一个ModelAndView
对象。这个对象将用于存储我们将要传递给视图的数据(模型数据)以及指定要使用的视图(视图名称)。
ModelAndView mav = new ModelAndView();
2. 向模型中添加数据
接下来,我们使用addObject
方法将一个属性添加到ModelAndView
对象中。这个属性包括一个名称和一个值。在这个例子中,我们添加了一个名为"testScope"
的属性,其值为"hello,ModelAndView"
。
mav.addObject("testScope", "hello,ModelAndView");
这个属性将被存储在HTTP请求的属性中,可以在视图中被访问和展示。
3. 设置视图名称
然后,我们使用setViewName
方法来指定当这个ModelAndView
对象被返回时,应该使用的视图名称。在这个例子中,我们指定视图名称为"success"
。这意味着Spring框架的视图解析器将会寻找名为success.jsp
的页面,并将模型数据传递给这个页面。
mav.setViewName("success");
4. 返回ModelAndView
对象
最后,我们将填充好的ModelAndView
对象返回给Spring框架。框架将处理这个对象,将模型数据传递给指定的视图,并执行页面跳转。
return mav;
5. 视图展示数据
在success.jsp
页面中,我们使用EL表达式${testScope}
来访问并展示ModelAndView
对象中添加的"testScope"
属性的值。
<p>从请求作用域获取的值: ${testScope}</p>
当用户访问/testModelAndView
路径时,testModelAndView
方法会被触发,执行上述过程。最终,用户的浏览器会展示success.jsp
页面,并在页面上显示"hello,ModelAndView"
这个字符串。
2.5. Model向request域对象共享数据
2.5.1. 代码示例
控制器类添加 tesModel 方法
package com.sakurapaid.mvc.test;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
/**
* TestController类用于处理请求。
*/
@Controller
public class TestController {
/**
* 处理"/RequestByServletAPI"请求的方法。
*
* @param request HttpServletRequest对象,用于获取请求信息和设置响应信息。
* @return 返回一个字符串"success",指示视图解析器解析成功。
*/
@RequestMapping("/RequestByServletAPI")
public String test(HttpServletRequest request)
{
// 设置请求属性"testScope"的值为"hello,servletAPI"
request.setAttribute("testScope", "hello,servletAPI");
return "success";
}
/**
* 测试ModelAndView功能的方法。
* 该方法不接受任何参数,返回一个ModelAndView实例。
* Model主要用于向请求域共享数据,View主要用于设置视图,实现页面跳转。
*
* @return ModelAndView 包含了模型数据和视图名称的对象。
*/
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
ModelAndView mav = new ModelAndView();
// 向请求域中添加一个名为"testScope"的对象,其值为"hello,ModelAndView"。
mav.addObject("testScope", "hello,ModelAndView");
// 设置视图名称为"success",即将跳转到的页面。
mav.setViewName("success");
return mav;
}
@RequestMapping("/testModel")
public String testModel(Model model){
model.addAttribute("testScope", "hello,Model");
return "success";
}
}
修改 index.jsp 文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<%--使用ServletAPI向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/RequestByServletAPI">ServletAPI</a> <br>
<%--使用ModelAndView向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testModelAndView">ModelAndView</a> <br>
<%--使用Model向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testModel">Model</a>
</body>
</html>
success.jsp 目标页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 页面定义部分,指定页面的输出内容类型和使用的语言 -->
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Success</h1>
<!-- 显示操作成功的标题 -->
<p>从请求作用域获取的值: ${testScope}</p>
<!-- 通过表达式从请求作用域中获取名为"testScope"的属性值,并显示在页面上 -->
</body>
</html>
测试输出
2.5.2. 小总结
使用Model向request域对象共享数据的过程:
- 创建TestController类:这是一个Spring MVC控制器(
@Controller
注解),负责处理特定的HTTP请求。 - 定义
testModel()
方法:
-
- 使用
@RequestMapping("/testModel")
注解映射到URL路径/testModel
。 - 方法接收一个
Model
类型的参数。Model
是Spring MVC提供的一种接口,用于向视图传递模型数据。 - 在方法内部,调用
model.addAttribute("testScope", "hello,Model")
,将键为testScope
、值为"hello,Model"
的数据添加到Model对象中。这一步实际上就是向请求作用域共享数据,因为Model会在渲染视图时将数据放入请求作用域。
- 使用
- 配置
success.jsp
:作为目标页面,它负责显示从请求作用域获取的数据。
-
- 使用JSP表达式
${testScope}
来从请求作用域获取名为testScope
的属性值。 - 当
success.jsp
被渲染时,${testScope}
会被替换为实际从请求作用域中获取的值。
- 使用JSP表达式
- 测试过程:
-
- 用户访问首页
index.jsp
,点击链接“Model”,发起对/testModel
的HTTP请求。 - Spring MVC框架根据URL路径找到并调用
TestController
中的testModel()
方法。 testModel()
方法将数据"hello,Model"
添加到Model对象,并返回视图名"success"
。- Spring MVC框架根据返回的视图名找到并渲染
success.jsp
页面。 - 在渲染过程中,JSP引擎在
success.jsp
中遇到${testScope}
表达式,从当前请求作用域查找并取出对应的值(即"hello,Model"
),将其替换到HTML中。 - 最终用户看到的
success.jsp
页面上显示了从请求作用域获取的值:“从请求作用域获取的值: hello,Model”。
- 用户访问首页
综上所述,使用Model向request域对象共享数据的过程如下:
- 在控制器方法中接收一个Model参数,通过调用
addAttribute()
方法将数据(键值对)添加到Model中。 - 控制器方法返回视图名,触发视图渲染。
- 在目标JSP页面中使用
${}
表达式从请求作用域获取已共享的数据,并动态插入到页面内容中。
2.6. Map向request域对象共享数据
2.6.1. 代码示例
控制器类添加 testMap 方法
package com.sakurapaid.mvc.test;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* TestController类用于处理请求。
*/
@Controller
public class TestController {
/**
* 处理"/RequestByServletAPI"请求的方法。
* 通过HttpServletRequest来处理请求,设置请求属性,并返回一个字符串指示视图解析器如何处理响应。
*
* @param request HttpServletRequest对象,用于获取请求信息和设置响应信息。
* @return 返回一个字符串"success",指示视图解析器解析成功。
*/
@RequestMapping("/RequestByServletAPI")
public String test(HttpServletRequest request)
{
// 设置请求属性
request.setAttribute("testScope", "hello,servletAPI");
return "success";
}
/**
* 测试ModelAndView功能的方法。
* 该方法不接受任何参数,返回一个ModelAndView实例,用于演示如何使用ModelAndView来处理请求和响应。
*
* @return ModelAndView 包含了模型数据和视图名称的对象。
*/
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
ModelAndView mav = new ModelAndView();
// 添加模型数据
mav.addObject("testScope", "hello,ModelAndView");
// 设置视图名称
mav.setViewName("success");
return mav;
}
/**
* 使用Model来传递数据的方法。
* 通过Model来向请求域添加数据,然后返回一个视图名称。
*
* @param model 用于向请求域添加数据的Model对象。
* @return 返回一个字符串"success",指示视图解析器解析成功。
*/
@RequestMapping("/testModel")
public String testModel(Model model){
// 向Model中添加数据
model.addAttribute("testScope", "hello,Model");
return "success";
}
/**
* 使用Map来传递数据的方法。
* 通过Map来向请求域添加数据,然后返回一个视图名称。
*
* @param map 用于向请求域添加数据的Map对象。
* @return 返回一个字符串"success",指示视图解析器解析成功。
*/
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){
// 向Map中添加数据
map.put("testScope", "hello,Map");
return "success";
}
}
修改 inde.jsp 文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<%--使用ServletAPI向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/RequestByServletAPI">ServletAPI</a> <br>
<%--使用ModelAndView向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testModelAndView">ModelAndView</a> <br>
<%--使用Model向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testModel">Model</a> <br>
<%--使用Map向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testMap">Map</a>
</body>
</html>
success.jsp 目标页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 页面定义部分,指定页面的输出内容类型和使用的语言 -->
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Success</h1>
<!-- 显示操作成功的标题 -->
<p>从请求作用域获取的值: ${testScope}</p>
<!-- 通过表达式从请求作用域中获取名为"testScope"的属性值,并显示在页面上 -->
</body>
</html>
测试输出
2.6.2. 小总结
使用Map
向request域对象共享数据的过程如下:
- 创建TestController类:这是一个Spring MVC控制器(
@Controller
注解),负责处理特定的HTTP请求。 - 定义
testMap()
方法:
-
- 使用
@RequestMapping("/testMap")
注解映射到URL路径/testMap
。 - 方法接收一个
Map<String, Object>
类型的参数。Map
是一个通用的键值对容器,这里用来临时存储要共享的数据。 - 在方法内部,调用
map.put("testScope", "hello,Map")
,将键为testScope
、值为"hello,Map"
的数据放入Map对象中。Spring MVC框架会自动将此Map对象的内容添加到请求作用域,从而实现数据共享。
- 使用
- 配置
success.jsp
:作为目标页面,它负责显示从请求作用域获取的数据。
-
- 使用JSP表达式
${testScope}
来从请求作用域获取名为testScope
的属性值。 - 当
success.jsp
被渲染时,${testScope}
会被替换为实际从请求作用域中获取的值。
- 使用JSP表达式
- 测试过程:
-
- 用户访问首页
index.jsp
,点击链接“Map”,发起对/testMap
的HTTP请求。 - Spring MVC框架根据URL路径找到并调用
TestController
中的testMap()
方法。 testMap()
方法将数据"hello,Map"
放入Map对象,并返回视图名"success"
。- Spring MVC框架接收到返回的视图名,开始渲染
success.jsp
页面。 - 在渲染过程中,JSP引擎在
success.jsp
中遇到${testScope}
表达式,从当前请求作用域查找并取出对应的值(即"hello,Map"
),将其替换到HTML中。 - 最终用户看到的
success.jsp
页面上显示了从请求作用域获取的值:“从请求作用域获取的值: hello,Map”。
- 用户访问首页
总结起来,使用Map
向request域对象共享数据的过程包括:
- 在控制器方法中接收一个
Map
参数,通过调用put()
方法将数据(键值对)放入Map中。 - 控制器方法返回视图名,触发视图渲染。
- 在目标JSP页面中使用
${}
表达式从请求作用域获取已共享的数据,并动态插入到页面内容中。
需要注意的是,虽然这里使用了Map
来传递数据,但实际上是Spring MVC框架自动将Map中的数据添加到了请求作用域。对于初学者来说,更推荐直接使用Model
对象(如testModel()
方法所示),因为它与Spring MVC框架结合更紧密,使用更直观,且提供了更多与视图渲染相关的便捷功能。使用Map
传递数据的方式在某些特殊场景下可能会出现,但在常规开发中并不常见。
2.7. ModelMap向request域对象共享数据
2.7.1. 代码示例
控制器类添加 testModelMap 方法
package com.sakurapaid.mvc.test;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* TestController类用于处理请求。
*/
@Controller
public class TestController {
/**
* 处理"/RequestByServletAPI"请求的方法。
* 通过HttpServletRequest来处理请求,设置请求属性,并返回一个字符串指示视图解析器如何处理响应。
*
* @param request HttpServletRequest对象,用于获取请求信息和设置响应信息。
* @return 返回一个字符串"success",指示视图解析器解析成功。
*/
@RequestMapping("/RequestByServletAPI")
public String test(HttpServletRequest request)
{
// 设置请求属性
request.setAttribute("testScope", "hello,servletAPI");
return "success";
}
/**
* 测试ModelAndView功能的方法。
* 该方法不接受任何参数,返回一个ModelAndView实例,用于演示如何使用ModelAndView来处理请求和响应。
*
* @return ModelAndView 包含了模型数据和视图名称的对象。
*/
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
ModelAndView mav = new ModelAndView();
// 添加模型数据
mav.addObject("testScope", "hello,ModelAndView");
// 设置视图名称
mav.setViewName("success");
return mav;
}
/**
* 使用Model来传递数据的方法。
* 通过Model来向请求域添加数据,然后返回一个视图名称。
*
* @param model 用于向请求域添加数据的Model对象。
* @return 返回一个字符串"success",指示视图解析器解析成功。
*/
@RequestMapping("/testModel")
public String testModel(Model model){
// 向Model中添加数据
model.addAttribute("testScope", "hello,Model");
return "success";
}
/**
* 使用Map来传递数据的方法。
* 通过Map来向请求域添加数据,然后返回一个视图名称。
*
* @param map 用于向请求域添加数据的Map对象。
* @return 返回一个字符串"success",指示视图解析器解析成功。
*/
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){
// 向Map中添加数据
map.put("testScope", "hello,Map");
return "success";
}
/**
* 使用ModelMap来传递数据的方法。
* 通过ModelMap来向请求域添加数据,然后返回一个视图名称。
*
* @param modelMap 用于向请求域添加数据的ModelMap对象。
* @return 返回一个字符串"success",指示视图解析器解析成功。
*/
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
modelMap.addAttribute("testScope", "hello,ModelMap");
return "success";
}
}
修改 inde.jsp 文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<%--使用ServletAPI向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/RequestByServletAPI">ServletAPI</a> <br>
<%--使用ModelAndView向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testModelAndView">ModelAndView</a> <br>
<%--使用Model向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testModel">Model</a> <br>
<%--使用Map向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testMap">Map</a> <br>
<%--使用ModelMap向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testModelMap">ModelMap</a>
</body>
</html>
success.jsp 目标页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 页面定义部分,指定页面的输出内容类型和使用的语言 -->
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Success</h1>
<!-- 显示操作成功的标题 -->
<p>从请求作用域获取的值: ${testScope}</p>
<!-- 通过表达式从请求作用域中获取名为"testScope"的属性值,并显示在页面上 -->
</body>
</html>
测试输出
2.7.2. 小总结
使用ModelMap
向request域对象共享数据的过程与使用Model
非常相似,因为ModelMap
是Model
接口的一个实现类。
- 定义
testModelMap()
方法:
-
- 使用
@RequestMapping("/testModelMap")
注解映射到URL路径/testModelMap
。 - 方法接收一个
ModelMap
类型的参数。ModelMap
是Spring MVC专门为视图模型设计的类,继承自LinkedHashMap
,具有Map
接口的所有功能,并额外提供了一些便于操作模型数据的方法。 - 在方法内部,调用
modelMap.addAttribute("testScope", "hello,ModelMap")
,将键为testScope
、值为"hello,ModelMap"
的数据添加到ModelMap对象中。ModelMap
对象会自动将其内容添加到请求作用域,实现数据共享。
- 使用
- 配置
success.jsp
:与之前相同,目标页面success.jsp
负责从请求作用域获取并显示名为testScope
的属性值。 - 测试过程:
-
- 用户访问首页
index.jsp
,点击链接“ModelMap”,发起对/testModelMap
的HTTP请求。 - Spring MVC框架根据URL路径找到并调用
TestController
中的testModelMap()
方法。 testModelMap()
方法将数据"hello,ModelMap"
添加到ModelMap对象,并返回视图名"success"
。- Spring MVC框架接收到返回的视图名,开始渲染
success.jsp
页面。 - 在渲染过程中,JSP引擎在
success.jsp
中遇到${testScope}
表达式,从当前请求作用域查找并取出对应的值(即"hello,ModelMap"
),将其替换到HTML中。 - 最终用户看到的
success.jsp
页面上显示了从请求作用域获取的值:“从请求作用域获取的值: hello,ModelMap”。
- 用户访问首页
总结起来,使用ModelMap
向request域对象共享数据的过程包括:
- 在控制器方法中接收一个
ModelMap
参数,通过调用addAttribute()
方法将数据(键值对)添加到ModelMap中。 - 控制器方法返回视图名,触发视图渲染。
- 在目标JSP页面中使用
${}
表达式从请求作用域获取已共享的数据,并动态插入到页面内容中。
尽管ModelMap
提供了比Map
更多的便利功能,但在实际开发中,通常更推荐直接使用Model
接口作为方法参数(如testModel()
所示)。这样做可以使代码更通用,因为Spring MVC会自动将传入的Model
参数转换为合适的实现类(如ModelMap
或ExtendedModelMap
)。这样,即使以后Spring MVC内部实现发生变化,你的代码也不需要调整。因此,使用Model
作为参数是一种更好的编程习惯。
2.8. Model/ModelMap/Map的关系
在Spring MVC中,当我们在控制器方法(如@RequestMapping注解的方法)中使用Model、ModelMap或Map类型的参数来向request域对象共享数据时,虽然它们表面上看起来不同,但本质上,这些参数都被Spring MVC框架处理为BindingAwareModelMap类型。
- Model:
-
- 接口定义:Model是一个接口,它定义了一组方法,用于在控制器中向一个数据模型添加属性。这些属性在后续的视图渲染过程中可供访问和展示。
- 简化控制器逻辑:作为开发者,我们无需直接操作底层数据结构,只需通过Model接口提供的方法添加属性,简化了控制器的编码工作。
- 基于Map的实现:虽然Model接口本身不直接实现Map接口,但在Spring MVC的实际应用中,传入控制器方法的Model参数通常会被实现为一个Map(如
BindingAwareModelMap
),因此可以像操作Map一样向其中添加键值对。
- ModelMap:
-
- 具体实现:ModelMap是Model接口的一个具体实现类,它不仅提供了Model接口所定义的方法,还内建了一个Map来实际存储属性数据。
- 增强功能:相较于直接使用Map,ModelMap提供了额外的便利性和功能,如支持批量添加属性、自动类型转换等,使得数据管理更加高效。
- 常用选择:由于其便捷性和针对视图渲染的优化,ModelMap在Spring MVC应用中被广泛用作控制器与视图间数据传递的标准工具。
- Map:
-
- 通用数据结构:Map是Java标准库中的一个接口,用于表示键值对(key-value pairs)的集合,是一种通用的数据存储结构。
- 灵活运用:在Spring MVC中,任何实现了Map接口的对象(如
HashMap
、LinkedHashMap
等)都可以用来在控制器和视图之间传递数据。这种方式更为底层和灵活,但需要开发者自行处理一些细节问题,如属性添加、类型转换等。 - 直接使用场景:尽管ModelMap是首选,但在某些特定场景下,开发者可能选择直接使用Map,如需要特定Map实现的特性,或者希望对数据传递过程有更精细的控制。
总结来说,Model作为接口,定义了在控制器和视图间传递数据的基本操作;ModelMap作为Model接口的实现,提供了一个功能丰富、专为Spring MVC优化的属性容器;而Map作为一种通用数据结构,虽然可以直接用于数据传递,但使用时需自行处理更多细节。在日常开发中,ModelMap因其便捷性和针对性优化,成为在Spring MVC中传递模型数据的主流选择。
2.9. 向session域共享数据
2.9.1. 代码示例
控制器类添加 testSession 方法
package com.sakurapaid.mvc.test;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Map;
@Controller
public class TestController {
/**
* 测试会话功能的方法。
*
* @param session HttpSession对象,用于存储和获取会话属性。
* @return 返回一个字符串"success",表示会话测试成功。
*/
@RequestMapping("/testSession")
public String testSession(HttpSession session){
// 设置会话属性"testScope",值为"hello,session"
session.setAttribute("testScope", "hello,session");
return "success";
}
/**
* 处理"/RequestByServletAPI"请求的方法。
* 通过HttpServletRequest来处理请求,设置请求属性,并返回一个字符串指示视图解析器如何处理响应。
*
* @param request HttpServletRequest对象,用于获取请求信息和设置响应信息。
* @return 返回一个字符串"success",指示视图解析器解析成功。
*/
@RequestMapping("/RequestByServletAPI")
public String test(HttpServletRequest request)
{
// 设置请求属性
request.setAttribute("testScope", "hello,servletAPI");
return "success";
}
/**
* 测试ModelAndView功能的方法。
* 该方法不接受任何参数,返回一个ModelAndView实例,用于演示如何使用ModelAndView来处理请求和响应。
*
* @return ModelAndView 包含了模型数据和视图名称的对象。
*/
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
ModelAndView mav = new ModelAndView();
// 添加模型数据
mav.addObject("testScope", "hello,ModelAndView");
// 设置视图名称
mav.setViewName("success");
return mav;
}
/**
* 使用Model来传递数据的方法。
* 通过Model来向请求域添加数据,然后返回一个视图名称。
*
* @param model 用于向请求域添加数据的Model对象。
* @return 返回一个字符串"success",指示视图解析器解析成功。
*/
@RequestMapping("/testModel")
public String testModel(Model model){
// 向Model中添加数据
model.addAttribute("testScope", "hello,Model");
return "success";
}
/**
* 使用Map来传递数据的方法。
* 通过Map来向请求域添加数据,然后返回一个视图名称。
*
* @param map 用于向请求域添加数据的Map对象。
* @return 返回一个字符串"success",指示视图解析器解析成功。
*/
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){
// 向Map中添加数据
map.put("testScope", "hello,Map");
return "success";
}
/**
* 使用ModelMap来传递数据的方法。
* 通过ModelMap来向请求域添加数据,然后返回一个视图名称。
*
* @param modelMap 用于向请求域添加数据的ModelMap对象。
* @return 返回一个字符串"success",指示视图解析器解析成功。
*/
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
modelMap.addAttribute("testScope", "hello,ModelMap");
return "success";
}
}
修改 index.jsp 界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<%--使用ServletAPI向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/RequestByServletAPI">ServletAPI</a> <br>
<%--使用ModelAndView向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testModelAndView">ModelAndView</a> <br>
<%--使用Model向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testModel">Model</a> <br>
<%--使用Map向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testMap">Map</a> <br>
<%--使用ModelMap向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testModelMap">ModelMap</a> <br>
<%--使用session域向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testSession">Session</a>
</body>
</html>
测试输出
2.9.2. 小总结
使用HttpSession向session域对象共享数据的过程如下:
- 定义testSession()方法:
-
- 使用
@RequestMapping("/testSession")
注解映射到URL路径/testSession
。 - 方法接收一个
HttpSession
类型的参数,该参数代表当前用户的会话对象。 - 在方法内部,调用
httpSession.setAttribute("testScope", "hello,Session")
,将键为testScope
、值为"hello,Session"
的数据添加到HttpSession
对象中。HttpSession
对象用于存储与用户会话相关的数据,这些数据在整个会话期间保持有效,直至会话过期或被显式清除。
- 使用
- 配置success.jsp:
-
- 目标页面success.jsp负责从session作用域获取并显示名为
testScope
的属性值。与之前一样,使用${testScope}
表达式来获取并显示session域中的数据。
- 目标页面success.jsp负责从session作用域获取并显示名为
- 测试过程:
-
- 用户访问首页index.jsp,点击链接“Session”,发起对
/testSession
的HTTP请求。 - Spring MVC框架根据URL路径找到并调用TestController中的
testSession()
方法。 testSession()
方法将数据"hello,Session"
添加到HttpSession
对象,并返回视图名"success"
。- Spring MVC框架接收到返回的视图名,开始渲染success.jsp页面。
- 在渲染过程中,JSP引擎在success.jsp中遇到
${testScope}
表达式,从当前session作用域查找并取出对应的值(即"hello,Session"
),将其替换到HTML中。 - 最终用户看到的success.jsp页面上显示了从session作用域获取的值:“从session作用域获取的值: hello,Session”。
- 用户访问首页index.jsp,点击链接“Session”,发起对
总结起来,使用HttpSession
向session域对象共享数据的过程包括:
- 在控制器方法中接收一个
HttpSession
参数,通过调用setAttribute()
方法将数据(键值对)添加到HttpSession
中。 - 控制器方法返回视图名,触发视图渲染。
- 在目标JSP页面中使用
${}
表达式从session作用域获取已共享的数据,并动态插入到页面内容中。
使用HttpSession
可以实现跨请求的数据共享,适用于需要在用户整个会话期间持久化数据的场景,如用户登录状态、购物车信息等。但需要注意的是,过度依赖session可能导致内存消耗增加,应合理控制session中存储的数据量和生命周期。
2.10. 向application域共享数据
2.10.1. 代码示例
控制器类添加 testApplication 方法
/**
* 测试应用程序的范围属性。
*
* @param session HttpSession对象,用于获取Servlet上下文。
* @return 返回一个字符串"success",表示操作成功。
*/
@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
// 获取Servlet上下文
ServletContext application = session.getServletContext();
// 向Servlet上下文设置一个属性,名称为"testScope",值为"hello,application"
application.setAttribute("testScope", "hello,application");
return "success";
}
修改 index.jsp 界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<%--使用ServletAPI向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/RequestByServletAPI">ServletAPI</a> <br>
<%--使用ModelAndView向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testModelAndView">ModelAndView</a> <br>
<%--使用Model向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testModel">Model</a> <br>
<%--使用Map向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testMap">Map</a> <br>
<%--使用ModelMap向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testModelMap">ModelMap</a> <br>
<%--使用session域向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testSession">Session</a>
<%--使用application域向request域对象共享数据--%>
<a href="${pageContext.request.contextPath}/testApplication">Application</a>
</body>
</html>
测试输出
2.10.2. 小总结
使用ServletContext
向application域对象共享数据的过程:
- 定义testApplication()方法:
-
- 使用
@RequestMapping("/testApplication")
注解映射到URL路径/testApplication
。 - 方法接收一个
HttpSession
类型的参数,通过该参数获取ServletContext
对象(即应用程序上下文)。 - 在方法内部,调用
session.getServletContext()
获取ServletContext
对象。 - 调用
application.setAttribute("testScope", "hello,application")
,将键为testScope
、值为"hello,application"
的数据添加到ServletContext
对象中。ServletContext
对象代表整个Web应用程序的全局共享区域,其存储的数据在整个应用程序范围内可见,直到应用程序被重新部署或服务器关闭。
- 使用
- 配置success.jsp:
-
- 目标页面success.jsp负责从application作用域获取并显示名为
testScope
的属性值。与之前一样,使用${testScope}
表达式来获取并显示application域中的数据。
- 目标页面success.jsp负责从application作用域获取并显示名为
- 测试过程:
-
- 用户访问首页index.jsp,点击链接“Application”,发起对
/testApplication
的HTTP请求。 - Spring MVC框架根据URL路径找到并调用TestController中的
testApplication()
方法。 testApplication()
方法将数据"hello,application"
添加到ServletContext
对象,并返回视图名"success"
。- Spring MVC框架接收到返回的视图名,开始渲染success.jsp页面。
- 在渲染过程中,JSP引擎在success.jsp中遇到
${testScope}
表达式,从当前application作用域查找并取出对应的值(即"hello,application"
),将其替换到HTML中。 - 最终用户看到的success.jsp页面上显示了从application作用域获取的值:“从application作用域获取的值: hello,application”。
- 用户访问首页index.jsp,点击链接“Application”,发起对
总结起来,使用ServletContext
向application域对象共享数据的过程包括:
- 在控制器方法中接收一个
HttpSession
参数,通过其获取ServletContext
对象。 - 调用
setAttribute()
方法将数据(键值对)添加到ServletContext
中。 - 控制器方法返回视图名,触发视图渲染。
- 在目标JSP页面中使用
${}
表达式从application作用域获取已共享的数据,并动态插入到页面内容中。
使用ServletContext
可以在整个Web应用程序范围内共享数据,适用于需要所有用户或所有请求都能访问的全局配置信息、静态资源映射等场景。由于application作用域的生命周期较长,应谨慎管理存储在其中的数据,避免占用过多内存或导致数据泄漏。