一个请求怎么找到对应的 Controller 方法?字面量和变量谁优先?末尾斜杠和后缀还能匹配吗?两个方法映射冲突什么时候报错?这篇在 Spring Boot 4.0.8 的 Spring MVC 7.0.9 上用一组请求逐个验证。
说明 | 请求 | 状态 | 谁处理的
字面量 vs 变量 | GET /users/me | 200 | me
字面量 vs 变量 | GET /users/42 | 200 | byId(42)
正则变量 vs 任意变量 | GET /users/42/orders | 500 | {"timestamp":"2026-10-19T03:28:04.811Z","status":5
正则变量 vs 任意变量 | GET /users/bob/orders | 200 | ordersAny(bob)
/** vs 精确 | GET /files/readme.txt | 200 | files/readme.txt
/** vs 精确 | GET /files/a/b/c.txt | 200 | files/**
服务端日志里 /users/42/orders 的 500 原因:Ambiguous handler methods mapped for '/users/42/orders': {ordersAny(java.lang.String), ordersNumeric(java.lang.String)}] with root cause字面量优先于变量,精确路径优先于 /**,带正则的变量并不比普通变量更具体。
说明 | 请求 | 状态 | 响应(404 等是 Boot 的错误页 JSON)
末尾斜杠 | GET /hello/ | 404 | {"timestamp":"2026-10-19T03:28:05.137Z","status":4
后缀 | GET /hello.json | 404 | {"timestamp":"2026-10-19T03:28:05.139Z","status":4
大小写 | GET /Hello | 404 | {"timestamp":"2026-10-19T03:28:05.140Z","status":4
带点的路径变量(没写正则) | GET /users/a.b | 200 | byId(a.b)
写了 {name:.+} 的带点变量 | GET /doc/report.final.pdf | 200 | doc(report.final.pdf)
路径变量里的 %2F | GET /users/a%2Fb | 400 | <!doctype html><html lang="en"><head><title>HTTP S末尾斜杠、后缀、大小写都不自动匹配;路径变量里的点保留;%2F 被 Tomcat 拒绝。
说明 | 请求 | 状态 | 谁处理的 / 错误
Accept: application/json | GET /neg | 200 | neg-json
Accept: text/plain | GET /neg | 200 | neg-text
Accept: text/html | GET /neg | 406 | <html><body><h1>Whitelabel Error Page</h1><p>This
?debug=true | GET /cond?debug=true | 200 | cond(debug=true)
没有参数 | GET /cond | 200 | cond()
X-Env: prod | GET /hdr | 200 | hdr(prod)
没有头 | GET /hdr | 200 | hdr()
Content-Type: application/json | POST /consume | 200 | consume-json
Content-Type: text/plain | POST /consume | 415 | {"timestamp":"2026-10-19T03:28:05.170Z","status":4produces、consumes、params、headers 可以区分同一路径下的方法。
两个方法映射到 GET /x/{a} 与 GET /x/{b}:启动成功
同一个应用启动后请求 GET /x/1 → 500,消息:Ambiguous handler methods mapped for '/x/1': {java.lang.String demo.Main.two(java.lang.String), java.lang.String demo.Main.one(java.lang.String)}] with root cause
GET /x/**/y(PathPattern 不允许 ** 在中间):启动失败 PatternParseException: {*...} or ** pattern elements should be placed at the start or end of the pattern歧义映射启动不报错,请求时 500;** 在中间启动就失败。