导语

Spring MVC 是基于 MVC(Model-View-Controller)设计模式的 Java Web 开发框架,注解驱动是其核心特性,可替代传统 XML 配置,熟悉注解驱动可以大大简化我们的开发流程。本文按“控制器标识、请求映射、参数绑定、模型数据传递、响应处理、异常处理、核心依赖”七大核心场景分类,对 Spring MVC 常用注解进行详细解析——每类注解均先给出官方学术定义(保障专业性),再通过生活化场景比喻(降低理解门槛)拆解作用,两种方式帮助理解,适用于 Java 开发学习者及工程实践参考。

一、控制器标识类注解(MVC 中 C 层核心)

核心作用:标识类为 Spring MVC 控制器,承担“接收请求、分发处理、返回结果”职责,被标注类会被 Spring IoC 容器扫描实例化。

1. @Controller

官方学术定义

Spring 的 @Component 衍生注解,用于标注类为 Spring MVC 的控制器(Controller),是 MVC 架构中 C 层的核心标识。被标注的类会被 Spring IoC 容器扫描并实例化,类内方法可通过请求映射注解绑定 HTTP 请求;方法返回值默认解析为视图名称(配合视图解析器渲染页面),承担“接收请求、分发处理、返回视图”的核心职责。

生动通俗解释

可以将 Spring MVC 比作“互联网餐厅”:@Controller 就是给普通员工(类)发“大堂经理上岗证”——有了这个证,该员工只负责对接顾客(前端请求),不再干后厨(Service)、洗碗(DAO)的活。顾客所有点餐请求,必须先找这个“持证经理”,由他分配给对应的服务员(控制器方法)处理。

特点:默认管“堂食”(返回页面),比如顾客点完餐,经理会让后厨做好菜(处理业务),再让传菜员把菜(页面+数据)端给顾客。

2. @RestController

官方学术定义

Spring 4.0 新增的组合注解(等价于 @Controller + @ResponseBody),用于标注 RESTful 风格的控制器类。被标注的类中,所有方法的返回值默认直接序列化为 JSON/XML 等格式的响应体(Response Body),而非解析为视图名称;适用于前后端分离的接口开发,无需在方法上额外添加 @ResponseBody。

生动通俗解释

延续“互联网餐厅”场景:@RestController 是“外卖专属大堂经理上岗证”——这个经理不管堂食(返回页面),只处理外卖订单(前后端分离接口)。顾客(前端)点外卖,经理对接后,直接让后厨把菜(数据)打包成外卖盒(JSON 格式),通过快递(HTTP 响应)寄给顾客,不用走堂食的“摆台、传菜”流程(渲染页面)。

例子:前端调接口查用户信息,@RestController 的方法直接返回 JSON,前端自己渲染,后端不用管页面。

二、请求映射类注解(绑定请求与控制器方法)

核心作用:建立 HTTP 请求(路径、方式)与控制器方法的映射关系,实现“请求精准路由”。

1. @RequestMapping

官方学术定义

用于将 HTTP 请求映射到控制器方法的核心注解,可标注在类/方法上:标注在类上,为控制器所有方法设置“基础请求路径”(命名空间);标注在方法上,指定具体请求路径,同时可通过 method(限定请求方式:GET/POST/PUT/DELETE)、params(限定请求参数)、headers(限定请求头)、produces/consumes(指定请求/响应媒体类型)细化匹配规则。

生动通俗解释

@RequestMapping 是给“大堂经理+服务员”贴“专属服务标签”:贴在类上,比如给经理贴“一楼点餐区”,意味着所有找这个经理的顾客,必须先到一楼(基础路径);贴在方法上,给经理手下的服务员贴“牛肉面专属窗口”,还能备注“只接现金付款(method=GET)、必须报手机号(params=phone)”。

示例代码:


