速读#
这篇用 Spring Boot 写第一个接口,重点是和 FastAPI 对照,看见不同框架背后不变的三件事:路由、参数绑定、序列化。注解不是知识点清单,而是声明「这是什么」,框架负责接线。
最朴素的 Controller#
@RestController@RequestMapping("/api")public class HelloController {
@GetMapping("/hello") public Map<String, Object> hello() { return Map.of("message", "Hello from s1oopX"); }
@GetMapping("/items/{id}") public Map<String, Object> item(@PathVariable("id") int id, @RequestParam(name = "q", defaultValue = "") String q) { return Map.of("id", id, "q", q); }}@RestController:返回数据,不是页面@GetMapping("/items/{id}"):路径接到方法{id}→@PathVariable;查询串 →@RequestParam
@RestController 本身就是“注解是声明”的样本:它组合了 @Controller 与 @ResponseBody——这个类处理请求,返回值直接写入响应体,不去找页面模板。返回的 Map 由 Jackson 序列化成 JSON;显式写 @PathVariable("id") 和 @RequestParam(name = "q"),则避免参数绑定依赖编译器是否保留方法参数名。生产接口我更倾向返回明确的 DTO 或 Java record,让字段变化能被编译器和接口文档发现;第一条接口用 Map,只是为了把路由链路看清。
注解是声明,不是知识点清单。 我声明「这是接口」「这是路径参数」,框架替我接线。理解「声明式」这个底子,注解多就不构成记忆负担——每个注解都在回答同一个问句:「这是什么」。
启动后我不只看控制台写着 Started,还发一次真实请求验证绑定和序列化:
curl -i "http://localhost:8080/api/items/42?q=book"# HTTP/1.1 200 ...# {"q":"book","id":42}JSON 对象字段顺序不属于接口契约,客户端应按字段名读取,不能依赖示例里的排列顺序。
同一件事,两种表达#
| 底层在做的事 | FastAPI | Spring |
|---|---|---|
| 路由 | @app.get | @GetMapping |
| 参数绑定 | item_id: int | @PathVariable int id |
| 类型不对时 | 自动 422 | 自动 400 |
| 序列化 | 自动 JSON | 自动 JSON |
FastAPI 是函数加装饰器,Spring MVC 是类加注解。表达不同,请求入口却能用同一张图理解:框架登记“路径与处理函数”的映射,请求到来后匹配路由、转换和校验参数、调用业务代码,再序列化响应。路径参数传入非数字时,两边都能在业务方法前拦下,FastAPI 默认返回 422,Spring MVC 通常返回 400;具体错误体仍取决于版本和项目的全局异常配置。
取舍:看团队和运行约束,不给框架排等级#
| 维度 | FastAPI | Spring Boot + Spring MVC |
|---|---|---|
| 语言与模型 | Python 类型提示、依赖注入,支持 async def | Java 类型系统、IoC,默认 Servlet 阻塞模型 |
| 常见优势 | 小团队迭代快,数据与 AI 生态衔接直接 | 企业集成、事务、安全与团队约定成熟 |
| 主要成本 | Python 运行时、同步/异步边界仍要理解 | 启动装配、注解与生态概念更多 |
两者都能稳定跑在生产,也都能被写得难维护。Spring Boot 的约定和生态在多人长期协作、复杂事务与企业集成里很值钱;FastAPI 在 Python 团队、小型服务和 AI 工作流里通常更直接。运行模型也不能只看接口长得像:Spring MVC 默认一请求占用一个工作线程,FastAPI 的 async def 只有在使用非阻塞依赖并正确 await 时才获得并发收益,CPU 密集任务两边都要另做隔离或扩展。
差别不在好坏,在我现阶段更怕哪种麻烦。
学第二门框架最大的收获#
第一次 FastAPI 记的是“装饰器怎么配”,第一次 Spring 记的是“注解怎么写”。写完第二个,记的东西变了——开始先找请求路径上不变的几个环节:路由、参数转换与校验、业务调用、异常翻译、序列化。认证、依赖注入、中间件和生命周期会继续加在这条链上,但不必再把每个框架当成完全陌生的世界。
一旦看见这层「不变」,换框架的成本从「重学一门」降到「换个语法壳」。后来 DRF ViewSet、再写 Spring Controller,异曲同工。
这就是慢功夫的红利——啃透一个抽象,变体不慌。
这篇留下的不是注解清单,是对照框架:
写第一个 HTTP 接口时,先找到路由、参数绑定、异常和序列化;再去学习这个框架特有的运行模型与工程约定。
