跳转至

Symphra Container 技术规范文档

项目概述

项目名称: Symphra Container 类型: Python 依赖注入容器库 目标: 提供高性能、类型安全、易于使用的 DI 容器,支持多种生命周期、循环依赖处理和框架集成


设计决策确认表

决策项 选择方案 说明
服务键模式 混合模式 同时支持类型键和字符串键
字符串键类型提示 TypedDict + Literal 通过重载和类型提示保证 IDE 自动补全
API 风格 混合风格 同时提供 resolve()get() 方法
链式调用 不支持 每个操作独立,返回 None
生命周期类型 4 种 Singleton, Transient, Scoped, Factory
服务覆盖 override 参数 默认 False(忽略重复),True 时替换
可选依赖 优先容器解析 有默认值也优先从容器解析,无则使用默认值
解析失败 抛异常 无法解析时立即抛出详细异常

核心 API 快速参考

注册 API

# 类型键注册
container.register(UserService)  # 自动装配
container.register(IService, ServiceImpl)  # 指定实现
container.register(Service, lifetime=Lifetime.TRANSIENT)  # 生命周期
container.register(Service, NewImpl, override=True)  # 覆盖

# 字符串键注册
container.register("db", Database)
container.register_factory("logger", create_logger)
container.register_instance("config", config_obj)
container.register_value("api_key", "sk_...")

# 异步工厂
container.register_async_factory("async_db", async_create_db)

# 批量注册
container.scan("myapp.services")  # 自动扫描 @injectable 类
container.register_module(DatabaseModule())  # 模块化注册

获取 API

# 类型键获取
service: UserService = container.resolve(UserService)  # 推荐
service = container.try_resolve(UserService)  # Optional[UserService]

# 字符串键获取
db: Database = container.get("database")  # 类型注解辅助
db = container.get("database", Database)  # 显式指定类型
db = container.try_get("database")  # Optional[Database]

# 异步获取
service = await container.resolve_async(Service)
service = await container.get_async("service_key")

# 检查和元数据
exists = container.has("database")  # bool
descriptor = container.get_descriptor(UserService)  # 服务描述符
all_loggers = container.get_all(Logger)  # 获取所有同类型服务

删除和覆盖 API

# 删除
container.unregister(UserService)  # 按类型删除
container.unregister("database")  # 按键删除
container.try_unregister("optional")  # 安全删除
container.unregister(IService, all=True)  # 删除所有实现

# 覆盖
container.replace(OldService, NewService)  # 直接替换
container.unregister(Service)  # 或先删后注册
container.register(Service, NewImpl, override=True)

# 清空
container.clear()  # 清空所有服务
container.clear(lifetime=Lifetime.TRANSIENT)  # 清空特定生命周期

作用域 API

# 创建作用域
with container.create_scope() as scope:
    service = scope.resolve(UserService)

# 异步作用域
async with container.create_async_scope() as scope:
    service = await scope.resolve_async(UserService)

# 子容器(继承父容器)
child = container.create_child()

装饰器 API

# 类装饰器
@injectable
class Service: pass  # 默认 Singleton

@injectable.transient
class Service: pass

@injectable.factory
def create_service(): pass

# 方法装饰器
@inject
def process(service: UserService = Injected):
    pass

# 属性注入
class Service:
    db: Database = inject()
    cache: Cache = inject("cache_service")

类型系统架构

1. 核心类型层次

ServiceKey (Union[Type[T], str])
    ├── Type[T] - 类型键(推荐)
    └── str - 字符串键

Lifetime (Enum)
    ├── SINGLETON - 全局唯一
    ├── TRANSIENT - 每次创建
    ├── SCOPED - 作用域共享
    └── FACTORY - 工厂模式

ProviderType (Enum)
    ├── CLASS - 类构造
    ├── FACTORY - 工厂函数
    ├── INSTANCE - 直接实例
    └── VALUE - 配置值

2. 类型提示保证

方案 A: 类型键(100% IDE 支持)

class UserService:
    pass

container.register(UserService)
service: UserService = container.resolve(UserService)  # ✅ IDE 完美推断