@Controller
@RequestMapping("/user") // 类上:所有/user开头的请求找这个控制器
public class UserController {
    // 方法上:/user/get的GET请求找这个方法
    @RequestMapping(value="/get", method=RequestMethod.GET)
    public String getUser() { ... }
}

2. @GetMapping / @PostMapping / @PutMapping / @DeleteMapping

官方学术定义

Spring 4.3 新增的 @RequestMapping 衍生注解,是“请求方式限定版”的语法糖:

@GetMapping:等价于 @RequestMapping(method = RequestMethod.GET),仅匹配 GET 请求,适用于查询数据(无副作用);

  • @PostMapping:等价于 @RequestMapping(method = RequestMethod.POST),仅匹配 POST 请求,适用于创建/提交数据(有副作用,数据不在 URL 暴露);

  • @PutMapping:等价于 @RequestMapping(method = RequestMethod.PUT),仅匹配 PUT 请求,RESTful 中约定用于全量更新资源(更新所有字段);

  • @DeleteMapping:等价于 @RequestMapping(method = RequestMethod.DELETE),仅匹配 DELETE 请求,RESTful 中约定用于删除资源。

生动通俗解释

这四个注解是“餐厅专用窗口”,分工明确:

  • @GetMapping:“查询窗口”——只接顾客“问事儿”的请求(比如“牛肉面多少钱?”),对应查数据;

  • @PostMapping:“下单窗口”——只接顾客“提交订单”的请求(比如“点一碗牛肉面加蛋”),对应新增数据;

  • @PutMapping:“整单修改窗口”——顾客想把“微辣不加蛋”改成“特辣加双蛋”(全量改订单),对应全量更新数据;

  • @DeleteMapping:“取消订单窗口”——顾客不想吃了,取消整单,对应删除数据。

示例:@GetMapping("/user/123") = “查订单号 123 的外卖进度”;@DeleteMapping("/user/123") = “取消订单号 123 的外卖”。

三、请求参数绑定注解(提取请求中的数据)

核心作用:从 HTTP 请求(参数、路径、请求体、请求头、Cookie)中提取数据,绑定到控制器方法形参,供业务逻辑处理。

1. @RequestParam

官方学术定义

用于将 HTTP 请求中的查询参数(Query String)/表单参数绑定到控制器方法形参。可指定 name/value(映射请求参数名与形参名)、required(是否必传,默认 true)、defaultValue(默认值),解决“请求参数名与形参名不一致”或“可选参数”问题。

生动通俗解释

@RequestParam 是“服务员记订单备注”:顾客点餐时喊“加个蛋”,但菜单上的备注项叫“附加食材”——服务员(@RequestParam)把“加个蛋”(请求参数)对应写到“附加食材”(方法形参)的位置;还能规定“这个备注必须写”(required=true),或“没写就默认加青菜”(defaultValue="青菜")。

示例代码:


// 顾客没说加啥,默认加青菜;说了加蛋,就传“蛋”
public String orderNoodle(@RequestParam(name="add", required=false, defaultValue="青菜") String addFood) { ... }

2. @PathVariable

官方学术定义

用于将 HTTP 请求 URL 中的路径变量(URI 模板变量)绑定到控制器方法形参,适用于 RESTful 风格 URL(如 /user/{id})。可指定 name/value 映射变量名,实现动态 URL 参数的提取。

生动通俗解释

@PathVariable 是“服务员核对订单号”:餐厅外卖 URL 是 /order/{orderId},{orderId} 是动态订单号(比如 /order/123)——服务员(@PathVariable)把 URL 里的 123 抠出来,对应到方法里的 orderId 参数,知道要查/改/删哪个订单。

示例代码:


// URL是/order/123,id就等于123
@GetMapping("/order/{orderId}")
public String getOrder(@PathVariable("orderId") String id) { ... }

3. @RequestBody

官方学术定义

