Spring MVC中Controller是如何把数据传递给View的?

在 Spring MVC 中,Controller 负责请求的处理,准备需要展示的数据,并将这些数据传递给 View,由 View 负责最终的页面渲染。数据从 Controller 传递到 View 主要通过模型 (Model) 实现。

Spring MVC 提供了以下几种方式让 Controller 将数据添加到模型中,然后将模型传递给视图:

  1. 使用 Model 接口作为 Controller 方法参数 (推荐)

    • 这是目前最常用和推荐的方式。可以在 Controller 方法的参数列表中声明一个 org.springframework.ui.Model 类型的参数。
    • Spring MVC 在调用方法时会自动创建一个 Model 实例并将其传递给方法。
    • 可以使用 model.addAttribute(key, value) 方法向模型中添加属性,其中 key 是属性名(字符串),value 是要传递的数据对象。View 将使用这个 key 来访问数据。
    java 复制代码
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @Controller
    public class UserController {
    
        @GetMapping("/greeting")
        public String greeting(@RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
            // 准备数据
            String message = "Hello, " + name + "!";
    
            // 将数据添加到模型中,key 是 "greetingMessage"
            model.addAttribute("greetingMessage", message);
            model.addAttribute("pageTitle", "Greeting Page"); // 可以添加多个属性
    
            // 返回视图名
            return "greetingView"; // Spring MVC 会找到名为 greetingView 的视图来渲染
        }
    }

    greetingView 这个视图(例如 JSP, Thymeleaf 模板等)中,我们可以通过 greetingMessagepageTitle 这两个 key 来访问模型中的数据。

  2. 使用 MapModelMap 作为 Controller 方法参数

    • ModelMapLinkedHashMap 的子类,也实现了 Map 接口,提供了与 Model 相似的功能 (addAttribute 方法)。
    • Map 接口也可以直接使用。
    • 它们的行为与 Model 接口基本相同,都是向模型中添加键值对数据。
    java 复制代码
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import java.util.Map;
    
    @Controller
    public class ProductController {
    
        @GetMapping("/productDetails")
        public String showProductDetails(Map<String, Object> model) {
            // 准备数据
            Product product = productService.getProductById(123L);
    
            // 将数据添加到 Map (模型) 中
            model.put("product", product);
            model.put("currency", "USD");
    
            // 返回视图名
            return "productDetailsView";
        }
    }

    productDetailsView 视图中,可以通过 productcurrency 访问数据。

  3. 使用 ModelAndView 返回值

    • ModelAndView 是 Spring MVC 早期常用的类,它将模型数据和视图名封装在一个对象中返回。
    • 通过 addObject(key, value) 添加模型数据,通过 setViewName(viewName) 设置视图名。
    java 复制代码
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.servlet.ModelAndView;
    
    @Controller
    public class OrderController {
    
        @GetMapping("/orderDetails")
        public ModelAndView showOrderDetails() {
            // 准备数据
            Order order = orderService.getOrderById(456L);
    
            // 创建 ModelAndView 对象
            ModelAndView mav = new ModelAndView();
    
            // 添加模型数据
            mav.addObject("order", order);
            mav.addObject("status", "Shipped");
    
            // 设置视图名
            mav.setViewName("orderDetailsView");
    
            return mav;
        }
    }

    orderDetailsView 视图中,可以通过 orderstatus 访问数据。

  4. 使用 @ModelAttribute 标注方法

    • 可以在 Controller 类中定义一个方法,并使用 @ModelAttribute 标注它。
    • 这个方法会在 Controller 的请求处理方法执行之前被调用。
    • 该方法的返回值(通过参数 Model 添加的数据)会自动添加到模型中,供当前 Controller 的所有方法以及最终的视图使用。常用于添加一些公共的、所有视图都需要的数据(如导航信息、用户登录状态等)。
    java 复制代码
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.ModelAttribute;
    
    @Controller
    public class CommonDataController {
    
        // 这个方法会在每次处理请求前执行
        @ModelAttribute
        public void addCommonData(Model model) {
            model.addAttribute("appName", "My Awesome App");
            model.addAttribute("currentUser", "Guest"); // 实际应用中这里会获取当前登录用户
        }
    
        @GetMapping("/home")
        public String homePage() {
            // homeView 视图可以直接访问 appName 和 currentUser
            return "homeView";
        }
    
        @GetMapping("/about")
        public String aboutPage() {
            // aboutView 视图也可以访问 appName 和 currentUser
            return "aboutView";
        }
    }

    homeViewaboutView 中,都可以访问 appNamecurrentUser

  5. 通过 Controller 方法的返回值 (当返回值不是 String, View, ModelAndView 等特殊类型时)

    • 如果 Controller 方法返回一个 POJO 对象,并且没有 @ResponseBody 注解,Spring MVC 会尝试将这个返回值也添加到模型中。默认情况下,属性名会根据返回值的类型自动生成(例如,返回 User 对象,属性名可能是 "user" 或 "aUser")。

数据如何到达 View?

当 Controller 方法执行完毕,Spring MVC 会将模型中的数据(通常存储在一个 MapModelMap 对象中)放入到请求属性 (Request Attributes) 中。然后,Spring MVC 将控制权交给选定的 View。

不同的 View 技术访问请求属性的方式不同:

  • JSP/JSTL/EL (Expression Language): 可以直接使用 EL 表达式访问模型中的属性,例如 ${greetingMessage}${user.name}
  • Thymeleaf: 使用 OGNL 或 Spring EL 表达式,例如 <p th:text="${greetingMessage}"></p><span th:text="${product.price}"></span>
  • Freemarker: 使用 ${greetingMessage}${user.name}

总结传递数据的过程:

  1. Controller 通过 ModelMapModelAndView@ModelAttribute 方法将数据以键值对的形式添加到 Spring 管理的模型中。
  2. Spring MVC 在处理请求的后期,将模型中的数据复制到当前请求的属性中 (HttpServletRequest 的属性)。
  3. Spring MVC 委托 ViewResolver 找到对应的 View(例如 JSP 文件)。
  4. View 引擎(例如 JSP 容器、Thymeleaf 引擎)在渲染页面时,可以从请求属性中获取到 Controller 放入的模型数据,并将其呈现在最终的 HTML 或其他格式的响应中。

选择哪种方式取决于具体使用场景。使用 Model 参数是最灵活的方式,它将模型的管理与视图名分开来返回,使 Controller 方法更清晰。ModelAndView 适合模型数据和视图名紧密关联、一起返回的场景。@ModelAttribute 适合跨多个请求处理的公共数据。

相关推荐
诸葛小猿25 分钟前
Pdf转Word案例(java)
java·pdf·word·格式转换
yuren_xia30 分钟前
Spring MVC中跨域问题处理
java·spring·mvc
计算机毕设定制辅导-无忧学长39 分钟前
ActiveMQ 源码剖析:消息存储与通信协议实现(二)
java·activemq·java-activemq
一个憨憨coder1 小时前
Spring 如何解决循环依赖问题?
java·后端·spring
钢铁男儿1 小时前
深入解析C#参数传递:值参数 vs 引用参数
java·开发语言·c#
学渣676561 小时前
.idea和__pycache__文件夹分别是什么意思
java·ide·intellij-idea
purrrew1 小时前
【Java ee 初阶】多线程(9)上
java·java-ee
深色風信子2 小时前
Eclipse 插件开发 5 编辑器
java·eclipse·编辑器
小魏的马仔2 小时前
【java】使用iText实现pdf文件增加水印功能
java·开发语言·pdf
老任与码2 小时前
Spring AI(1)—— 基本使用
java·人工智能·spring ai