☰
XMall 电商项目 SpringMVC 注解实战全解:从请求映射到全局异常处理的完整技术指南
2026/10/7 16:24:31 网站建设 项目流程
  • 电商
  • 后端
  • 微服务

【免费下载链接】xmall

基于SOA架构的分布式电商购物商城 前后端分离 前台商城:Vue全家桶 后台管理系统:Dubbo/SSM/Elasticsearch/Redis/MySQL/ActiveMQ/Shiro/Zookeeper等

项目地址:https://gitcode.com/gh_mirrors/xm/xmall
点击查看免费下载

本文以 XMall(基于 SOA 架构的分布式电商购物商城)后台与前台 Web 模块的真实源码为依托,系统梳理 SpringMVC 核心注解的用法与底层机制。读者将掌握@Controller/@RestController的职责分工、@RequestMapping各属性(value/method/params/headers/produces)的匹配规则、参数绑定注解(@RequestParam/@PathVariable/@RequestBody/@CookieValue)的实战姿势,以及基于@ExceptionHandler构建全局异常处理体系的完整方案。

一、写在前面:XMall 中 SpringMVC 的位置

XMall 是前后端分离的分布式电商项目,前台商城使用 Vue 全家桶,后台管理系统则基于 Dubbo/SSM/Elasticsearch/Redis/MySQL/ActiveMQ/Shiro/Zookeeper 等技术栈。在后台管理端与前台 Web 端,SpringMVC 扮演 Web 层的核心角色:

  • 前台 Web 端:xmall-front-web 暴露/goods/**、/member/**等 REST 接口供 Vue 页面调用;
  • 后台管理端:xmall-manager-web 通过/item/**、/order/**等接口支撑管理后台的 JSP 页面与数据交互。

两个工程的请求入口均由 web.xml 中的DispatcherServlet承接:配置contextConfigLocation指向 springmvc.xml,url-pattern设为/拦截所有请求,load-on-startup设为 1 保证容器启动即加载。SpringMVC 的“注解驱动”开发模式,正是从这一入口开始贯穿整个 Web 层。下面以注解为主线展开。

二、控制器定义注解:@Controller 与 @RestController

@Controller

通过@Controller标注即可将 class 定义为一个 controller 类,交由 Spring 容器管理,并参与 SpringMVC 的请求分发。通常配合视图解析器返回 JSP 页面名,由InternalResourceViewResolver完成视图渲染。

@RestController

@RestController是@Controller与@ResponseBody的组合注解:类上标注后,该类所有方法的返回值都会直接写入 HTTP Response Body,而不是走视图解析。在 XMall 前后端分离架构中,前台与后台的控制器几乎全部采用@RestController:

// xmall-front-web/src/main/java/cn/exrick/front/controller/GoodsController.java @RestController @Api(description = "商品页面展示") public class GoodsController { ... }
// xmall-front-web/src/main/java/cn/exrick/front/controller/MemberController.java @RestController @Api(description = "会员注册登录") public class MemberController { ... }

商品管理端同样如此,ItemController.java 以@RestController标注并配合@Api(Swagger)注解生成接口文档。

从源码结构看,XMall 中的@RestController返回值统一封装为Result/DataTablesResult等 JSON 结构(如 ResultUtil),由 Jackson 等 JSON 转换器序列化后返回前端,这正体现了@RestController在前后端分离项目中的典型用法——控制器只负责数据契约,视图层完全交给前端框架。

三、请求映射注解:@RequestMapping 的五大属性

@RequestMapping用于将 URL 映射到处理方法,其核心属性如下。

value —— 需要匹配的 URL 格式

@RequestMapping(value = "/member/add", method = RequestMethod.POST)

method —— 所需处理请求的 HTTP 协议(get、post、put、delete 等)

method用于限定请求方法,不匹配时 SpringMVC 返回 405。上面示例即表示只有 POST 请求/member/add才会进入该方法。

params —— 请求参数约束

格式为"paramname=paramvalue"或"paramname!=paramvalue",表示参数必须等于某值、不等于某值或必须存在时才进入此映射方法;不填写时表明不限制。示例:当请求/testParams.do?param1=value1&param2=value2时能正确访问到testParams方法:

@RequestMapping(value = "testParams", params = { "param1=value1", "param2", "!param3" }) public String testParams() { System.out.println("test Params..........."); return "testParams"; }

headers —— 请求头约束

用来限定对应 request 请求的 headers 中必须包括的内容。例如headers={"Connection=keep-alive"}表示请求头中 connection 的值必须为 keep-alive。当请求/testHeaders.do时,只有当请求头包含Accept信息且请求的 host 为localhost时才能正确访问到testHeaders方法:

@RequestMapping(value = "testHeaders", headers = { "host=localhost", "Accept" }) public String testHeaders() { return "headers"; }

produces —— 指定返回的内容类型

指定返回的内容类型,仅当 request 请求头中的 Accept 类型中包含该指定类型才返回;同时该值也会写入响应的Content-Type头,控制返回内容的媒体类型与字符集:

@RequestMapping(value = "testProduces", produces = "text/plain;charset=utf-8") @RequestMapping(value = "testProduces", produces = "application/json;charset=utf-8") @RequestMapping(value = "testProduces", produces = "application/xml;charset=utf-8")

XMall 实战印证:produces在前台商品快速搜索接口中得到直接应用——GoodsController.java 中:

@RequestMapping(value = "/goods/quickSearch", produces = "text/plain;charset=UTF-8", method = RequestMethod.GET) @ApiOperation(value = "快速搜索") public String getQuickSearch(@RequestParam(defaultValue = "") String key){ return searchService.quickSearch(key); }

这里显式声明以text/plain;charset=UTF-8返回纯文本,同时解决了中文乱码问题,是produces在真实项目中的典型用法。

四、组合映射注解:@GetMapping / @PostMapping / @PutMapping / @DeleteMapping

四个组合注解分别映射 HTTP 的 get、post、put、delete 请求。@DeleteMapping等同于@RequestMapping(value = "/member/add", method = RequestMethod.DELETE),其余同理,是@RequestMapping(method=...)的语义化简写。

虽然 XMall 的前台控制器主要使用完整的@RequestMapping(value = ..., method = RequestMethod.X)写法(这是 SSM 项目中较常见的风格),但从 Spring 4.3 开始,官方推荐使用组合注解,二者的等价关系如下:

组合注解等价写法
@GetMapping("/url")@RequestMapping(value = "/url", method = RequestMethod.GET)
@PostMapping("/url")@RequestMapping(value = "/url", method = RequestMethod.POST)
@PutMapping("/url")@RequestMapping(value = "/url", method = RequestMethod.PUT)
@DeleteMapping("/url")@RequestMapping(value = "/url", method = RequestMethod.DELETE)

XMall 实战印证:商品管理端 ItemController.java 完整演示了 PUT 与 DELETE 的语义化用法:

// 下架商品(PUT 语义:更新状态) @RequestMapping(value = "/item/stop/{id}", method = RequestMethod.PUT) public Result<TbItem> stopItem(@PathVariable Long id){ TbItem tbItem = itemService.alertItemState(id, 0); return new ResultUtil<TbItem>().setData(tbItem); } // 删除商品(DELETE 语义:支持批量删除,路径参数为数组) @RequestMapping(value = "/item/del/{ids}", method = RequestMethod.DELETE) public Result<TbItem> deleteItem(@PathVariable Long[] ids){ for(Long id : ids){ itemService.deleteItem(id); } return new ResultUtil<TbItem>().setData(null); }

可以看到 REST 语义被贯彻到了 URL 设计与方法选择中:查询用 GET、新增用 POST、状态变更用 PUT、删除用 DELETE,这正是@RequestMapping(method=...)在实际架构中的价值。

五、参数绑定注解:@RequestParam、@PathVariable、@RequestBody、@CookieValue

5.1 @RequestParam —— 绑定请求参数

属性说明:

  • value:对应表单 name 空间的值(即请求参数名);
  • required:是否允许为空(默认为 true,缺省时参数缺失会报 400 错误);
  • defaultValue:默认值(设置后 required 自动失效,参数缺失时使用默认值)。
@RequestMapping("requestParam") public String testRequestParam(@RequestParam(required = false) String name, @RequestParam("age") int age) { return "requestParam"; }

XMall 实战印证:分页、排序、筛选等列表查询参数最适合用@RequestParam配合默认值,见 GoodsController.java:

@RequestMapping(value = "/goods/allGoods", method = RequestMethod.GET) public Result<AllGoodsResult> getAllProduct(@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "20") int size, @RequestParam(defaultValue = "") String sort, @RequestParam(defaultValue = "") Long cid, @RequestParam(defaultValue = "-1") int priceGt, @RequestParam(defaultValue = "-1") int priceLte){ AllGoodsResult allGoodsResult = contentService.getAllProduct(page, size, sort, cid, priceGt, priceLte); return new ResultUtil<AllGoodsResult>().setData(allGoodsResult); }

管理端 ItemController.java 甚至直接绑定 DataTables 表格的搜索框与排序列名(search[value]、order[0][column]、order[0][dir]),让前端表格组件与后端参数无缝对接。

5.2 @PathVariable —— 绑定 URL 路径模板变量

@PathVariable获得地址栏中传的参数(REST 风格的 URL 路径参数):

@RequestMapping(value = "/{groupId}.do") public void detail(@PathVariable long groupId){ groupRepository.selectOne(groupId); }

XMall 实战印证:商品详情与状态操作大量使用路径变量,见 ItemController.java:

@RequestMapping(value = "/item/{itemId}", method = RequestMethod.GET) @ApiOperation(value = "通过ID获取商品") public Result<ItemDto> getItemById(@PathVariable Long itemId){ ItemDto itemDto = itemService.getItemById(itemId); return new ResultUtil<ItemDto>().setData(itemDto); }

注意@PathVariable默认为必填;若 URL 模板中没有对应占位符,会抛出MissingPathVariableException。批量删除接口@PathVariable Long[] ids则展示了路径变量支持数组绑定(/item/del/1,2,3)的扩展用法。

5.3 @RequestBody —— 将请求体转换为 Java 实体

@RequestBody用来将客户端发送过来的请求参数数据格式(JSON/XML 等)转换成 Java 实体:

@RequestMapping(value = "/xxxxx.do") public void create(@RequestBody() String host){ System.out.println("-----------" + host); }

在前后端分离架构中,前端提交的 JSON 对象通常通过@RequestBody直接反序列化为 DTO 实体。

XMall 实战印证:会员登录接口将极验验证参数与账号密码整体封装进MemberLoginRegist对象,见 MemberController.java:

@RequestMapping(value = "/member/login", method = RequestMethod.POST) @ApiOperation(value = "用户登录") public Result<Member> login(@RequestBody MemberLoginRegist memberLoginRegist, HttpServletRequest request){ ... }

头像上传接口同样用@RequestBody CommonDto接收 base64 图片数据(MemberController.java),体现了@RequestBody对复杂嵌套 JSON 的承载能力。需要留意的是:@RequestBody与@RequestParam职责不同——前者读取整个消息体,后者读取 URL 或表单字段,二者可以并存于同一方法签名中。

5.4 @CookieValue —— 绑定请求头中的 Cookie 值

@CookieValue可以把 Request header 中关于 cookie 的值绑定到方法的参数上。例如有如下 Cookie 值:JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84,即可把JSESSIONID的值绑定到参数cookie上:

@RequestMapping("/displayHeaderInfo.do") public void displayHeaderInfo(@CookieValue("JSESSIONID") String cookie) { }

这在需要读取会话标识、自动登录票据或埋点追踪 ID 的场景中非常实用。与@RequestParam类似,它同样支持required与defaultValue属性。

六、响应状态注解:@ResponseStatus

@ResponseStatus返回一个指定的 HTTP response 状态码,可直接标注在方法上(或异常类上),配合value与reason两个属性使用:

@ResponseStatus(reason = "no reason", value = HttpStatus.BAD_REQUEST) @RequestMapping("/responsestatus") public void responseStatusTest(){ }

标注后,SpringMVC 会将该方法映射的响应状态码设置为HttpStatus.BAD_REQUEST(400),reason则作为响应体中的错误原因返回。

XMall 实战印证:全局异常处理器 RestCtrlExceptionHandler.java 中,对参数绑定异常(BindException)显式声明@ResponseStatus(value = HttpStatus.OK):

@ExceptionHandler(BindException.class) @ResponseStatus(value = HttpStatus.OK) @ResponseBody public Result<Object> bindExceptionHandler(BindException e){ String errorMsg = "请求数据校验不合法: "; if(e != null){ errorMsg = e.getMessage(); log.warn(errorMsg); } return new ResultUtil<>().setErrorMsg(errorMsg); }

这里刻意将业务异常统一回 200 并在 JSON 体中携带错误信息,是前端友好型接口设计的常见取舍——HTTP 层不报错,业务层通过Result的state/message字段表达成败,避免前端因跨域或代理配置丢失非 2xx 状态码。

七、异常处理注解:@ExceptionHandler 与全局异常体系

@ExceptionHandler用于处理控制器方法抛出的异常,可声明在单个控制器内,也可通过@ControllerAdvice提升为全局处理:

@RequestMapping("/exception") public void ExceptionTest() throws Exception{ throw new Exception("i don't know"); } @ExceptionHandler public String handleException(Exception e, HttpServletRequest request){ System.out.println(e.getMessage()); return "helloworld"; }

当/exception抛出异常时,SpringMVC 会查找可处理该异常类型的方法handleException并执行,方法可声明Exception e与HttpServletRequest request等参数。

XMall 实战印证:XMall 将异常处理升级为全局统一体系,前台 RestCtrlExceptionHandler.java 通过@ControllerAdvice覆盖所有控制器,按异常类型分级处理:

@ControllerAdvice public class RestCtrlExceptionHandler { // 参数绑定异常 @ExceptionHandler(BindException.class) @ResponseStatus(value = HttpStatus.OK) @ResponseBody public Result<Object> bindExceptionHandler(BindException e){ ... } // 业务自定义异常(xmall-common 中的 XmallException) @ResponseStatus(value = HttpStatus.OK) @ExceptionHandler(XmallException.class) @ResponseBody public Result<Object> handleXmallException(XmallException e) { String errorMsg = "Xmall exception: "; if (e != null){ errorMsg = e.getMsg(); log.warn(e.getMessage()); } return new ResultUtil<>().setErrorMsg(errorMsg); } // 兜底异常:识别文件上传超限、XmallException 等常见错误 @ExceptionHandler(Exception.class) @ResponseStatus(value = HttpStatus.OK) @ResponseBody public Result<Object> handleException(Exception e) { ... } }

该设计有几点值得学习:

  1. 自定义业务异常:基于 XmallException 承载业务错误语义,与@ExceptionHandler(XmallException.class)形成一一对应的处理分支;
  2. 兜底策略:handleException对Maximum upload size、XmallException:等消息做二次解析,将底层异常转换为用户可读的中文提示(如“上传文件大小超过5MB限制”),与 springmvc.xml 中multipartResolver的maxUploadSize=5242880(5MB)限制相互印证;
  3. 统一返回结构:所有异常均包装为Result对象输出,保证前端异常处理逻辑的一致性。

八、综合实战:从注解到完整 REST 控制器

结合 XMall 前台 GoodsController.java,可以看到一篇真实控制器如何将这些注解组合成一个完整的商品查询 API:

@RestController @Api(description = "商品页面展示") public class GoodsController { @Autowired private ContentService contentService; @Autowired private SearchService searchService; // 获取导航栏:GET 请求,无参数 @RequestMapping(value = "/goods/navList", method = RequestMethod.GET) public Result<List<TbPanelContent>> getNavList(){ List<TbPanelContent> list = contentService.getNavList(); return new ResultUtil<List<TbPanelContent>>().setData(list); } // 商品搜索:@RequestParam 默认值 + produces 限定返回类型 + 注入 ES 搜索服务 @RequestMapping(value = "/goods/search", method = RequestMethod.GET) public Result<SearchResult> searchProduct(@RequestParam(defaultValue = "") String key, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "20") int size, @RequestParam(defaultValue = "") String sort, @RequestParam(defaultValue = "-1") int priceGt, @RequestParam(defaultValue = "-1") int priceLte){ SearchResult searchResult = searchService.search(key, page, size, sort, priceGt, priceLte); return new ResultUtil<SearchResult>().setData(searchResult); } }

一个标准的 XMall REST 控制器通常遵循如下模式:

  1. 类级:@RestController声明 JSON 输出 +@Api接入 Swagger 文档(Swagger2Config.java 通过RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)扫描带@ApiOperation的方法生成接口文档);
  2. 方法级:@RequestMapping或组合注解声明 URL 与 HTTP 方法,@ApiOperation描述接口语义;
  3. 参数级:查询参数用@RequestParam(配合默认值兜底)、路径参数用@PathVariable、JSON 体用@RequestBody;
  4. 返回值:统一封装Result<T>,由ResultUtil构建;
  5. 异常:由@ControllerAdvice+@ExceptionHandler全局兜底。

九、扩展观察:注解之外的处理链

SpringMVC 的价值不仅在于注解本身。从 XMall 源码可以看到注解与周边组件的协作关系:

  • 拦截器:前台 LimitRaterInterceptor.java 基于HandlerInterceptorAdapter实现 IP 限流与全局限流(结合 RedisRaterLimiter.java),并在preHandle中通过HandlerMethod.getMethod()读取方法上的@RateLimiter注解(RateLimiter.java)实现方法级限流——这是“注解 + 拦截器”协同的经典范式;
  • 前端控制器:DispatcherServlet负责把请求分发到上述注解标注的方法,其装配在 springmvc.xml 中完成:<context:component-scan base-package="cn.exrick.front"/>扫描控制器与拦截器 Bean,<mvc:resources>映射 Swagger 静态资源;
  • Dubbo 服务引用:控制器通过@Autowired注入的 Service 实际是 Dubbo 远程引用(见 springmvc.xml 中的<dubbo:reference>),注解驱动让远程服务调用对控制器透明,这是 SOA 架构与 SpringMVC 无缝结合的关键。

十、小结

SpringMVC 的注解体系是 Web 层开发的基石。通过 XMall 源码可以看到:@RestController支撑前后端分离的 JSON 契约,@RequestMapping及其组合注解构建清晰的 REST 路由,@RequestParam/@PathVariable/@RequestBody覆盖查询、路径、消息体三种参数来源,@CookieValue读取会话凭证,@ResponseStatus控制响应语义,而@ControllerAdvice+@ExceptionHandler则把散落的异常处理收敛为统一防线。读者在阅读 SpringMVC.md 笔记之余,可对照 XMall 的 front 控制器目录、manager 控制器目录 与全局异常处理器,逐注解验证其在真实分布式电商场景中的落地方式。

  • 电商
  • 后端
  • 微服务

【免费下载链接】xmall

基于SOA架构的分布式电商购物商城 前后端分离 前台商城:Vue全家桶 后台管理系统:Dubbo/SSM/Elasticsearch/Redis/MySQL/ActiveMQ/Shiro/Zookeeper等

项目地址:https://gitcode.com/gh_mirrors/xm/xmall
点击查看免费下载

相关推荐

上一篇:Test Infrastructure
下一篇:@angular/google-maps MapPolyline 完全指南:在 Angular 中绘制与管理 Google 地图折线

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询