用于将 HTTP 请求体(Request Body)中的 JSON/XML 等格式数据反序列化为 Java 对象(POJO),适用于 POST/PUT 请求传递复杂数据(如新增用户时传完整用户信息)。要求请求的 Content-Type 为 application/json 等非表单格式。

生动通俗解释

@RequestBody 是“服务员收完整的订单单”:顾客点外卖时,不是只说“加蛋”,而是递来一张完整订单单(JSON),上面写了面的种类、口味、地址、电话(复杂数据)——服务员把这张单子完整收下来,转换成餐厅内部的“订单档案”(Java 对象),方便后厨处理。

示例代码:


// 前端传JSON:{"noodleType":"牛肉面","taste":"微辣"}
// 直接转换成Order对象
@PostMapping("/order")
public String createOrder(@RequestBody Order order) { ... }

4. @RequestHeader

官方学术定义

用于将 HTTP 请求头(Request Header)中的指定字段绑定到控制器方法形参,可指定 name/value(头字段名)、required、defaultValue,适用于获取请求头信息(如 Token、Content-Type、User-Agent)。

生动通俗解释

@RequestHeader 是“服务员查顾客的会员卡信息”:顾客点餐时,会员卡信息(请求头)附在订单上——服务员专门查这个会员卡字段,知道顾客是不是会员、有没有折扣(比如查 token 头字段验证身份)。

示例代码:


// 提取请求头里的token,验证是否是合法用户
public String checkMember(@RequestHeader("token") String token) { ... }

5. @CookieValue

官方学术定义

用于将 HTTP 请求中的 Cookie 值绑定到控制器方法形参,可指定 name/value(Cookie 名称)、required、defaultValue,适用于获取客户端存储的 Cookie(如登录状态、用户积分)。

生动通俗解释

@CookieValue 是“服务员查顾客的积分卡(Cookie)”:顾客之前来消费过,积分卡(Cookie)存在手机里(客户端)——这次点餐,服务员专门查这张积分卡,看看有多少积分能抵扣(比如查 user_score Cookie)。

示例代码:


// 提取Cookie里的user_score,知道顾客的积分
public String getScore(@CookieValue("user_score") Integer score) { ... }

四、模型数据传递注解(C 层向 V 层传数据)

核心作用:将控制器处理后的数据,传递到视图层(V 层),供页面渲染展示,或实现跨请求数据共享。

1. @ModelAttribute

官方学术定义

有两种使用方式:① 标注在方法上:该方法在控制器所有请求处理方法执行前执行,用于初始化公共模型数据(如加载今日特价、营业时间);② 标注在形参上:将请求参数绑定到指定 POJO,并将该对象放入 Model 中,供视图渲染使用。

生动通俗解释

@ModelAttribute 是“餐厅的餐前准备”:① 标注在方法上:后厨在所有顾客点餐前,先做好常备小菜(公共数据,比如“今日牛肉面特价 15 元”),不管哪个顾客来,都能看到;② 标注在形参上:顾客点牛肉面时,服务员把“面的种类、口味”填到统一的“点餐单模板”(POJO)里,再把模板放到前台(Model),供大堂展示(视图渲染)。

示例代码:


// 方式1:初始化公共数据(所有请求都能拿到todaySpecial)
@ModelAttribute
public void initData(Model model) {
    model.addAttribute("todaySpecial", "牛肉面特价15元");
}

// 方式2:绑定参数到Order对象,并放入Model
@PostMapping("/order")
public String submitOrder(@ModelAttribute Order order) { ... }

2. @SessionAttributes

官方学术定义

标注在控制器类上,指定需要存入 HttpSession 的模型数据名称,将 Model 中的指定属性同步到 Session 中,实现跨请求数据共享(如用户登录状态在多个请求中保持)。需配合 @ModelAttribute/Model 使用,使用后需手动清理 Session 避免内存泄漏。

生动通俗解释

