目录

1.介绍一下org.springframework.ui.Model

1. 本质与实现原理

2. 示例代码

2.被@RequestMapping标注的类的方法参数

3.BindingResult / Errors 必须紧跟在被校验的实体参数后面(代码说明)

正确代码

错误代码

■自定义校验器(Validator 实现类)

■相关标签作用说明

Spring MVC 注解完整对照表

注解组合替换关系表

请求参数接收方式对比表

JSON自动转换相关注解

按使用场景推荐注解组合

注解组合替换关系表

请求参数接收方式对比表


====

====

1.介绍一下org.springframework.ui.Model

org.springframework.ui.Model 是 Spring MVC 框架中极其核心的接口,它的主要作用是在 控制器(Controller) 和 视图(View) 之间建立数据传递的桥梁。

简单来说,当你在后端处理完业务逻辑后,需要将数据(如用户信息、列表)送到前端页面(如 JSP、Thymeleaf)显示,Model 就是那个用来装数据的“容器”。

1. 本质与实现原理

  • 本质:它是一个增强版的 Map<String, Object>。你可以向其中添加 Key-Value 键值对。

  • 实际实现类:在 Spring MVC 运行时,我们直接使用的 Model 接口对象的实际实现类通常是 BindingAwareModelMap(它继承自 ModelMap,而 ModelMap 又继承自 LinkedHashMap)。

  • 存储位置:当你调用 model.addAttribute() 时,数据最终会被放入 HttpServletRequest 的 attributes 属性域中。因此,它的作用域是 Request 级别(一次请求有效)。

2. 示例代码

package com.example.controller; // 假设包名,根据实际情况修改

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

// 自定义的业务类和实体类(根据项目实际包路径导入)
import com.example.service.UserService;
import com.example.entity.User;

@Controller
@RequestMapping("/api/user")  // 类级别定义基础路径
public class UserController {

    // 模拟业务服务
    private UserService userService = new UserService();

    // 方式一:method = RequestMethod.GET —— 查询用户详情
    @RequestMapping(value = "/profile", method = RequestMethod.GET)
    public String getUserProfile(@RequestParam("id") Long id, Model model) {
        // 1. 业务数据
        User user = userService.findById(id);
        String pageTitle = "用户个人中心";

        // 2. 显式将数据放入 Model(推荐,视图名清晰)
        model.addAttribute("userInfo", user);
        model.addAttribute("title", pageTitle);

        // 3. 返回视图逻辑名(由视图解析器解析,如 Thymeleaf 或 JSP)
        return "profile"; // 对应 /WEB-INF/views/profile.jsp 或 profile.html
    }

    // 方式二:method = RequestMethod.POST —— 新增用户(重定向)
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String addUser(@ModelAttribute User user, Model model) {
        // 模拟保存
        userService.save(user);
        
        // 给 Model 添加提示信息(仅在当前请求有效)
        model.addAttribute("message", "添加成功!");
        
        // 重定向时,Model 中的数据不会带过去(需要用 RedirectAttributes)
        return "redirect:/api/user/profile?id=" + user.getId();
    }
}

==

2.被@RequestMapping标注的类的方法参数

====

