Files
trumandu--myboot/docs/dependency-injection.md
T
2026-07-13 12:34:57 +08:00

628 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 依赖注入使用指南
MyBoot 框架提供了基于 `dependency_injector` 的自动依赖注入功能,让您可以轻松管理服务之间的依赖关系,无需手动获取和传递依赖。
## 目录
- [快速开始](#快速开始)
- [基本用法](#基本用法)
- [声明依赖](#1-声明依赖)
- [服务命名规则](#2-服务命名规则)
- [多级依赖](#3-多级依赖)
- [可选依赖](#4-可选依赖)
- [Client 依赖注入](#5-client-依赖注入)
- [Component 组件](#6-component-组件)
- [高级特性](#高级特性)
- [最佳实践](#最佳实践)
- [常见问题](#常见问题)
## 快速开始
### 安装依赖
确保已安装 `dependency_injector`
```bash
pip install dependency-injector
```
### 基本示例
```python
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} 已创建")
```
框架会自动:
1. 检测 `OrderService` 的依赖(`UserService``EmailService`
2. 按正确的顺序初始化服务
3. 自动注入依赖到 `OrderService` 的构造函数
## 基本用法
### 1. 声明依赖
通过类型注解声明依赖是最简单的方式:
```python
@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_service`
- `EmailService``email_service`
- `DatabaseClient``database_client`
- **自定义命名**:通过装饰器参数指定
```python
@service('custom_user_service')
class UserService:
pass
```
### 3. 多级依赖
支持多级依赖,框架会自动处理依赖顺序:
```python
@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` 类型注解声明可选依赖:
```python
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 中:
```python
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_client`
- `RedisClient` → `redis_client`
- **自定义命名**:通过装饰器参数指定
```python
@client(name="my_redis")
class RedisClient:
pass
```
#### Client 查找方式
框架支持多种方式查找 Client 依赖:
```python
@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` 装饰器用于注册通用组件,支持依赖注入。它可用于任意需要托管的类(工具类、配置类、包含定时任务的类等)。
#### 基本用法
```python
from myboot.core.decorators import component
@component()
class EmailHelper:
"""邮件工具类"""
def send(self, to: str, content: str):
print(f"发送邮件到 {to}: {content}")
```
#### 带依赖注入
```python
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` 类中的定时任务。
```python
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 配置选项
```python
@component(
name='my_component', # 组件名称,默认使用类名的 snake_case
scope='singleton', # 生命周期:'singleton'(默认)或 'prototype'
lazy=False, # 是否懒加载
primary=False # 当按类型获取有多个匹配时,是否为首选
)
class MyComponent:
pass
```
#### 从容器获取组件
```python
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` 类型提示:
```python
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` 参数控制服务的生命周期:
```python
# 单例模式(默认)
@service(scope='singleton')
class UserService:
pass
# 工厂模式(每次创建新实例)
@service(scope='factory')
class TaskService:
pass
```
### 3. 循环依赖检测
框架会自动检测循环依赖并抛出清晰的错误:
```python
@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. 获取服务实例
在路由或其他地方获取服务实例:
```python
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. 使用类型注解
推荐使用类型注解声明依赖,代码更清晰:
```python
# ✅ 推荐
@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. 避免循环依赖
设计服务时避免循环依赖:
```python
# ✅ 好的设计
@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 没有接口,但可以通过抽象基类或协议定义接口:
```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`
```python
from typing import Optional
@service()
class ProductService:
def __init__(
self,
db: DatabaseClient, # 必需依赖
cache: Optional[CacheService] = None # 可选依赖
):
self.db = db
self.cache = cache
```
## 常见问题
### Q1: 依赖注入失败怎么办?
如果依赖注入失败,框架会自动回退到传统方式(直接实例化)。检查日志中的错误信息:
1. **依赖的服务未注册**:确保依赖的服务已使用 `@service()` 装饰器
2. **服务名不匹配**:检查服务名是否正确(类名转下划线命名)
3. **循环依赖**:重构代码消除循环依赖
### Q2: 如何调试依赖关系?
框架会在日志中输出依赖关系信息:
```
已注册服务提供者: user_service (依赖: set())
已注册服务提供者: order_service (依赖: {'user_service', 'email_service'})
```
### Q3: 可以在运行时动态获取服务吗?
可以,使用 `get_service()` 函数:
```python
from myboot.core.application import get_service
def some_function():
user_service = get_service('user_service')
if user_service:
# 使用服务
pass
```
### Q4: 支持异步服务吗?
目前依赖注入主要支持同步服务。对于异步服务,建议在服务内部处理异步逻辑。
### Q5: 如何测试带依赖的服务?
在测试中,可以手动创建服务实例并注入 mock 对象:
```python
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
```
## 完整示例
```python
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 组件,可包含定时任务
- ✅ 支持多种依赖查找方式(名称、类型)
- ✅ 保持向后兼容,现有代码无需修改
开始使用依赖注入,让代码更加清晰和可维护!