首页 > Spring MVC
Spring MVC域对象共享数据
在 Spring MVC 中,控制器在接收到 DispatcherServlet 分发过来的请求后,会继续调用 Model 层对请求进行处理。Model 层处理完请求后的结果被称为模型数据,会将模型数据返回给 Controller。Controller 在接收到 Model 层返回的模型数据后,下一步就是将模型数据通过域对象共享的方式传递给 View 视图进行渲染,最终返回给客户端展示。
下面我们就一一对这几种域对象共享数据的方式进行介绍。
由于这种方式是通过原生 Servlet API 实现的,会导致控制器与 Servlet 容器耦合度过高,因此通常情况下,我们不推荐使用这种方式向 request 域对象中共享数据。
ModelAndView 为我们提供了多种方法,其中常用的方法如下表。
在 Controller 类中,ModelAndView 只有在作为控制器方法的返回值,返回给前端控制器(DispatcherServlet)时,前端控制器解析才会去解析它,示例代码如下。
1. 新建一个名为 springmvc-response-demo 的 Web 项目,并将 Spring MVC 相关的依赖引入到该项目中,web.xml 配置如下。
2. 在 src 下(类路径下)创建一个名为 springMVC.xml 的 Spring MVC 配置文件,配置内容如下。
3. 在 net.biancheng.c.entity 包下,创建一个名为 User 的实体类,代码如下。
4. 在 net.biancheng.c.entity 包下,创建一个名为 Product 的实体类,代码如下。
5. 在 net.biancheng.c.controller 包下,创建一个名为 LoginController 的 Controller 类,代码如下。
6. 在 net.biancheng.c.controller 包下,创建一个名为 ProductController 的 Controller 类,代码如下。
7. 在 net.biancheng.c.service 包下,创建一个名为 UserService 的接口,代码如下。
8. 在 net.biancheng.service.impl 包下,创建 UserService 接口的实现类 UserServiceImpl,代码如下。
9. 在 net.biancheng.c.service 包下,创建一个名为 ProductService 的接口,代码如下。
10. 在 net.biancheng.service.impl 包下,创建 ProductService 接口的实现类 ProductServiceImpl,代码如下。
11. 在 net.biancheng.c.dao 包下,创建一个名为 UserDao 的类,代码如下。
12. 在 net.biancheng.c.dao 包下,创建一个名为 ProductDao 的类,代码如下。
13. 在 webapp/WEB-INF 下新建一个 templates 目录,并在该目录下创建 user.html,代码如下。
14. 在 webapp/WEB-INF/templates 目录下,新建一个 productList.html,代码如下。
15. 在 webapp/WEB-INF/templates 目录下,新建一个 product.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:商品详情
Spring MVC 提供了多种域对象共享数据的方式,其中最常用的方式如下:域对象是服务器在内存上创建的一块存储空间,主要用不同动态资源之间的数据传递和数据共享。在 Spring MVC 中,常用的域对象有 request 域对象、session 域对象、application 域对象等。
- 使用 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:登陆正确
图4:商品列表
20. 点击“查看商品详细信息”,结果如下图。
图5:商品详情