首页 > Spring MVC 阅读:6818

Spring MVC域对象共享数据

Spring MVC 中,控制器在接收到 DispatcherServlet 分发过来的请求后,会继续调用 Model 层对请求进行处理。Model 层处理完请求后的结果被称为模型数据,会将模型数据返回给 Controller。Controller 在接收到 Model 层返回的模型数据后,下一步就是将模型数据通过域对象共享的方式传递给 View 视图进行渲染,最终返回给客户端展示。

域对象是服务器在内存上创建的一块存储空间,主要用不同动态资源之间的数据传递和数据共享。在 Spring MVC 中,常用的域对象有 request 域对象、session 域对象、application 域对象等。

Spring MVC 提供了多种域对象共享数据的方式,其中最常用的方式如下:
  • 使用 Servlet API 向 request 域对象中共享数据
  • 使用 ModelAndView 向 request 域对象中共享数据
  • 使用 Model 向 request 域对象中共享数据
  • 使用 Map 向 request 域对象中共享数据
  • 使用 ModelMap 向 request 域对象中共享数据
  • 使用 Servlet API 向 session 域中共享数据
  • 使用 Servlet API 向 application 域中共享数据

下面我们就一一对这几种域对象共享数据的方式进行介绍。

1. 使用 Servlet API 向 request 域对象中共享数据

我们可以在控制器方法中设置一个 HttpServletRequest 类型的形参。通过它,我们就可以将模型数据共享到 request 域对象中,示例代码如下。
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request) {
 request.setAttribute("testScope", "hello,Servet API");
 return "success";
}

由于这种方式是通过原生 Servlet API 实现的,会导致控制器与 Servlet 容器耦合度过高,因此通常情况下,我们不推荐使用这种方式向 request 域对象中共享数据。

2. 使用 ModelAndView 向 request 域对象中共享数据

我们还可以通过 Spring 提供的 ModelAndView 向 reuqest 域对象中共享数据。ModelAndView 主要由 model(模型)和 view(视图)两部分组成。其中,model 负责数据共享,而 view 则主要用于设置视图,实现页面的跳转。

ModelAndView 为我们提供了多种方法,其中常用的方法如下表。

方法 说明
ModelAndView addObject(String attributeName, @Nullable Object attributeValue) 添加模型数据
ModelAndView addObject(Object attributeValue)
ModelAndView addAllObjects(@Nullable Map<String, ?> modelMap)
void setViewName(@Nullable String viewName) 设置视图
void setView(@Nullable View view)

在 Controller 类中,ModelAndView 只有在作为控制器方法的返回值,返回给前端控制器(DispatcherServlet)时,前端控制器解析才会去解析它,示例代码如下。
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
 /**
 * ModelAndView有Model和View的功能
 * Model主要用于向请求域共享数据
 * View主要用于设置视图,实现页面跳转
 */
 ModelAndView mav = new ModelAndView();
 //向请求域共享数据
 mav.addObject("testScope", "hello,ModelAndView");
 //设置视图,实现页面跳转
 mav.setViewName("success");
 return mav;
}

3. 使用 Model 向 request 域对象中共享数据

我们可以在 Controller 控制器方法中设置一个 Model 类型的形参。通过它,我们也可以向 request 域对象中共享数据,示例代码如下。
@RequestMapping("/testModel")
public String testModel(Model model) {
 model.addAttribute("testScope", "hello,Model");
 return "success";
}

4. 使用 Map 向 request 域对象中共享数据

我们可以在 Controller 控制器方法中设置一个 Map 类型的形参。通过它,我们也可以向 request 域对象中共享数据,示例代码如下。
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map) {
 map.put("testScope", "hello,Map");
 return "success";
}

5. 使用 ModelMap 向 request 对象中共享数据

我们可以在 Controller 控制器方法中设置一个 ModelMap 类型的形参。通过它,我们也可以向 request 域对象中共享数据,示例代码如下。
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap) {
 modelMap.addAttribute("testScope", "hello,ModelMap");
 return "success";
}

6. 使用 Servlet API 向 session 域对象中共享数据

我们可以在控制器方法中设置一个 HttpSession 类型的形参。通过它,我们就可以将数据共享到 session 域对象中,示例代码如下。
@RequestMapping("/testSession")
public String testSession(HttpSession session) {
 session.setAttribute("testSessionScope", "hello,session");
 return "success";
}

