速读#
这篇用 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 int id, @RequestParam(required = false) String q) { return Map.of("id", id, "q", q == null ? "" : q); }}@RestController:返回数据,不是页面@GetMapping("/items/{id}"):路径接到方法{id}→@PathVariable;查询串 →@RequestParam
@RestController 本身就是「注解是声明」的样本:它等于 @Controller + @ResponseBody——「这个类处理请求」加「返回值直接序列化成 JSON,别去找页面模板」。返回的 Map 变成 JSON,是 Jackson 在幕后干的,我没写一行序列化代码。
注解是声明,不是知识点清单。 我声明「这是接口」「这是路径参数」,框架替我接线。理解「声明式」这个底子,注解多就不构成记忆负担——每个注解都在回答同一个问句:「这是什么」。
同一件事,两种表达#
| 底层在做的事 | FastAPI | Spring |
|---|---|---|
| 路由 | @app.get | @GetMapping |
| 参数绑定 | item_id: int | @PathVariable int id |
| 类型不对时 | 自动 422 | 自动 400 |
| 序列化 | 自动 JSON | 自动 JSON |
FastAPI:函数 + 装饰器。Spring:类 + 注解。表达不同,底下是同一件事,连接线的方式都同构:FastAPI 在模块加载时把被装饰的函数登记进路由表,Spring 在启动时扫描注解建出「路径 → 方法」的映射,请求进来都由框架的分发器查表、转换参数、再调用我的函数。我写的从来不是「处理请求的全过程」,只是那张表里的一行。连「参数类型不对怎么办」都各有默认答案——路径传个非数字,两边都不用我写 if 判断,框架先拦下,只是错误码的习惯不同。
取舍:练习要轻,生产要稳#
| FastAPI | Spring | |
|---|---|---|
| 重量 | 轻、启动快 | 重、约定多 |
| 我何时偏它 | 练手写一堆小接口 | 长期工程化维护 |
没有谁更好,看场景。Spring 的「重」不是白重:类型系统、分层约定、和一整套生态的一致性,都是在为「很多人长期改同一个项目」付保险费;一个人写小工具,这份保险费就是纯负担。还是那条「练习要轻、生产要稳」的取舍——FastAPI 生产环境装不装 [standard]、去不去 --reload,也是同一个问句。
差别不在好坏,在我现阶段更怕哪种麻烦。
学第二门框架最大的收获#
第一次 FastAPI 记的是「装饰器怎么配」;第一次 Spring 记的是「注解怎么写」。写完第二个,记的东西变了——开始记所有后端框架都在做的几件事:路由、参数绑定、序列化。
一旦看见这层「不变」,换框架的成本从「重学一门」降到「换个语法壳」。后来 DRF ViewSet、再写 Spring Controller,异曲同工。
这就是慢功夫的红利——啃透一个抽象,变体不慌。
这篇留下的不是注解清单,是对照框架:
任何后端框架,本质上都在解决路由 / 参数绑定 / 序列化。差别在表达方式和重量级。