分类参数类型 / 注解作用说明示例代码
请求数据提取@PathVariable获取 URL 路径中的占位符变量@PathVariable Long id
@RequestParam获取请求参数(URL 查询参数或表单参数)@RequestParam String name
@RequestParam + required=false获取非必填参数,未传时为 null@RequestParam(required=false) Integer age
@RequestParam + defaultValue获取参数,未传时使用默认值@RequestParam(defaultValue="1") int page
@RequestBody将请求体(JSON/XML)绑定到 Java 对象@RequestBody User user
@RequestHeader获取 HTTP 请求头中的值@RequestHeader("User-Agent") String ua
@CookieValue获取 Cookie 中的值@CookieValue("JSESSIONID") String jid
@ModelAttribute将请求参数绑定到对象,并自动放入 Model@ModelAttribute User user
@MatrixVariable获取矩阵变量(/cars;color=red@MatrixVariable String color
数据输出Model添加键值对数据到 Request 作用域model.addAttribute("key", value)
ModelMapModel 的实现类,用法类似 MapmodelMap.put("key", value)
RedirectAttributes重定向时传递数据(Flash Attributes)redirectAttributes.addFlashAttribute("msg", "成功")
验证与错误BindingResult接收 @Valid 校验的结果(错误信息)@Valid @RequestBody User user, BindingResult result
ErrorsBindingResult 的父接口,功能相同Errors errors
Servlet APIHttpServletRequest获取原生请求对象HttpServletRequest request
HttpServletResponse获取原生响应对象HttpServletResponse response
HttpSession获取当前会话(自动创建)HttpSession session
WebRequestSpring 封装的通用请求接口WebRequest webRequest
NativeWebRequest更底层的 Web 请求接口NativeWebRequest nativeRequest
输入输出流InputStream / Reader获取请求体的原始输入流InputStream inputStream
OutputStream / Writer获取响应的原始输出流OutputStream outputStream
安全与上下文Principal获取当前登录用户信息(配合 Spring Security)Principal principal
Locale获取当前请求的语言区域Locale locale
TimeZone获取当前请求的时区TimeZone timeZone
Spring DataPageable分页参数(需引入 Spring Data)Pageable pageable(自动解析 ?page=0&size=10
Sort排序参数(需引入 Spring Data)Sort sort(自动解析 ?sort=id,desc
通用封装HttpEntity<T>封装整个请求(Headers + Body)HttpEntity<User> entity
RequestEntity<T>HttpEntity 的子类,带更多请求信息RequestEntity<User> requestEntity
构建工具UriComponentsBuilder用于构建动态 URLUriComponentsBuilder builder
其他可选MultipartFile接收上传的文件(需搭配 @RequestParam@RequestParam MultipartFile file
BindingAwareModelMapModel 的具体实现类,较少直接用BindingAwareModelMap modelMap
ModelAttribute + @SessionAttributes配合 Session 使用的属性类上标注 @SessionAttributes,方法参数直接用 @ModelAttribute 取

====

特别提醒

  1. BindingResult / Errors 必须紧跟在被校验的实体参数后面,否则校验异常会直接抛出,无法进入方法体。

  2. Pageable 和 Sort 需要引入 Spring Data 相关依赖才能使用。

  3. 以上参数可以任意组合,顺序基本自由(除了 BindingResult 必须紧跟被校验对象)。

===

3.BindingResult / Errors 必须紧跟在被校验的实体参数后面(代码说明)

===

正确代码

@RestController
@RequestMapping("/api/user")
public class UserController {

    @PostMapping("/register")
    public String register(
            // 1. 被校验的实体
            @Valid @RequestBody User user,
            // 2. BindingResult 必须紧跟其后(中间不能插入任何其他参数)
            BindingResult result,
            // 3. 其他参数可以放在 BindingResult 后面
            HttpSession session) {

        // 校验是否有错误
        if (result.hasErrors()) {
            // 获取所有错误信息并返回
            String errors = result.getAllErrors()
                    .stream()
                    .map(ObjectError::getDefaultMessage)
                    .collect(Collectors.joining(", "));
            return "校验失败:" + errors;
        }

        // 业务逻辑(只有校验通过才会执行到这里)
        userService.register(user);
        session.setAttribute("userId", user.getId());
        return "注册成功";
    }
}

错误代码

===

@PostMapping("/register")
public String register(
        @Valid @RequestBody User user,
        // 错误:中间插入了 HttpSession 参数
        HttpSession session,      // ← 这里插入了其他参数,打断了顺序
        BindingResult result) {   // ← 没有紧跟被校验对象

    // 即使这里有 if(result.hasErrors()),也永远不会执行到这里
    if (result.hasErrors()) {
        return "校验失败";
    }
    // 业务逻辑...
    return "注册成功";
}

===

■自定义校验器(Validator 实现类)

===

层级类名关键注解
控制层LoginController@Controller@InitBinder("loginForm")@PostMapping
业务校验层LoginFormValidator@Component(实现 Validator 接口)
数据传输层LoginForm无注解(纯POJO)

XXXXForm

package com.example.demo.dto;

public class LoginForm {
    private String username;
    private String password;
    private String confirmPassword;

    // getter 和 setter(必须要有,否则绑定失败)
    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; }

    public String getConfirmPassword() { return confirmPassword; }
    public void setConfirmPassword(String confirmPassword) { this.confirmPassword = confirmPassword; }
}

LoginFormValidator

package com.example.demo.validator;

import com.example.demo.dto.LoginForm;
import org.springframework.stereotype.Component; // 注意这个注解
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

@Component // 交给Spring管理,以便注入到Controller
public class LoginFormValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        // 指定只校验 LoginForm 类型的对象
        return LoginForm.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        LoginForm form = (LoginForm) target;

        // 1. 校验用户名
        String username = form.getUsername();
        if (username == null || username.trim().isEmpty()) {
            // rejectValue(字段名, 错误码, 默认提示信息)
            errors.rejectValue("username", "username.empty", "用户名不能为空");
        }

        // 2. 校验密码
        String password = form.getPassword();
        if (password == null || password.length() < 6) {
            errors.rejectValue("password", "password.length", "密码长度至少为6位");
        }

        // 3. 跨字段校验:密码和确认密码是否一致
        if (password != null && !password.equals(form.getConfirmPassword())) {
            errors.rejectValue("confirmPassword", "password.mismatch", "两次输入的密码不一致");
        }
    }
}

控制层代码

package com.example.demo.controller;

import com.example.demo.dto.LoginForm;
import com.example.demo.validator.LoginFormValidator;
import org.springframework.beans.factory.annotation.Autowired; // 导入此注解
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/auth")
public class LoginController {

    @Autowired  // ★ 明确标识这是注入进来的
    private LoginFormValidator loginFormValidator; // ★ 去掉了 final

    @InitBinder("loginForm")
    public void initLoginFormBinder(WebDataBinder binder) {
        // 直接使用注入的实例
        binder.setValidator(loginFormValidator);
    }

    @GetMapping("/login")
    public String showLoginPage(Model model) {
        model.addAttribute("loginForm", new LoginForm());
        return "login";
    }

    @PostMapping("/login")
    public String doLogin(
            @ModelAttribute("loginForm") LoginForm loginForm,
            BindingResult bindingResult,
            Model model) {

        if (bindingResult.hasErrors()) {
            return "login";
        }
        System.out.println("登录成功:" + loginForm.getUsername());
        return "redirect:/home";
    }
}

xxxx

====

==

package com.example.demo.controller;

import com.example.demo.model.LoginRequest;
import com.example.demo.model.PageRequest;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;

@Controller  // 注意:是 @Controller,不是 @RestController
@RequestMapping("/api/user")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    /**
     * POST 请求,接收 JSON 请求体,返回 JSON 响应
     * 使用 @RequestMapping 指定 method = RequestMethod.POST
     * 显式使用 @RequestBody 和 @ResponseBody
     */
    @RequestMapping(value = "/page", method = RequestMethod.POST)
    @ResponseBody  // 显式声明:返回 JSON 格式
    public Map<String, Object> getUserPage(@RequestBody PageRequest pageRequest) {
        Map<String, Object> result = new HashMap<>();
        
        try {
            // 从请求体中获取参数
            int page = pageRequest.getPage();
            int size = pageRequest.getSize();
            String keyword = pageRequest.getKeyword();
            
            // 调用 Service 层查询数据
            Page<User> userPage = userService.findPage(page, size, keyword);
            
            // 组装返回数据
            result.put("code", 200);
            result.put("message", "查询成功");
            result.put("data", userPage.getContent());
            result.put("total", userPage.getTotalElements());
            result.put("page", page);
            result.put("size", size);
            
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", "查询失败:" + e.getMessage());
        }
        
        return result;  // @ResponseBody 会将 Map 自动转为 JSON
    }
    
    /**
     * 另一个示例:登录接口
     * 使用 @RequestMapping 指定 method = RequestMethod.POST
     */
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody  // 显式声明返回 JSON
    public Map<String, Object> login(@RequestBody LoginRequest loginRequest) {
        Map<String, Object> result = new HashMap<>();
        
        if ("admin".equals(loginRequest.getUsername()) 
            && "123456".equals(loginRequest.getPassword())) {
            result.put("code", 200);
            result.put("message", "登录成功");
            result.put("token", "abc123xyz");
        } else {
            result.put("code", 401);
            result.put("message", "用户名或密码错误");
        }
        
        return result;
    }
}