方案 B: 字符串键 + 类型注解

# 使用类型注解作为类型提示来源
db: Database = container.get("database")  # ✅ IDE 从注解推断

# 或显式指定类型
db = container.get("database", Database)  # ✅ IDE 推断为 Database

方案 C: Literal + Overload(最优)

from typing_extensions import Literal, overload

class Container:
    @overload
    def get(self, key: Literal["database"]) -> Database: ...

    @overload
    def get(self, key: Literal["cache"]) -> Cache: ...

    @overload
    def get(self, key: str, service_type: Type[T]) -> T: ...

# 使用时 IDE 自动完成
db = container.get("database")  # ✅ IDE 知道返回 Database
cache = container.get("cache")  # ✅ IDE 知道返回 Cache

3. TypedDict 方案(可选增强)

from typing_extensions import TypedDict

class ServiceRegistry(TypedDict, total=False):
    database: Database
    cache: Cache
    logger: Logger

# 用于 IDE 类型提示和代码生成
registry: ServiceRegistry = {
    "database": PostgresDB(),
    "cache": RedisCache(),
    "logger": FileLogger(),
}

# IDE 可以根据 registry 的定义提供自动补全

循环依赖处理机制

问题场景

# 场景 1: 直接循环
class A:
    def __init__(self, b: B):
        self.b = b

class B:
    def __init__(self, a: A):
        self.a = a

# 场景 2: 间接循环
class A:
    def __init__(self, b: B): pass

class B:
    def __init__(self, c: C): pass

class C:
    def __init__(self, a: A): pass

解决方案

方案 1: Lazy 包装(推荐)✅

from symphra_container import Lazy

class A:
    def __init__(self, b: Lazy[B]):
        self.b = b  # 首次访问 self.b 时才解析 B

class B:
    def __init__(self, a: A):
        self.a = a

# 完全避免循环依赖!
container.register(A)
container.register(B)
a = container.resolve(A)  # ✅ 成功

方案 2: 属性/方法注入

class A:
    def __init__(self):
        self.b: Optional[B] = None

class B:
    def __init__(self, a: A):
        self.a = a

# 手动初始化器
def init_a(a: A, b: B):
    a.b = b

container.register(A)
container.register(B)
container.register_initializer(A, init_a, [B])

方案 3: 工厂函数延迟

def create_a(b: Lazy[B]):
    return A(b)

container.register_factory(A, create_a)
container.register(B)

检测机制

解析栈: [A]
  ├─ 解析 A 的依赖
  ├─ 需要 B,栈: [A, B]
  │  ├─ 解析 B 的依赖
  │  ├─ 需要 A,栈: [A, B, A]
  │  ├─ 检测到循环!A 已在栈中
  │  └─ 抛出 CircularDependencyError
  │     详细信息: A -> B -> A

生命周期管理详解

1. Singleton(单例)

# 全局唯一实例
container.register(Database, lifetime=Lifetime.SINGLETON)

db1 = container.resolve(Database)
db2 = container.resolve(Database)
assert db1 is db2  # ✅ 同一实例

# 子容器也共享
child = container.create_child()
db3 = child.resolve(Database)
assert db3 is db1  # ✅ 全局共享

特性: - 线程安全的单例创建(双重检查锁) - 应用启动时或首次请求时初始化 - 适用于无状态服务(数据库、缓存、日志)

2. Transient(瞬态)

# 每次创建新实例
container.register(RequestHandler, lifetime=Lifetime.TRANSIENT)

handler1 = container.resolve(RequestHandler)
handler2 = container.resolve(RequestHandler)
assert handler1 is not handler2  # ✅ 不同实例

特性: - 每次请求都创建新实例 - 无内存泄漏风险 - 适用于有状态服务、请求处理器

3. Scoped(作用域)

# 作用域内唯一
container.register(UnitOfWork, lifetime=Lifetime.SCOPED)

with container.create_scope() as scope1:
    uow1a = scope1.resolve(UnitOfWork)
    uow1b = scope1.resolve(UnitOfWork)
    assert uow1a is uow1b  # ✅ 作用域内共享