7. 使用 Servlet API 向 application 域对象中共享数据

我们可以在控制器方法中设置一个 HttpSession 类型的形参,并通过它获取到 application 域对象,最终我们就可以将数据共享到 application 域对象中,示例代码如下。
@RequestMapping("/testApplication")
public String testApplication(HttpSession session) {
 ServletContext application = session.getServletContext();
 application.setAttribute("testApplicationScope", "hello,application");
 return "success";
}

示例

下面,我们就通过一个简单的案例,来演示下如何实现通过域对象中共享数据,将模型数据返回到 view 视图中。

1. 新建一个名为 springmvc-response-demo 的 Web 项目,并将 Spring MVC 相关的依赖引入到该项目中,web.xml 配置如下。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
 id="WebApp_ID" version="4.0">
 <display-name>first-springmvc-demo</display-name>
 <!--请求和响应的字符串过滤器-->
 <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>
 <!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
 <servlet>
 <servlet-name>dispatcherServlet</servlet-name>
 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
 <!--配置 DispatcherServlet 的一个初始化参数:spring mvc 配置文件按的位置和名称-->
 <init-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>classpath:springMVC.xml</param-value>
 </init-param>
 <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
 <servlet-name>dispatcherServlet</servlet-name>
 <!--设置springMVC的核心控制器所能处理的请求的请求路径/所匹配的请求可以是/login或.html或.js或.css方式的请求路径但是/不能匹配.jsp请求路径的请求-->
 <url-pattern>/</url-pattern>
 </servlet-mapping>
</web-app>

2. 在 src 下(类路径下)创建一个名为 springMVC.xml 的 Spring MVC 配置文件,配置内容如下。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/context
 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
 <!--开启组件扫描-->
 <context:component-scan base-package="net.biancheng.c"></context:component-scan>
 <!-- 配置 Thymeleaf 视图解析器 -->
 <bean id="viewResolver"
 class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
 <property name="order" value="1"/>
 <property name="characterEncoding" value="UTF-8"/>
 <property name="templateEngine">
 <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
 <property name="templateResolver">
 <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
 <!-- 视图前缀 -->
 <property name="prefix" value="/WEB-INF/templates/"/>
 <!-- 视图后缀 -->
 <property name="suffix" value=".html"/>
 <property name="templateMode" value="HTML5"/>
 <property name="characterEncoding" value="UTF-8"/>
 </bean>
 </property>
 </bean>
 </property>
 </bean>
</beans>

3. 在 net.biancheng.c.entity 包下,创建一个名为 User 的实体类,代码如下。
package net.biancheng.c.entity;
public class User {
 private String userId;
 private String userName;
 private String password;
 public String getUserId() {
 return userId;
 }
 public void setUserId(String userId) {
 this.userId = userId;
 }
 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;
 }
 @Override
 public String toString() {
 return "User{" +
 "userId='" + userId + '\'' +
 ", userName='" + userName + '\'' +
 ", password='" + password + '\'' +
 '}';
 }
}

4. 在 net.biancheng.c.entity 包下,创建一个名为 Product 的实体类,代码如下。
package net.biancheng.c.entity;
import java.math.BigDecimal;
public class Product {
 private Integer productId;
 private String productName;
 private BigDecimal price;
 private Integer storage;
 public Integer getProductId() {
 return productId;
 }
 public void setProductId(Integer productId) {
 this.productId = productId;
 }
 public String getProductName() {
 return productName;
 }
 public void setProductName(String productName) {
 this.productName = productName;
 }
 public BigDecimal getPrice() {
 return price;
 }
 public void setPrice(BigDecimal price) {
 this.price = price;
 }
 public Integer getStorage() {
 return storage;
 }
 public void setStorage(Integer storage) {
 this.storage = storage;
 }
 @Override
 public String toString() {
 return "Product{" +
 "productId=" + productId +
 ", productName='" + productName + '\'' +
 ", price=" + price +
 ", storage=" + storage +
 '}';
 }
}

5. 在 net.biancheng.c.controller 包下,创建一个名为 LoginController 的 Controller 类,代码如下。
package net.biancheng.c.controller;
import net.biancheng.c.entity.User;
import net.biancheng.c.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@Controller
public class LoginController {
 @Autowired
 private UserService userService;
 @RequestMapping("/user")
 public String sayHello() {
 return "user";
 }
 @RequestMapping("/login")
 public String login(User user, HttpServletRequest request) {
 User user2 = userService.getUserByUserName(user.getUserName());
 if (user2 != null && user2.getPassword().equals(user.getPassword())) {
 HttpSession session = request.getSession();
 session.setAttribute("loginUser", user2);
 return "redirect:/getProductList";
 }
 request.setAttribute("msg", "账号或密码错误!");
 return "user";
 }
}

