FastAPI 的依赖注入(Dependency Injection)是我最喜欢的特性之一。只需在路由参数中写 Depends(get_db),框架就会自动解析依赖链、处理缓存、管理生命周期。但当你需要自定义依赖行为时,理解它的内部原理就变得重要。
Depends 的本质
Depends() 返回的是一个 Depends 实例,这个实例不包含任何执行逻辑——它只是一个标记对象,告诉 FastAPI 的依赖解析器:"这个参数需要从某个可调用对象获取"。
from fastapi import Depends
def get_current_user(token: str = Header(...)) -> User:
return decode_token(token)
@app.get("/me")
def me(user: User = Depends(get_current_user)):
return user当请求到达 /me 时,FastAPI 做了以下几件事:检查路由函数的参数签名,发现 user 有 Depends 标记,递归解析 get_current_user 的依赖(本例中 Header),按顺序执行依赖链,将结果注入路由函数。
依赖缓存与 Scope
FastAPI 提供 use_cache 参数控制依赖的复用行为:
- 默认 True:同一请求内多次引用该依赖只执行一次,结果缓存复用。适合数据库连接。
- False:每次引用都重新执行。适合需要独立状态的操作。
依赖的生命周期与请求绑定。FastAPI 通过 contextlib.AsyncExitStack 管理带清理逻辑的依赖(如 yield 的数据库 session),确保即使中途抛异常也能正确释放资源。
常见陷阱
循环依赖:A 依赖 B,B 又依赖 A,FastAPI 会抛出 RecursionError。解决方案是引入中间服务层或使用 lazy 参数延迟加载。
过度嵌套:依赖链超过 3 层时,代码可读性急剧下降。每多一层抽象,新同事理解成本翻倍。经验法则:如果依赖的依赖本身又有复杂依赖,考虑扁平化。
async vs sync:如果依赖函数是 async def,FastAPI 会用 await 调用;同步函数则在线程池中执行。混用时需要注意,一个同步依赖如果被 async 路由引用,不会自动获得异步优势。
依赖注入的本质是控制反转。用好它,你的 FastAPI 代码会像乐高积木一样可组装。
--- 约 700 字