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 适合跨多个请求处理的公共数据。

相关推荐
侠客行031716 小时前
Mybatis连接池实现及池化模式
java·mybatis·源码阅读
蛇皮划水怪16 小时前
深入浅出LangChain4J
java·langchain·llm
老毛肚17 小时前
MyBatis体系结构与工作原理 上篇
java·mybatis
风流倜傥唐伯虎18 小时前
Spring Boot Jar包生产级启停脚本
java·运维·spring boot
Yvonne爱编码18 小时前
JAVA数据结构 DAY6-栈和队列
java·开发语言·数据结构·python
Re.不晚18 小时前
JAVA进阶之路——无奖问答挑战1
java·开发语言
你这个代码我看不懂18 小时前
@ConditionalOnProperty不直接使用松绑定规则
java·开发语言
fuquxiaoguang18 小时前
深入浅出:使用MDC构建SpringBoot全链路请求追踪系统
java·spring boot·后端·调用链分析
琹箐18 小时前
最大堆和最小堆 实现思路
java·开发语言·算法
__WanG19 小时前
JavaTuples 库分析
java