6. 在 net.biancheng.c.controller 包下,创建一个名为 ProductController 的 Controller 类,代码如下。
package net.biancheng.c.controller;
import net.biancheng.c.entity.Product;
import net.biancheng.c.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
@Controller
public class ProductController {
 @Autowired
 private ProductService productService;
 @RequestMapping("/getProductList")
 public ModelAndView getProductList() {
 ModelAndView modelAndView = new ModelAndView();
 modelAndView.setViewName("productList");
 List<Product> productList = productService.getProductList();
 modelAndView.addObject(productList);
 return modelAndView;
 }
 @RequestMapping("/getProduct")
 public String getProduct(Integer productId, Model model) {
 Product productById = productService.getProductById(productId);
 model.addAttribute("product", productById);
 return "product";
 }
}

7. 在 net.biancheng.c.service 包下,创建一个名为 UserService 的接口,代码如下。
package net.biancheng.c.service;
import net.biancheng.c.entity.User;
public interface UserService {
 User getUserByUserName(String userName);
}

8. 在 net.biancheng.service.impl 包下,创建 UserService 接口的实现类 UserServiceImpl,代码如下。
package net.biancheng.c.service.impl;
import net.biancheng.c.dao.UserDao;
import net.biancheng.c.entity.User;
import net.biancheng.c.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService {
 @Autowired
 private UserDao userDao;
 @Override
 public User getUserByUserName(String userName) {
 return userDao.getUserByUserName(userName);
 }
}

9. 在 net.biancheng.c.service 包下,创建一个名为 ProductService 的接口,代码如下。
package net.biancheng.c.service;
import net.biancheng.c.entity.Product;
import java.util.List;
public interface ProductService {
 List<Product> getProductList();
 Product getProductById(Integer productId);
}

10. 在 net.biancheng.service.impl 包下,创建 ProductService 接口的实现类 ProductServiceImpl,代码如下。
package net.biancheng.c.service.impl;
import net.biancheng.c.dao.ProductDao;
import net.biancheng.c.entity.Product;
import net.biancheng.c.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("productService")
public class ProductServiceImpl implements ProductService {
 @Autowired
 private ProductDao productDao;
 @Override
 public List<Product> getProductList() {
 return productDao.getProductList();
 }
 @Override
 public Product getProductById(Integer productId) {
 return productDao.getProductById(productId);
 }
}

11. 在 net.biancheng.c.dao 包下,创建一个名为 UserDao 的类,代码如下。
package net.biancheng.c.dao;
import net.biancheng.c.entity.User;
import org.springframework.stereotype.Repository;
import java.util.*;
@Repository
public class UserDao {
 private static Map<String, User> users = null;
 static {
 users = new HashMap<String, User>();
 User user = new User();
 user.setUserId("1001");
 user.setUserName("C语言中文网默认用户");
 user.setPassword("123456789");
 User user1 = new User();
 user1.setUserId("1002");
 user1.setUserName("南忘");
 user1.setPassword("qwertyuiop");
 User user2 = new User();
 user2.setUserId("1003");
 user2.setUserName("Java用户");
 user2.setPassword("987654321");
 User user3 = new User();
 user3.setUserId("1004");
 user3.setUserName("李小龙");
 user3.setPassword("1qazxsw2");
 users.put(user.getUserName(), user);
 users.put(user1.getUserName(), user1);
 users.put(user2.getUserName(), user2);
 users.put(user3.getUserName(), user3);
 }
 public List getUserList() {
 List userList = new ArrayList();
 Set<String> keys = users.keySet();
 for (String key : keys) {
 User user = users.get(key);
 userList.add(user);
 }
 return userList;
 }
 public User getUserByUserName(String userName) {
 User user = users.get(userName);
 return user;
 }
 public void addUser(User user) {
 users.put(user.getUserId(), user);
 }
 public void update(User user) {
 users.put(user.getUserId(), user);
 }
 public void delete(String userId) {
 users.remove(userId);
 }
}