====

■相关标签作用说明

====

Spring MVC 注解完整对照表

===

注解分类作用等价/替代关系使用位置自动转JSON适用场景示例代码
@Controller控制器声明声明该类为Spring MVC控制器类级别❌ 否返回视图(JSP/HTML)@Controller public class UserController {}
@RestController控制器声明@Controller + @ResponseBody 的合体@Controller + @ResponseBody类级别✅ 是(自动)前后端分离,返回JSON@RestController public class UserController {}
@RequestMapping请求映射将请求映射到控制器方法基础注解类/方法级别取决于类注解通用映射,可指定多种请求方式@RequestMapping(value="/page", method=POST)
@GetMapping请求映射处理GET请求@RequestMapping(method=GET)方法级别取决于类注解查询数据@GetMapping("/user/{id}")
@PostMapping请求映射处理POST请求@RequestMapping(method=POST)方法级别取决于类注解新增/登录/提交数据@PostMapping("/login")
@PutMapping请求映射处理PUT请求@RequestMapping(method=PUT)方法级别取决于类注解更新数据@PutMapping("/user/{id}")
@DeleteMapping请求映射处理DELETE请求@RequestMapping(method=DELETE)方法级别取决于类注解删除数据@DeleteMapping("/user/{id}")
@PatchMapping请求映射处理PATCH请求@RequestMapping(method=PATCH)方法级别取决于类注解部分更新数据@PatchMapping("/user/{id}")
@RequestBody参数绑定将HTTP请求体(JSON)绑定到Java对象方法参数上❌ 否(是接收)POST/PUT请求,接收JSON数据public Result method(@RequestBody User user)
@RequestParam参数绑定从URL查询参数或表单数据获取参数方法参数上❌ 否(是接收)GET请求查询参数、表单提交public Result method(@RequestParam int page)
@PathVariable参数绑定从URL路径中获取参数方法参数上❌ 否(是接收)RESTful风格URLpublic Result method(@PathVariable Long id)
@ModelAttribute参数绑定将请求参数绑定到Java对象(表单数据)无(不加任何注解时Spring默认也是此行为)方法参数上❌ 否(是接收)表单数据提交,自动封装对象public Result method(@ModelAttribute User user)
@ResponseBody响应处理将方法返回值自动转为JSON写入响应体可被@RestController替代方法/类级别✅ 是(自动)返回JSON数据@ResponseBody public Result method()
@Autowired依赖注入自动注入Spring Bean字段/构造方法/Setter❌ 否注入Service、DAO等@Autowired private UserService userService;

