Skip to content

依赖注入

Depends、子依赖、作用域、依赖覆盖与测试。

Updated View as Markdown
For humans

依赖注入

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() 恢复

面试追问

  1. Depends 怎么工作? 声明依赖,框架自动调用并注入返回值。依赖树递归解析
  2. yield 依赖是干什么的? 请求生命周期管理:yield 前初始化、yield 后清理(关会话)。异常也走 finally
  3. 同一依赖会被调用多次吗? 不会。FastAPI 按请求缓存依赖结果,依赖树中复用
  4. 作用域怎么控制? 默认请求级;lru_cache 提升为进程级单例。FastAPI 没有 Spring 式作用域注解
  5. 怎么在测试里替换依赖? dependency_overrides 映射替换,测试数据库/鉴权/mock。测完 clear
Navigation

Type to search…

↑↓ navigate↵ selectEsc close