@SessionAttributes 是“餐厅的会员档案柜(Session)”:给大堂经理(控制器)指定,把顾客的会员信息(模型数据)存到档案柜里——顾客这次点完餐,下次再来(跨请求),不用重新报会员号,经理直接从档案柜里查(比如存 userInfo 到 Session)。

示例代码:


@Controller
@SessionAttributes("userInfo") // 把userInfo存到Session
public class UserController {
    @GetMapping("/login")
    public String login(Model model) {
        // 同步到Session,后续请求可直接用
        model.addAttribute("userInfo", new User("张三", "会员"));
        return "index";
    }
}

3. @RequestAttribute

官方学术定义

用于将请求域(HttpServletRequest)中的属性绑定到控制器方法形参,适用于获取拦截器/过滤器提前存入请求域的属性(区别于 @RequestParam 获取请求参数)。

生动通俗解释

@RequestAttribute 是“服务员查前台提前放好的临时单据”:餐厅保安(拦截器)在顾客进大堂前,查了健康码,把结果(健康码状态)放到前台临时单据盒(请求域)——服务员直接从盒子里拿,不用再问顾客要健康码。

示例代码:


// 获取拦截器存入的health_status,知道顾客健康码状态
public String checkHealth(@RequestAttribute("health_status") String status) { ... }

五、响应处理注解(C 层返回结果给前端)

核心作用:控制控制器方法的返回结果类型(响应体/视图)、HTTP 响应状态码,适配不同交互场景(页面渲染/接口调用)。

1. @ResponseBody

官方学术定义

标注在控制器方法/类上,表示方法返回值直接作为 HTTP 响应体(Response Body)返回,而非解析为视图名称。返回值会被 HttpMessageConverter 序列化为 JSON/XML,适用于 AJAX/RESTful 接口(@RestController 已包含该注解)。

生动通俗解释

@ResponseBody 是“直接打包外卖,不摆堂食”:经理处理完请求后,不把数据交给传菜员摆到餐桌上(渲染视图),而是直接打包成外卖盒(JSON),通过快递(HTTP 响应)寄给顾客(比如返回用户数据的 JSON 串,前端直接用)。

示例代码:


// 直接返回JSON:{"name":"张三","age":20}
@GetMapping("/user/123")
@ResponseBody
public User getUser() {
    return new User("张三", 20);
}

2. @ResponseStatus

官方学术定义

标注在控制器方法/异常处理方法上,指定 HTTP 响应的状态码和原因短语(reason)。可手动设置响应状态(如 201 Created 表示资源创建成功、404 Not Found 表示资源不存在),覆盖默认状态码,使接口符合 HTTP 规范。

生动通俗解释

@ResponseStatus 是“给外卖订单贴状态标签”:顾客下单后,餐厅给订单贴“已接单(200)”“已创建(201)”“没找到订单(404)”的标签——前端一看就知道请求结果(比如新增用户成功,返回 201,告诉前端“资源创建好了”)。

示例代码:


// 新增用户成功,返回201状态码+提示
@PostMapping("/user")
@ResponseStatus(code = HttpStatus.CREATED, reason = "用户创建成功")
public void createUser(@RequestBody User user) { ... }

六、异常处理注解(处理请求中的异常)

核心作用:统一捕获并处理控制器执行过程中抛出的异常,避免异常直接暴露给前端,提升接口健壮性。

1. @ExceptionHandler

官方学术定义

标注在控制器类中的方法上,指定该方法处理当前控制器抛出的指定类型异常。当控制器方法抛出匹配异常时,该方法触发,可自定义异常响应(如返回错误信息、跳转错误页面),实现局部异常处理。

生动通俗解释

@ExceptionHandler 是“大堂经理的专属售后员”:这个售后员只处理自己经理手下的顾客投诉(当前控制器的异常)——比如顾客点的牛肉面卖完了(抛出 NoFoodException),售后员专门处理这个投诉,告诉顾客“抱歉,可换拉面”,不用找餐厅总售后。