注解组合替换关系表

===

组合写法等价简化写法说明
@Controller + 每个方法 @ResponseBody@RestController类级别统一返回JSON
@RequestMapping(value="/page", method=RequestMethod.POST)@PostMapping("/page")专门处理POST请求
@RequestMapping(value="/page", method=RequestMethod.GET)@GetMapping("/page")专门处理GET请求
@RequestMapping(value="/page", method=RequestMethod.PUT)@PutMapping("/page")专门处理PUT请求
@RequestMapping(value="/page", method=RequestMethod.DELETE)@DeleteMapping("/page")专门处理DELETE请求

===

请求参数接收方式对比表

====

方式注解数据来源请求示例后端代码
JSON请求体@RequestBodyHTTP Body{"username":"admin","password":"123"}public Result login(@RequestBody User user)
URL查询参数@RequestParamURL ? 后面/user?page=1&size=10public Result list(@RequestParam int page, @RequestParam int size)
路径参数@PathVariableURL路径中/user/123public Result get(@PathVariable Long id)
表单数据@ModelAttribute表单提交username=admin&password=123public Result login(@ModelAttribute User user)

JSON自动转换相关注解

=====

注解是否自动转JSON需要Jackson依赖说明
@RestController✅ 是✅ 需要类级别所有方法都转JSON
@ResponseBody✅ 是✅ 需要方法级别或类级别
@Controller(不加@ResponseBody)❌ 否❌ 不需要返回视图,不转JSON
@RequestBody❌ 否(是接收JSON)✅ 需要将JSON转为Java对象

按使用场景推荐注解组合

场景推荐注解组合说明
前后端分离(纯JSON接口)@RestController + @PostMapping/@GetMapping + @RequestBody全部返回JSON
传统Web应用(返回页面)@Controller + @GetMapping/@PostMapping返回JSP/HTML
混合应用(页面+JSON)@Controller + @ResponseBody(特定方法)部分返回页面,部分返回JSON
RESTful API@RestController + @GetMapping/@PostMapping/@PutMapping/@DeleteMapping + @PathVariable标准REST风格
文件上传@PostMapping + @RequestParam("file") MultipartFile file接收文件
表单提交@PostMapping + @ModelAttribute 或直接使用对象自动封装表单数据

====

=====

注解组合替换关系表

===

组合写法等价简化写法说明
@Controller + 每个方法 @ResponseBody@RestController类级别统一返回JSON
@RequestMapping(value="/page", method=RequestMethod.POST)@PostMapping("/page")专门处理POST请求
@RequestMapping(value="/page", method=RequestMethod.GET)@GetMapping("/page")专门处理GET请求
@RequestMapping(value="/page", method=RequestMethod.PUT)@PutMapping("/page")专门处理PUT请求
@RequestMapping(value="/page", method=RequestMethod.DELETE)@DeleteMapping("/page")专门处理DELETE请求

====

请求参数接收方式对比表

=====

方式注解数据来源请求示例后端代码
JSON请求体@RequestBodyHTTP Body{"username":"admin","password":"123"}public Result login(@RequestBody User user)
URL查询参数@RequestParamURL ? 后面/user?page=1&size=10public Result list(@RequestParam int page, @RequestParam int size)
路径参数@PathVariableURL路径中/user/123public Result get(@PathVariable Long id)
表单数据@ModelAttribute表单提交username=admin&password=123public Result login(@ModelAttribute User user)

===

===

         

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