FastAPI 中,使用 依赖注入函数 都可以实现依赖注入,但它们的设计方式、适用场景和生命周期管理有所不同。以下是它们的区别以及各自的应用场景:

1. 函数依赖注入

函数依赖注入是最常见的一种方式,通过 Depends 来声明依赖,FastAPI 会自动调用这些函数并注入返回的对象。

特点:

  • 轻量级:函数依赖注入通常比较简洁,适用于处理简单的依赖项,比如数据库连接、外部 API 客户端等。
  • 请求级别的实例:函数依赖项在每次请求时都会被实例化,适用于需要频繁创建和销毁的资源(例如数据库连接)。
  • 自动清理:依赖项通常是按请求生命周期管理的,FastAPI 会在请求结束时自动清理资源(例如关闭数据库连接)。

应用场景:

  • 数据库连接:每个请求可能需要一个独立的数据库连接(例如,MySQL、PostgreSQL)。
  • API 客户端:例如,每次请求都需要调用外部 API,创建一个新的 API 客户端连接。
  • 请求级缓存:某些缓存是基于请求的,生命周期较短。

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 依赖函数:提供数据库连接
def get_db_connection():
    conn = mysql.connector.connect(...)  # 创建连接
    try:
        yield conn
    finally:
        conn.close()

# 路由使用该依赖
@app.get("/items/")
def get_items(db: mysql.connector.MySQLConnection = Depends(get_db_connection)):
    cursor = db.cursor()
    cursor.execute("SELECT * FROM items")
    items = cursor.fetchall()
    return items

2. 类依赖注入

类依赖注入方式是将依赖项封装为类对象,通常用于需要共享状态或较长生命周期的服务。FastAPI 会在请求开始时为该类创建一个实例,随后可以通过类的实例方法提供各种功能。

特点:

  • 共享状态:类依赖通常会被设计为一个具有共享状态的对象,可以在类的多个方法之间共享数据。适用于需要在多个请求之间共享状态的服务。
  • 适用于复杂的逻辑和服务:如果一个服务需要保存状态,或者需要初始化复杂的资源(如数据库连接池、外部服务连接等),类封装会比简单的函数更加清晰和合适。
  • 长生命周期:类的实例可以跨请求存在(可以通过应用启动时创建),因此它更适用于那些具有长生命周期的服务。

应用场景:

  • 配置服务:应用级别的配置,通常只在启动时创建,整个生命周期中不会改变。
  • 外部服务客户端:如外部 API 客户端或某些大型的外部服务连接(例如,消息队列的连接池)。
  • 状态管理服务:需要在多次请求中共享状态的信息(例如,缓存服务、认证服务等)。

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 依赖类:数据库连接池服务
class DatabaseService:
    def __init__(self, host: str, user: str, password: str, db: str):
        self.conn_pool = mysql.connector.connectpool(...)  # 初始化连接池

    def get_connection(self):
        return self.conn_pool.get_connection()

# 依赖注入函数:注入数据库服务
def get_database_service() -> DatabaseService:
    return DatabaseService(host="localhost", user="user", password="pass", db="test_db")

# 路由使用该依赖
@app.get("/items/")
def get_items(db_service: DatabaseService = Depends(get_database_service)):
    db_connection = db_service.get_connection()
    cursor = db_connection.cursor()
    cursor.execute("SELECT * FROM items")
    items = cursor.fetchall()
    return items

类与函数依赖注入的主要区别

特性类依赖注入函数依赖注入
实例化时机FastAPI 会在请求开始时创建类的实例。每个请求都会调用依赖函数并创建新的实例。
状态共享类的实例可以在多个请求之间共享状态。通常每次请求都会创建新的实例,无法共享状态。
适用复杂度适用于需要复杂逻辑和状态的服务(如数据库连接池、配置管理)。适用于轻量级服务,如数据库连接或外部 API 客户端。
生命周期类的实例可以跨请求存在,适用于长期存在的服务。每次请求都创建新实例,适用于短生命周期依赖。
可测试性类实例通常更容易测试(通过模拟类的实例)。依赖函数也可以很容易地模拟和测试。

何时使用类依赖注入?

  • 共享服务:如果你需要一个在多个请求之间共享的服务,如数据库连接池、配置管理或缓存服务。
  • 复杂逻辑:当你的依赖项包含多个方法或复杂的初始化逻辑时,类会是一个好的选择。
  • 持久化的状态:类依赖可以保存一些状态并在多个请求之间共享,这在需要多个请求共用某些信息时非常有用(如认证信息、缓存状态等)。

何时使用函数依赖注入?

  • 轻量级服务:如果你的依赖项很简单,并且每个请求都有不同的实例(如数据库连接),使用函数依赖注入更加简单和直接。
  • 无状态的服务:当服务不需要保存任何状态,或者每个请求都需要创建新实例时,函数依赖注入更为合适。
  • 简化的服务管理:函数依赖注入适合那些只需创建一次并简单使用的服务,不需要复杂的构造函数和多种方法。

总结

  • 函数依赖注入:适合处理短生命周期、无状态的依赖项,如数据库连接、外部 API 客户端、短期的服务等。
  • 类依赖注入:适合需要共享状态、较复杂逻辑、长生命周期的服务,如数据库连接池、外部服务的客户端、全局配置服务等。

根据你的需求选择合适的方式来实现依赖注入,通常函数依赖注入更适合简单和请求级别的资源,而类依赖注入则适合需要跨请求共享状态或者有复杂逻辑的服务。