找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4779

积分

0

好友

667

主题
发表于 1 小时前 | 查看: 2| 回复: 0

昨天介绍了依赖注入的初步概念,可能有些同学觉得有点抽象。其实,它并没有想象中那么复杂。

今天,我们用3分钟,通过一个生活化的比喻,帮你把这个能让代码结构更清晰的核心概念彻底弄明白。

在深入学习FastAPI的依赖注入之前,我们有必要先理解几个基础概念,这样才能事半功倍。

第一个是函数参数传递:当你编写一个函数时,它所需的外部资源或数据,应该通过参数传递进来,而不是在函数内部直接创建或获取。

第二个是装饰器模式:这是一种为函数“添加包装”的技术,可以在不修改原函数代码的前提下,为其扩展新的功能。

第三个是解耦思想:让代码的不同模块尽可能地保持独立,修改一个模块时,不会“牵一发而动全身”。这就像拼搭积木,各个部分松散耦合,可以灵活组合。

理解了这三个概念,依赖注入学起来就会顺畅很多。


如果觉得抽象,我们打个现实的比方:炒菜的过程

假设你中午想加个菜:‘宫保鸡丁’。

  • 传统写法 = 这道菜完全由你亲手操办:从去菜市场买菜、回家洗菜切菜、起锅烧油翻炒、到最后装盘上桌,所有步骤亲力亲为。
  • 依赖注入 = 你只需要向外卖平台下一个订单:“我要一份宫保鸡丁的净菜包”。平台会自动把切配好的鸡肉、蔬菜和调配好的酱料送上门,你只需要进行最后的翻炒步骤即可。

看出区别了吗?

  • 传统写法:函数内部需要自己创建和组装所有依赖项(比如数据库连接、配置文件、工具类实例)。
  • 依赖注入:函数只需要声明“我需要什么”,外部框架或容器会自动将这些准备好的“依赖”送进来,函数直接使用即可。

核心就是:声明依赖,而非创建依赖。

这就是依赖注入的本质。

注意:这里你声明需要的“依赖”,也并非凭空产生,它们要么是你自己预先编写好的,要么是框架或第三方库提供的。

拓展一下:IoC 和 DI 是什么关系?

很多人容易混淆IoC(控制反转)和DI(依赖注入),我们来理清一下:

  • IoC (控制反转) 是一种宽泛的设计思想。它指的是将程序中对依赖对象的控制权,从程序内部“反转”到外部容器或框架。
  • DI (依赖注入) 是实现IoC思想的一种具体技术手段。它通过参数、属性或构造函数等方式,将依赖对象“注入”到需要使用它的类或函数中。

简单说:IoC是理念,DI是实践

FastAPI 采用的就是依赖注入这种实践方式。

回到“宫保鸡丁”的例子

我们要实现一个获取当前用户信息的功能:

❌ 传统写法(臃肿且职责不清)

from fastapi import FastAPI, Header, HTTPException
import jwt

app = FastAPI()

@app.get("/me")
def get_current_user(authorization: str = Header(None)):
    # 1. 自己解析 JWT token
    # 2. 自己创建数据库连接
    # 3. 自己查询数据库验证用户是否存在
    # 4. 还要记得自己关闭数据库连接
    # ... 大量与核心业务无关的代码
    pass

这个 get_current_user 函数就像那份需要你从买菜做起的“宫保鸡丁”菜谱,所有工序都堆在一起。

✅ 依赖注入写法(清晰且优雅)

from fastapi import FastAPI, Depends
from fastapi.security import HTTPBearer

# 1. 把 token 解析逻辑抽离成一个独立的“依赖”函数
async def get_current_user_id(token: str = Depends(HTTPBearer())):
    # 专责解析token,返回用户ID
    user_id = decode_jwt(token)
    return user_id

# 2. 把数据库会话管理也抽成依赖(示意,实际常用 yield 管理生命周期)
async def get_db():
    # 创建并返回一个数据库会话
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# 3. 路径操作函数只需声明需要什么,并专注于核心业务逻辑
@app.get("/me")
async def get_current_user(
    user_id: int = Depends(get_current_user_id),
    db = Depends(get_db)
):
    # 这里直接使用注入进来的 user_id 和 db 会话
    user = db.query(User).filter(User.id == user_id).first()
    return user
  • Depends() 函数就像是那个“外卖平台”的入口。你告诉它需要什么(get_current_user_id, get_db),它就会在调用你的函数之前,先运行这些依赖函数,并把结果“送”给你。

Depends() 的基本用法

FastAPI 实现依赖注入的核心就是 Depends() 函数,其用法非常简单:

1. 定义依赖函数

编写一个普通的函数(可以是异步 async def 或同步 def),让它返回你需要的值。

def get_query_param(q: str = None):
    return q

2. 在路径操作函数中使用

在路径操作函数的参数列表中,使用 Depends(你的依赖函数) 作为默认值。

@app.get("/items/")
async def read_items(q: str = Depends(get_query_param)):
    return {"q": q}

就这么简单!FastAPI 会自动调用 get_query_param 函数,并将其返回值赋给路径操作函数中的 q 参数。

常见误区与避坑指南

❌ 误区一:什么都要注入

并非所有函数调用都需要做成依赖。像一些纯计算的、无状态的工具函数,直接调用即可。过度设计会引入不必要的复杂性。在面向对象编程中,过度使用设计模式也可能导致类似问题。

❌ 误区二:依赖链过长

依赖可以嵌套(一个依赖本身可以声明它需要其他依赖),但应避免嵌套过深(例如超过3层)。过长的依赖链会使代码逻辑难以追踪,应考虑进行合理的拆分,保持代码的清晰度。

❌ 误区三:在依赖函数中编写业务逻辑

依赖函数应专注于提供资源或数据(如获取当前用户、创建数据库连接、验证权限)。核心的业务判断和处理逻辑应放在路径操作函数中。例如,get_current_user 只负责返回用户对象,而“检查用户是否有管理员权限”这个业务规则,应该在接口逻辑里判断。

总结

依赖注入不是用来炫技的复杂模式,而是一种让代码结构更清晰、更易维护的实用方法。

记住FastAPI开发中的三个关键技巧:

FastAPI开发三个关键技巧图示:重复代码抽成依赖,资源管理用yield,测试时直接传mock

  1. 重复代码抽成依赖:将诸如身份验证、数据库会话获取等通用逻辑封装成依赖,避免代码重复。
  2. 资源管理用 yield:对于需要精确控制创建和销毁的资源(如数据库连接),在依赖函数中使用 yield,FastAPI能确保资源被正确清理。
  3. 测试时直接传 mock:由于依赖是注入的,在单元测试中,你可以轻松传入模拟(mock)对象来替换真实依赖,使得测试更加独立和高效。

优雅的代码往往不是通过硬编码写出来的,而是通过良好的设计和清晰的依赖关系“组织”出来的。掌握依赖注入,能帮助你在Web开发中构建出更健壮、更易测试的应用。




上一篇:少儿编程入门:三款免费编程工具与家长引导指南
下一篇:实战干货:OpenClaw 海量技能库中筛选高可用运维技能组合的完整流程
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-30 02:16 , Processed in 0.871633 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表