示例代码:


@Controller
public class OrderController {
    // 专门处理NoFoodException
    @ExceptionHandler(NoFoodException.class)
    @ResponseBody
    public String handleNoFood(NoFoodException e) {
        return "错误:" + e.getMessage();
    }

    @GetMapping("/order/noodle")
    public String orderNoodle() {
        throw new NoFoodException("牛肉面已售罄");
    }
}

2. @ControllerAdvice

官方学术定义

@Component 衍生注解,标注全局异常处理类,结合 @ExceptionHandler 使用。该类中的 @ExceptionHandler 方法会处理所有控制器(或指定包下控制器)抛出的异常,实现全局异常统一处理,避免重复编写异常逻辑。

生动通俗解释

@ControllerAdvice 是“餐厅的总售后部”:管所有大堂经理的售后问题——不管哪个经理手下的顾客投诉(任何控制器抛异常),总售后都能处理(比如所有控制器的空指针异常,统一返回“系统出错,请稍后重试”),不用每个经理都配售后员。

示例代码:


@ControllerAdvice // 全局异常处理
public class GlobalExceptionHandler {
    // 处理所有RuntimeException
    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public String handleRuntimeException(RuntimeException e) {
        return "全局错误:" + e.getMessage();
    }
}

七、MVC 中高频依赖注解

核心作用:实现控制器与服务层(Service)的解耦,通过依赖注入(DI)自动装配 Bean,简化对象创建与调用。

1. @Autowired

官方学术定义

Spring 核心注解,基于依赖注入(DI)机制,自动装配 Spring IoC 容器中的匹配 Bean 到当前类的字段/构造方法/方法中。在 MVC 的 Controller 层中,常用于注入 Service 层 Bean,实现 C 层与 Service 层解耦,无需手动创建 Service 实例。

生动通俗解释

@Autowired 是“大堂经理的专属后厨联络员”:经理不用自己跑后厨找厨师(手动 new Service),只要贴个 @Autowired,餐厅人事部(Spring IoC)就会自动派一个后厨联络员(Service Bean)给经理——经理想做菜(调用 Service 方法),直接喊联络员就行。

示例代码:


@Controller
public class UserController {
    @Autowired
    private UserService userService; // 自动注入UserService

    @GetMapping("/user/{id}")
    public String getUser(@PathVariable("id") Integer id) {
        userService.getUserById(id); // 调用Service方法
        return "user";
    }
}

2. @Component

官方学术定义

Spring 核心注解,标注普通 Spring Bean,是 @Controller/@Service/@Repository 的父注解。被标注的类会被 Spring 扫描并实例化,纳入 IoC 容器管理,是依赖注入的基础。

生动通俗解释

@Component 是“餐厅员工的基础工牌”:不管是大堂经理(@Controller)、后厨厨师(@Service)、仓库管理员(@Repository),都得先有这个基础工牌,才能被餐厅人事部(Spring IoC)认可,纳入员工体系,随时能被调用。

结语

Spring MVC 注解主要是围绕“请求接收-参数提取-业务处理-数据传递-响应返回-异常处理”全流程设计,核心可归纳为三类:① 标识类注解(@Controller/@RestController):定义组件角色;② 交互类注解(@RequestMapping/@RequestBody 等):实现前后端数据交互;③ 辅助类注解(@Autowired/@ControllerAdvice 等):简化开发、统一处理。

掌握这些注解的使用场景与核心作用,可快速搭建规范的 Spring MVC 项目,实现前后端高效交互与代码解耦。在我们的实际开发中,可以根据“是否前后端分离、是否 RESTful 风格、是否需要跨请求共享数据”等场景,灵活选择注解组合。

文章在这里结束,我是一白,一个一直在学习的小白,希望我的文章可以为正在学习的你带来帮助,增添你对学习编程编程的兴趣。

Logo

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

更多推荐