依赖注入
FastAPI 的依赖注入(DI)是框架核心,比 Spring 更轻但思想一致。面试主线:Depends 怎么工作、yield 依赖怎么清理、测试怎么替换。
Depends 机制
from fastapi import Depends
def get_db():
db = SessionLocal()
try:
yield db # yield 前: 请求开始; yield 后: 请求结束清理
finally:
db.close()
@app.get("/users")
def list_users(db: Session = Depends(get_db)):
return db.query(User).all()- 参数类型加
Depends(...),FastAPI 自动调用依赖函数,返回值注入参数 - 依赖可以是函数、类、可调用对象
- yield 依赖:
yield之前是初始化,之后是清理(finally 保证异常也清理),数据库会话的标准姿势
子依赖与依赖树
def get_current_user(token: str = Depends(oauth2_scheme)):
...
def get_db_for_user(db: Session = Depends(get_db),
user: User = Depends(get_current_user)):
...
@app.get("/me")
def me(db: Session = Depends(get_db_for_user)): # 依赖嵌套依赖
...- 依赖可以依赖其他依赖,FastAPI 递归解析整棵依赖树
- 同一依赖在请求内只执行一次(缓存),多处引用不重复调用(yield 依赖也只在请求结束清理一次)
- 依赖树天然组织:鉴权依赖、数据库依赖、配置依赖分层组合
作用域
| 作用域 | 实现 | 生命周期 |
|---|---|---|
| 请求级(默认) | 普通依赖 | 每次请求执行 |
| 应用级(单例) | @lru_cache 包依赖 |
进程内一次 |
| 请求级+清理 | yield 依赖 | 请求结束清理 |
@lru_cache
def get_settings() -> Settings: # 应用级: 配置读一次
return Settings()注意:@lru_cache 后 Depends 依然可用(FastAPI 检测缓存),但参数必须可哈希。
依赖覆盖:测试的核心
def fake_get_db():
yield MemorySession()
app.dependency_overrides[get_db] = fake_get_db # 替换, 不改业务代码dependency_overrides:把原依赖函数替换为测试实现- 数据库换内存、外部 API 换 mock、鉴权换固定用户
- 测试完成后
app.dependency_overrides.clear()恢复
面试追问
- Depends 怎么工作? 声明依赖,框架自动调用并注入返回值。依赖树递归解析
- yield 依赖是干什么的? 请求生命周期管理:yield 前初始化、yield 后清理(关会话)。异常也走 finally
- 同一依赖会被调用多次吗? 不会。FastAPI 按请求缓存依赖结果,依赖树中复用
- 作用域怎么控制? 默认请求级;lru_cache 提升为进程级单例。FastAPI 没有 Spring 式作用域注解
- 怎么在测试里替换依赖? dependency_overrides 映射替换,测试数据库/鉴权/mock。测完 clear