12. 在 net.biancheng.c.dao 包下,创建一个名为 ProductDao 的类,代码如下。
package net.biancheng.c.dao;
import net.biancheng.c.entity.Product;
import net.biancheng.c.entity.User;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
import java.util.*;
@Repository
public class ProductDao {
 private static Map<Integer, Product> products = null;
 static {
 products = new HashMap<Integer, Product>();
 Product product = new Product();
 product.setProductId(1);
 product.setProductName("茅台");
 product.setPrice(new BigDecimal(9999));
 Product product1 = new Product();
 product1.setProductId(2);
 product1.setProductName("五粮液");
 product1.setPrice(new BigDecimal(8888));
 Product product2 = new Product();
 product2.setProductId(3);
 product2.setProductName("信阳毛尖");
 product2.setPrice(new BigDecimal(7777));
 Product product3 = new Product();
 product3.setProductId(4);
 product3.setProductName("深州大蜜桃");
 product3.setPrice(new BigDecimal(6666));
 products.put(product.getProductId(), product);
 products.put(product1.getProductId(), product1);
 products.put(product2.getProductId(), product2);
 products.put(product3.getProductId(), product3);
 }
 public List getProductList() {
 List productList = new ArrayList();
 Set<Integer> keys = products.keySet();
 for (Integer key : keys) {
 Product product = products.get(key);
 productList.add(product);
 }
 return productList;
 }
 public Product getProductById(Integer productId) {
 return products.get(productId);
 }
}

13. 在 webapp/WEB-INF 下新建一个 templates 目录,并在该目录下创建 user.html,代码如下。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
 <meta charset="UTF-8">
 <title>C语言中文网</title>
</head>
<body>
<form th:action="@{/login}" method="post">
 <table style="margin: auto">
 <tr>
 <td colspan="2" align="center">
 <p style="color: red;margin: auto" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
 </td>
 </tr>
 <tr>
 <td>用户名:</td>
 <td><input type="text" name="userName" required><br></td>
 </tr>
 <tr>
 <td>密码:</td>
 <td><input type="password" name="password" required><br></td>
 </tr>
 <tr>
 <td colspan="2" align="center"><input type="submit"></td>
 </tr>
 </table>
</form>
</body>
</html>

14. 在 webapp/WEB-INF/templates 目录下,新建一个 productList.html,代码如下。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
 <meta charset="UTF-8">
 <title>Title</title>
</head>
<body>
<h1 th:text="'欢迎您:'+${session.loginUser.getUserName()}"></h1>
<table th:border="1" th:cellspacing="0" th:cellpadding="10" style="text-align: center;">
 <thead>
 <th>商品id</th>
 <th>商品名</th>
 <th>商品价格</th>
 <th>操作</th>
 </thead>
 <tbody>
 <tr th:each="m:${productList}">
 <td th:text="${m.getProductId()}"></td>
 <td th:text="${m.getProductName()}"></td>
 <td th:text="${m.getPrice()}"></td>
 <td>
 <a th:href="@{/getProduct(productId=${m.getProductId()})}">查看商品详细信息</a>
 </td>
 </tr>
 </tbody>
</table>
</body>
</html>

15. 在 webapp/WEB-INF/templates 目录下,新建一个 product.html,代码如下。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
 <meta charset="UTF-8">
 <title>Title</title>
</head>
<body>
<table>
 <tr>
 <td> 商品ID:</td>
 <td th:text="${product.getProductId()}"></td>
 </tr>
 <tr>
 <td>商品名:</td>
 <td th:text="${product.getProductName()}"></td>
 </tr>
 <tr>
 <td>商品价格:</td>
 <td th:text="${product.getPrice()}"></td>
 </tr>
</table>
<a th:href="@{/getProductList}">返回首页</a>
</body>
</html>

16. 将 springmvc-response-demo 部署到 Tomcate 服务器中,启动 Tomcat 服务器。使用浏览器访问“http://localhost:8080/springmvc-response-demo/user”,结果如下。

登录页
图1:登陆页面

17. 分别输入错误的用户名和密码,结果如下图。


图2:登陆错误

18. 在登录页中,分别输入用户名:C语言中文网默认用户,密码:123456789,如下图。


图3:登陆正确
19. 点击“提交”按钮,跳转到商品列表页,如下图。

商品列表
图4:商品列表

20. 点击“查看商品详细信息”,结果如下图。

商品详情
图5:商品详情

AltStyle によって変換されたページ (->オリジナル) /