with container.create_scope() as scope2:
    uow2 = scope2.resolve(UnitOfWork)
    assert uow2 is not uow1a  # ✅ 不同作用域隔离

特性: - 使用 contextvars 实现线程安全 - 作用域销毁时自动清理资源 - 适用于 Web 请求、数据库事务

4. Factory(工厂)

# 使用工厂函数创建
def create_db(config: Config):
    return Database(config.db_url)

container.register_factory("database", create_db, lifetime=Lifetime.SINGLETON)

# 工厂可以有依赖,由容器自动注入
db = container.get("database")

特性: - 灵活的创建逻辑 - 支持异步工厂 - 可以依赖其他服务


依赖注入模式

1. 构造函数注入(优先)

class UserService:
    def __init__(self, db: Database, logger: Logger):
        self.db = db
        self.logger = logger

# 容器自动注入
service = container.resolve(UserService)
assert service.db is not None
assert service.logger is not None

优势: - ✅ 类型安全 - ✅ IDE 支持 - ✅ 不可变对象友好 - ✅ 依赖清晰

2. 属性注入(特殊情况)

class Service:
    db: Database = inject()  # 使用描述符
    logger: Logger = inject("logger_service")

# 初始化
service = Service()  # db 和 logger 自动注入

用途: - 可选依赖 - 循环依赖 - 遗留代码兼容

3. 方法注入(初始化)

class Service:
    def setup(self, db: Database, cache: Cache):
        self.db = db
        self.cache = cache

# 注册初始化器
container.register_initializer(
    Service,
    lambda svc, db, cache: svc.setup(db, cache),
    [Database, Cache]
)

错误处理规范

异常层次

ContainerException (基类)
├── ServiceNotFoundError - 服务未注册
│   └── "Service UserService not found in container"
├── CircularDependencyError - 循环依赖
│   └── "Circular dependency detected: A -> B -> C -> A"
├── InvalidServiceError - 无效服务定义
│   └── "Service type cannot be None"
├── RegistrationError - 注册错误
│   └── "Service UserService already registered, use override=True"
└── ResolutionError - 解析错误
    └── "Cannot resolve UserService: missing required dependency 'db'"

错误消息示例

try:
    container.resolve(NonExistentService)
except ServiceNotFoundError as e:
    # ServiceNotFoundError: Service NonExistentService not found in container
    # Available services:
    #   - UserService (Lifetime.SINGLETON)
    #   - Database (Lifetime.SINGLETON)
    #   - Logger (Lifetime.TRANSIENT)
    pass

try:
    container.resolve(ServiceWithCircularDep)
except CircularDependencyError as e:
    # CircularDependencyError: Circular dependency detected:
    # ServiceA -> ServiceB -> ServiceC -> ServiceA
    #
    # To fix:
    # 1. Use Lazy[ServiceA] in ServiceC
    # 2. Use property injection
    # 3. Restructure your dependencies
    pass

性能目标

操作 目标 说明
简单解析 < 1 μs 单个依赖
复杂解析 < 50 μs 10 层嵌套依赖
单例缓存 < 100 ns 已缓存实例查询
启动时间 < 100 ms 1000 个服务注册
内存占用 < 1 MB 1000 个服务定义

项目结构规划

