15 KiB
依赖注入使用指南
MyBoot 框架提供了基于 dependency_injector 的自动依赖注入功能,让您可以轻松管理服务之间的依赖关系,无需手动获取和传递依赖。
目录
快速开始
安装依赖
确保已安装 dependency_injector:
pip install dependency-injector
基本示例
from myboot.core.decorators import service
@service()
class UserService:
"""用户服务"""
def __init__(self):
self.users = {}
def get_user(self, user_id: int):
return self.users.get(user_id)
@service()
class EmailService:
"""邮件服务"""
def send_email(self, to: str, subject: str):
print(f"发送邮件到 {to}: {subject}")
@service()
class OrderService:
"""订单服务 - 自动注入 UserService 和 EmailService"""
def __init__(self, user_service: UserService, email_service: EmailService):
self.user_service = user_service
self.email_service = email_service
def create_order(self, user_id: int, product: str):
user = self.user_service.get_user(user_id)
self.email_service.send_email(user['email'], "订单创建", f"您的订单 {product} 已创建")
框架会自动:
- 检测
OrderService的依赖(UserService和EmailService) - 按正确的顺序初始化服务
- 自动注入依赖到
OrderService的构造函数
基本用法
1. 声明依赖
通过类型注解声明依赖是最简单的方式:
@service()
class ProductService:
def __init__(self, user_service: UserService, cache_service: CacheService):
self.user_service = user_service
self.cache_service = cache_service
框架会自动:
- 从类型注解中识别依赖的服务类
- 将类名转换为服务名(如
UserService→user_service) - 自动注入对应的服务实例
2. 服务命名规则
服务名称遵循以下规则:
-
默认命名:类名自动转换为下划线分隔的小写形式
UserService→user_serviceEmailService→email_serviceDatabaseClient→database_client
-
自定义命名:通过装饰器参数指定
@service('custom_user_service') class UserService: pass
3. 多级依赖
支持多级依赖,框架会自动处理依赖顺序:
@service()
class DatabaseClient:
def __init__(self):
self.connection = None
@service()
class UserRepository:
def __init__(self, db: DatabaseClient):
self.db = db
@service()
class UserService:
def __init__(self, user_repo: UserRepository):
self.user_repo = user_repo
依赖顺序:DatabaseClient → UserRepository → UserService
4. 可选依赖
使用 Optional 类型注解声明可选依赖:
from typing import Optional
@service()
class CacheService:
pass
@service()
class ProductService:
# cache_service 是可选的,如果不存在则为 None
def __init__(self, cache_service: Optional[CacheService] = None):
self.cache_service = cache_service
if self.cache_service:
# 使用缓存服务
pass
5. Client 依赖注入
除了 Service 之间的依赖注入,框架还支持将 Client 注入到 Controller 或 Service 中:
from myboot.core.decorators import client, service, rest_controller, get
@client()
class HttpClient:
"""HTTP 客户端"""
def request(self, url: str):
return {"url": url}
@client(name="redis_client") # 自定义名称
class RedisClient:
"""Redis 客户端"""
def get(self, key: str):
return None
@service()
class UserService:
"""注入 Client 到 Service"""
def __init__(self, http_client: HttpClient):
self.http_client = http_client
@rest_controller("/api")
class UserController:
"""注入 Client 和 Service 到 Controller"""
def __init__(self, user_service: UserService, redis_client: RedisClient):
self.user_service = user_service
self.redis_client = redis_client
@get("/users")
def list_users(self):
return []
Client 命名规则
-
默认命名:类名自动转换为下划线形式
HttpClient→http_clientRedisClient→redis_client
-
自定义命名:通过装饰器参数指定
@client(name="my_redis") class RedisClient: pass
Client 查找方式
框架支持多种方式查找 Client 依赖:
@client(name="my_http") # 自定义名称
class HttpClient:
pass
@rest_controller("/api")
class MyController:
# 以下方式都可以成功注入:
# 方式1:按自定义名称(参数名匹配)
def __init__(self, my_http: HttpClient):
pass
# 方式2:按自动转换名称
def __init__(self, http_client: HttpClient):
pass
# 方式3:按类型匹配(参数名任意)
def __init__(self, client: HttpClient):
pass
# 方式4:显式指定名称
def __init__(self, x: Provide['my_http']):
pass
6. Component 组件
@component 装饰器用于注册通用组件,支持依赖注入。它可用于任意需要托管的类(工具类、配置类、包含定时任务的类等)。
基本用法
from myboot.core.decorators import component
@component()
class EmailHelper:
"""邮件工具类"""
def send(self, to: str, content: str):
print(f"发送邮件到 {to}: {content}")
带依赖注入
from myboot.core.decorators import component, client
@client()
class SmtpClient:
def send_mail(self, to: str, subject: str, body: str):
pass
@component(name='email_helper')
class EmailHelper:
"""带依赖注入的组件"""
def __init__(self, smtp_client: SmtpClient):
self.smtp = smtp_client
def send(self, to: str, subject: str, body: str):
self.smtp.send_mail(to, subject, body)
包含定时任务的组件
重要:定时任务(@cron、@interval、@once)必须在 @component 装饰的类中定义。这是定义定时任务的唯一方式,不再支持模块级函数或 @service 类中的定时任务。
from myboot.core.decorators import component, service, cron, interval
@service()
class DataService:
def sync(self):
print("同步数据...")
def health_check(self):
print("健康检查...")
@component()
class DataSyncJobs:
"""数据同步任务集合 - 自动注入 DataService"""
def __init__(self, data_service: DataService):
self.data_service = data_service
@cron("0 2 * * *") # 每天凌晨 2 点
def sync_daily_data(self):
"""每日数据同步"""
self.data_service.sync()
@interval(hours=1) # 每小时
def check_data_health(self):
"""数据健康检查"""
self.data_service.health_check()
注意:
- 定时任务方法会在组件注册时自动扫描并注册到调度器
- 组件支持依赖注入,可以在构造函数中注入所需的服务
Component 配置选项
@component(
name='my_component', # 组件名称,默认使用类名的 snake_case
scope='singleton', # 生命周期:'singleton'(默认)或 'prototype'
lazy=False, # 是否懒加载
primary=False # 当按类型获取有多个匹配时,是否为首选
)
class MyComponent:
pass
从容器获取组件
from myboot.core.application import app
# 方式1:通过 container 获取
email_helper = app().container.get('email_helper')
# 方式2:通过 Application 直接获取
email_helper = app().get_component('email_helper')
# 方式3:依赖注入(推荐)
@component()
class NotificationService:
def __init__(self, email_helper: EmailHelper):
self.email_helper = email_helper
高级特性
1. 显式指定服务名
如果服务名与类名转换规则不匹配,可以使用 Provide 类型提示:
from myboot.core.di import Provide
@service('custom_user_service')
class UserService:
pass
@service()
class OrderService:
def __init__(self, user_service: Provide['custom_user_service']):
self.user_service = user_service
2. 服务生命周期
通过 scope 参数控制服务的生命周期:
# 单例模式(默认)
@service(scope='singleton')
class UserService:
pass
# 工厂模式(每次创建新实例)
@service(scope='factory')
class TaskService:
pass
3. 循环依赖检测
框架会自动检测循环依赖并抛出清晰的错误:
@service()
class ServiceA:
def __init__(self, service_b: ServiceB):
pass
@service()
class ServiceB:
def __init__(self, service_a: ServiceA):
pass
错误信息:
ValueError: 检测到循环依赖: service_a -> service_b -> service_a。
请重构代码以消除循环依赖。
4. 获取服务实例
在路由或其他地方获取服务实例:
from myboot.core.application import get_service
@get('/users/{user_id}')
def get_user(user_id: int):
user_service = get_service('user_service')
return user_service.get_user(user_id)
最佳实践
1. 使用类型注解
推荐使用类型注解声明依赖,代码更清晰:
# ✅ 推荐
@service()
class OrderService:
def __init__(self, user_service: UserService, email_service: EmailService):
self.user_service = user_service
self.email_service = email_service
# ❌ 不推荐(需要手动获取)
@service()
class OrderService:
def __init__(self):
from myboot.core.application import get_service
self.user_service = get_service('user_service')
self.email_service = get_service('email_service')
2. 避免循环依赖
设计服务时避免循环依赖:
# ✅ 好的设计
@service()
class UserService:
def __init__(self, user_repo: UserRepository):
self.user_repo = user_repo
@service()
class OrderService:
def __init__(self, user_service: UserService, order_repo: OrderRepository):
self.user_service = user_service
self.order_repo = order_repo
# ❌ 避免循环依赖
@service()
class ServiceA:
def __init__(self, service_b: ServiceB):
pass
@service()
class ServiceB:
def __init__(self, service_a: ServiceA):
pass
3. 使用接口而非具体实现
虽然 Python 没有接口,但可以通过抽象基类或协议定义接口:
from abc import ABC, abstractmethod
class IUserRepository(ABC):
@abstractmethod
def get_user(self, user_id: int):
pass
@service()
class UserRepository(IUserRepository):
def get_user(self, user_id: int):
return {"id": user_id}
@service()
class UserService:
def __init__(self, user_repo: IUserRepository):
self.user_repo = user_repo
4. 合理使用可选依赖
对于非必需的依赖,使用 Optional:
from typing import Optional
@service()
class ProductService:
def __init__(
self,
db: DatabaseClient, # 必需依赖
cache: Optional[CacheService] = None # 可选依赖
):
self.db = db
self.cache = cache
常见问题
Q1: 依赖注入失败怎么办?
如果依赖注入失败,框架会自动回退到传统方式(直接实例化)。检查日志中的错误信息:
- 依赖的服务未注册:确保依赖的服务已使用
@service()装饰器 - 服务名不匹配:检查服务名是否正确(类名转下划线命名)
- 循环依赖:重构代码消除循环依赖
Q2: 如何调试依赖关系?
框架会在日志中输出依赖关系信息:
已注册服务提供者: user_service (依赖: set())
已注册服务提供者: order_service (依赖: {'user_service', 'email_service'})
Q3: 可以在运行时动态获取服务吗?
可以,使用 get_service() 函数:
from myboot.core.application import get_service
def some_function():
user_service = get_service('user_service')
if user_service:
# 使用服务
pass
Q4: 支持异步服务吗?
目前依赖注入主要支持同步服务。对于异步服务,建议在服务内部处理异步逻辑。
Q5: 如何测试带依赖的服务?
在测试中,可以手动创建服务实例并注入 mock 对象:
def test_order_service():
# 创建 mock 依赖
mock_user_service = MockUserService()
mock_email_service = MockEmailService()
# 创建服务实例
order_service = OrderService(mock_user_service, mock_email_service)
# 测试
assert order_service is not None
完整示例
from myboot.core.decorators import service, get
from myboot.core.application import get_service
from typing import Optional
# 基础服务
@service()
class DatabaseClient:
def __init__(self):
self.connection = "connected"
print("✅ DatabaseClient 已初始化")
@service()
class CacheService:
def __init__(self):
self.cache = {}
print("✅ CacheService 已初始化")
# 仓储层
@service()
class UserRepository:
def __init__(self, db: DatabaseClient):
self.db = db
print("✅ UserRepository 已初始化(依赖: DatabaseClient)")
def find_by_id(self, user_id: int):
return {"id": user_id, "name": f"用户{user_id}"}
# 服务层
@service()
class UserService:
def __init__(
self,
user_repo: UserRepository,
cache: Optional[CacheService] = None
):
self.user_repo = user_repo
self.cache = cache
print("✅ UserService 已初始化(依赖: UserRepository, CacheService)")
def get_user(self, user_id: int):
# 尝试从缓存获取
if self.cache and user_id in self.cache.cache:
return self.cache.cache[user_id]
# 从数据库获取
user = self.user_repo.find_by_id(user_id)
# 存入缓存
if self.cache:
self.cache.cache[user_id] = user
return user
# 路由层
@get('/users/{user_id}')
def get_user(user_id: int):
user_service = get_service('user_service')
return user_service.get_user(user_id)
总结
依赖注入功能让您能够:
- ✅ 自动管理服务依赖关系
- ✅ 无需手动获取和传递依赖
- ✅ 支持多级依赖和可选依赖
- ✅ 自动检测循环依赖
- ✅ 支持 Client 注入到 Service 和 Controller
- ✅ 支持 Component 组件,可包含定时任务
- ✅ 支持多种依赖查找方式(名称、类型)
- ✅ 保持向后兼容,现有代码无需修改
开始使用依赖注入,让代码更加清晰和可维护!