Platform / 平台
Rust Core(macOS / Windows 共用)
Lithe version / Lithe 版本
0cdc30b(#306 合并提交)
OS and architecture / 操作系统与架构
平台无关,问题位于 rust/lithe-core/src/languages/spring.rs 的 endpoint_index / mapping() 路径。
Environment / 环境信息
这是 #299 与 #306 的后续解析正确性问题。#306 为 Spring 索引中的固定正则增加了缓存,并为 Bean、组件和注入等已登记注解建立了准确名称边界;Endpoint Mapping 仍保留合并前的朴素 contains 检测。
该行为在 #306 之前已经存在,不代表性能修复需要回滚。由于它属于端点索引而非 Bean 命名,单独跟踪,避免与 #315 混在一个修复中。
Steps to reproduce / 复现步骤
在一个可被 Spring 端点索引识别的 Controller 中使用名称带标准注解前缀的自定义注解:
@RestController
public class DemoController {
@GetMappingCustom("/x")
public String custom() { return ""; }
}
也可以直接对 mapping() 验证:
mapping("@GetMappingCustom(\"/x\")")
当前实现依次执行:
if annotation_text.contains("@GetMapping") { ... }
因此返回:
methods = ["GET"]
routes = ["/x"]
@RequestMappingFoo、@PostMappingCustom、@PutMappingCustom、@DeleteMappingCustom 和 @PatchMappingCustom 存在同类风险。
Expected behavior / 预期行为
只有准确的 Spring Mapping 注解名称才应生成端点:
@GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping
@RequestMapping
名称只是以这些字符串开头的自定义注解不应被当作端点。示例中的 @GetMappingCustom 应返回 None,并且不出现在 spring.index 的 endpoints 中。
Actual behavior / 实际行为
mapping() 使用子字符串包含判断,@GetMappingCustom("/x") 被当成真实 @GetMapping,生成一个不存在的 GET /x 端点。
同一路径中的类级判断也使用 annotation.contains("@RequestMapping"),自定义前缀注解可能错误更新 Controller 的 base_routes。
Root cause / 根因
Endpoint Mapping 没有使用 #306 中的准确注解边界能力:
- HTTP 方法注解通过字符串数组加
contains 判断。
@RequestMapping 在 mapping() 和类级 base route 判断中分别使用 contains。
- 检测、HTTP 方法映射和路由参数提取没有由同一个封闭定义驱动,后续新增或修改注解时容易再次分叉。
Recommended fix / 推荐修补方案
推荐新增一个文件内私有的封闭 Mapping 注解类型,沿用 #306 的单一声明思路,例如由一份声明生成:
- 注解名称
- 准确边界模式
@Name 后只能接空白、左括号或上下文结尾
- 对应 HTTP 方法;
RequestMapping 使用其参数中的 RequestMethod.*
- 测试所需的完整列表
mapping() 应先找到准确匹配的 Mapping 注解,再解析该注解自己的参数,不能继续使用 contains。类级 base route 判断也应复用同一匹配结果,而不是单独搜索 @RequestMapping。
实现约束:
- 固定模式继续使用
LazyLock<Regex>,不能恢复逐行 Regex::new。
- 若一个上下文存在多个 Mapping 注解,应按源码位置选择并记录明确策略,避免依赖枚举或 alternation 声明顺序。
annotation_routes 只能接收已隔离的真实 Mapping 注解文本,不能从相邻自定义注解中提取路径。
- 保留
@RequestMapping(method = { RequestMethod.GET, RequestMethod.POST }) 的数组和排序、去重行为。
Required tests / 必需测试
边界反例:
@GetMappingCustom("/x") 返回 None。
@PostMappingCustom、@PutMappingCustom、@DeleteMappingCustom、@PatchMappingCustom 返回 None。
@RequestMappingFoo("/base") 不产生类级 base route,也不产生方法端点。
正常行为:
- 五种标准方法 Mapping 仍返回正确 HTTP 方法和路径。
@RequestMapping 未指定 method 时仍返回 ANY。
@RequestMapping 的单个和数组 RequestMethod 继续正确排序、去重。
- 带
value =、path =、字符串数组和直接字符串参数的现有路由解析继续通过。
- 完整
spring.index fixture 中,自定义前缀注解不出现在 endpoints。
Acceptance criteria / 验收标准
- 所有标准 Mapping 注解保持现有正确行为。
- 名称更长的自定义注解不再生成端点或 base route。
cargo test -p lithe-core 通过。
./scripts/verify-rust-core-comments.sh 通过。
- 热路径没有新增运行期正则编译。
- 使用包含标准注解和前缀诱饵的固定语料比较输出,除错误端点被移除外没有无关变化。
Related / 关联
Checklist / 提交前检查
Platform / 平台
Rust Core(macOS / Windows 共用)
Lithe version / Lithe 版本
0cdc30b(#306 合并提交)OS and architecture / 操作系统与架构
平台无关,问题位于
rust/lithe-core/src/languages/spring.rs的endpoint_index/mapping()路径。Environment / 环境信息
这是 #299 与 #306 的后续解析正确性问题。#306 为 Spring 索引中的固定正则增加了缓存,并为 Bean、组件和注入等已登记注解建立了准确名称边界;Endpoint Mapping 仍保留合并前的朴素
contains检测。该行为在 #306 之前已经存在,不代表性能修复需要回滚。由于它属于端点索引而非 Bean 命名,单独跟踪,避免与 #315 混在一个修复中。
Steps to reproduce / 复现步骤
在一个可被 Spring 端点索引识别的 Controller 中使用名称带标准注解前缀的自定义注解:
也可以直接对
mapping()验证:当前实现依次执行:
因此返回:
@RequestMappingFoo、@PostMappingCustom、@PutMappingCustom、@DeleteMappingCustom和@PatchMappingCustom存在同类风险。Expected behavior / 预期行为
只有准确的 Spring Mapping 注解名称才应生成端点:
@GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping@RequestMapping名称只是以这些字符串开头的自定义注解不应被当作端点。示例中的
@GetMappingCustom应返回None,并且不出现在spring.index的 endpoints 中。Actual behavior / 实际行为
mapping()使用子字符串包含判断,@GetMappingCustom("/x")被当成真实@GetMapping,生成一个不存在的 GET/x端点。同一路径中的类级判断也使用
annotation.contains("@RequestMapping"),自定义前缀注解可能错误更新 Controller 的base_routes。Root cause / 根因
Endpoint Mapping 没有使用 #306 中的准确注解边界能力:
contains判断。@RequestMapping在mapping()和类级 base route 判断中分别使用contains。Recommended fix / 推荐修补方案
推荐新增一个文件内私有的封闭 Mapping 注解类型,沿用 #306 的单一声明思路,例如由一份声明生成:
@Name后只能接空白、左括号或上下文结尾RequestMapping使用其参数中的RequestMethod.*mapping()应先找到准确匹配的 Mapping 注解,再解析该注解自己的参数,不能继续使用contains。类级 base route 判断也应复用同一匹配结果,而不是单独搜索@RequestMapping。实现约束:
LazyLock<Regex>,不能恢复逐行Regex::new。annotation_routes只能接收已隔离的真实 Mapping 注解文本,不能从相邻自定义注解中提取路径。@RequestMapping(method = { RequestMethod.GET, RequestMethod.POST })的数组和排序、去重行为。Required tests / 必需测试
边界反例:
@GetMappingCustom("/x")返回None。@PostMappingCustom、@PutMappingCustom、@DeleteMappingCustom、@PatchMappingCustom返回None。@RequestMappingFoo("/base")不产生类级 base route,也不产生方法端点。正常行为:
@RequestMapping未指定 method 时仍返回ANY。@RequestMapping的单个和数组RequestMethod继续正确排序、去重。value =、path =、字符串数组和直接字符串参数的现有路由解析继续通过。spring.indexfixture 中,自定义前缀注解不出现在 endpoints。Acceptance criteria / 验收标准
cargo test -p lithe-core通过。./scripts/verify-rust-core-comments.sh通过。Related / 关联
Checklist / 提交前检查