symphra-container/
├── src/symphra_container/
│   ├── __init__.py                 # 公共 API 导出
│   ├── container.py                # 核心容器类
│   ├── registry.py                 # 服务注册表
│   ├── resolver.py                 # 依赖解析器
│   ├── types.py                    # 类型定义
│   ├── enums.py                    # 枚举类型
│   ├── exceptions.py               # 异常定义
│   │
│   ├── lifetimes/
│   │   ├── __init__.py
│   │   ├── manager.py              # 生命周期管理器基类
│   │   ├── singleton.py            # 单例管理器
│   │   ├── transient.py            # 瞬态管理器
│   │   ├── scoped.py               # 作用域管理器
│   │   └── factory.py              # 工厂管理器
│   │
│   ├── injection/
│   │   ├── __init__.py
│   │   ├── detector.py             # 依赖检测器
│   │   ├── constructor.py          # 构造函数注入
│   │   ├── property.py             # 属性注入
│   │   └── method.py               # 方法注入
│   │
│   ├── circular/
│   │   ├── __init__.py
│   │   ├── detector.py             # 循环依赖检测器
│   │   └── lazy_proxy.py           # 延迟代理实现
│   │
│   ├── decorators/
│   │   ├── __init__.py
│   │   ├── injectable.py           # @injectable 装饰器
│   │   └── inject.py               # @inject 装饰器
│   │
│   ├── scopes/
│   │   ├── __init__.py
│   │   ├── scope.py                # 作用域实现
│   │   └── context.py              # 作用域上下文
│   │
│   ├── integrations/
│   │   ├── __init__.py
│   │   ├── fastapi.py              # FastAPI 集成
│   │   └── flask.py                # Flask 集成
│   │
│   └── utils/
│       ├── __init__.py
│       ├── inspection.py            # 反射工具
│       └── typing_helpers.py        # 类型工具
├── tests/
│   ├── unit/
│   │   ├── test_container.py
│   │   ├── test_lifetimes.py
│   │   ├── test_injection.py
│   │   ├── test_circular.py
│   │   └── test_resolver.py
│   │
│   ├── integration/
│   │   ├── test_fastapi.py
│   │   └── test_flask.py
│   │
│   ├── performance/
│   │   └── benchmark.py
│   │
│   └── conftest.py
├── docs/
│   ├── index.md
│   ├── guide/
│   │   ├── quick_start.md
│   │   ├── registration.md
│   │   ├── resolution.md
│   │   ├── lifetimes.md
│   │   └── advanced.md
│   │
│   ├── api/
│   │   └── reference.md
│   │
│   └── examples/
│       ├── simple.py
│       ├── fastapi_example.py
│       └── flask_example.py
├── pyproject.toml
├── setup.py
├── README.md
├── API_DESIGN.md                   # 完整 API 文档
├── TECHNICAL_SPEC.md               # 本文件
└── CHANGELOG.md

开发阶段分解

阶段 1: 核心架构(3-5 天)

  • 项目初始化和工程配置
  • 核心类型系统设计
  • 容器基础类实现
  • 依赖解析器基础实现

阶段 2: 注册与解析(4-6 天)

  • 多种生命周期管理器
  • 工厂函数和实例注册
  • 自动装配功能
  • 属性和方法注入

阶段 3: 高级特性(5-7 天)

  • 循环依赖检测
  • Lazy Proxy 实现
  • 作用域管理
  • 异步支持

阶段 4: 框架集成(4-5 天)

  • 装饰器系统
  • FastAPI 集成
  • Flask 集成
  • 配置加载器

阶段 5: 测试与优化(5-7 天)

  • 单元测试和集成测试
  • 性能基准测试和优化
  • 类型检查完善
  • 文档编写

关键设计原则

1. KISS(保持简单)

  • 简洁的 API 设计
  • 最小化用户配置
  • 清晰的错误消息

2. SOLID 原则

  • S: 单一职责 - 每个类只负责一个方面
  • O: 开闭原则 - 易于扩展,难以修改
  • L: Liskov 替换 - 遵循协议定义
  • I: 接口隔离 - 精细化接口
  • D: 依赖倒置 - 依赖抽象,不依赖具体

3. 类型安全

  • 完整的类型提示
  • MyPy 严格模式通过
  • IDE 自动补全支持

4. 高性能

  • 解析栈缓存
  • 单例快速查询
  • 最小化反射调用

检验清单

完成后需验证:

  • 所有 4 种生命周期正确实现
  • 字符串键和类型键混合使用
  • IDE 自动补全正常工作
  • 循环依赖自动检测
  • Lazy Proxy 正确工作
  • 异步容器完全支持
  • FastAPI/Flask 集成可用
  • 测试覆盖率 ≥ 90%
  • 文档完整详细
  • 性能指标达标