2 个稳定版本
1.2.4 | 2022年6月26日 |
---|---|
1.2.3 | 2022年5月4日 |
1.2.1 |
|
1.1.0 |
|
0.1.6 |
|
#13 in #async-trait
55 每月下载量
用于 5 crates
315KB
4.5K SLoC
anthill-di
Rust ioc 系统
适用于深度依赖树
优势
- 两种工作模式:异步(可选阻塞 fn 版本)/ 同步
- 同步/异步构造函数(异步构造函数仅在异步模式下可用)
- 并行构建
- 运行时注入
- 运行时检查依赖循环[默认为可选的
loop-check
功能] - 3 种生命周期类型(瞬态/单例/上下文相关)
- 3 种注入方式:简单特质构造函数、异步/同步闭包作为构造函数、实例(仅在异步模式下可用异步闭包)
- 完整的调试信息显示,只需打印上下文(您可以使用附加的
debug-type-info
功能探索更多信息) - 灵活性:您可以在任何上下文嵌套中添加或删除组件/服务,检查组件/服务是否存在
缺陷
- 运行时检查依赖循环需要一些时间进行同步(您可以移除
loop-check
功能) - 异步构建需要一些时间进行同步(异步是可选的,要禁用请设置
default-features = false
) - 非全局上下文,仅树状结构,具有根/子上下文
警告
库需要 Rust nightly
基本概念
组件来源是 DependencyContext
/* creating root DependencyContext */
fn _() {
let root_context = DependencyContext::new_root()
}
有几种方法可以注册组件
/* all way for component registration */
async fn _() {
//let root_context = DependencyContext::new_root()
// adds component, which implement Constructor trait
root_context.register_type::<SomeComponent>(DependencyLifeCycle::Transient).await.unwrap();
// adds component from closure
root_context.register_closure(|_| Ok(SomeComponent {}), DependencyLifeCycle::Transient).await.unwrap();
// adds component from async closure
root_context.register_async_closure(
move |_: crate::DependencyContext| { async move { Ok(SomeComponent {}) }},
DependencyLifeCycle::Transient
).await.unwrap();
// adds an existing component
root_context.register_instance(RwLock::new(instance)).await.unwrap();
}
每种类型的组件注册(除实例注册外)都包含 3 个生命周期
实例注册始终是单例
/* Life times */
async fn _() {
//let root_context = DependencyContext::new_root()
// instance per call
root_context.register_type::<SomeComponent>(DependencyLifeCycle::Transient).await.unwrap();
// single instance
root_context.register_type::<SomeComponent>(DependencyLifeCycle::Singleton).await.unwrap();
// instance per local context
root_context.register_type::<SomeComponent>(DependencyLifeCycle::ContextDependent).await.unwrap();
}
要通过类型注册组件,您需要实现 Constructor 特质
/* Constructor implementation */
#[async_trait_with_sync::async_trait(Sync)]
impl Constructor for SomeComponent {
async fn ctor(_: crate::DependencyContext) -> BuildDependencyResult<Self> {
Ok( Self { } )
}
}
组件在构建时都会获得一个唯一上下文
您可以将上下文存储在结构体字段中,并在组件的任何地方获取依赖项
可以从上下文中请求嵌套(子)依赖项
/* Resolve nested service */
#[async_trait_with_sync::async_trait(Sync)]
impl Constructor for SomeComponent {
async fn ctor(ctx: crate::DependencyContext) -> BuildDependencyResult<Self> {
Ok( Self {
nested_service1: ctx.resolve().await?,
nested_service2: ctx.resolve().await?
} )
}
}
组件上下文与根上下文相同,这意味着它可以注册依赖项
/* register new dependency */
#[async_trait_with_sync::async_trait(Sync)]
impl Constructor for SomeComponent {
async fn ctor(ctx: crate::DependencyContext) -> BuildDependencyResult<Self> {
ctx.register_type::<SomeComponent2>(DependencyLifeCycle::Transient).await
.map_err(|e| BuildDependencyError::AddDependencyError { err: e })?;
Ok( Self {
nested_service1: ctx.resolve::<SomeComponent2>().await?,
nested_service2: ctx.resolve().await?
} )
}
}
您可以通过 TypeId 解析 first
(所有匹配的依赖项作为 vector
),或通过 TypeId
/* dependency resolving way */
async fn _() {
//let root_context = DependencyContext::new_root()
//root_context.register_type::<SomeComponent>(DependencyLifeCycle::Transient).await.unwrap();
// return first (by TypeId) component
let mut dependency = root_context.resolve::<SomeComponent>().await.unwrap();
// return all match as Vector<SomeComponent> (look at service mappings section)
let mut dependency_vector = root_context.resolve_collection::<Box<dyn SomeTrait>>().await.unwrap();
// return service with component by type_id
root_context.resolve_by_type_id::<Box<dyn GetStr>>(TypeId::of::<TransientDependency>()).await.unwrap()
}
每个生命周期解析方式不同
/* dependency resolving match */
async fn _() {
//let root_context = DependencyContext::new_root()
//root_context.register_type::<SomeComponent1>(DependencyLifeCycle::Transient).await.unwrap();
//root_context.register_type::<SomeComponent2>(DependencyLifeCycle::Singleton).await.unwrap();
//root_context.register_type::<SomeComponent3>(DependencyLifeCycle::ContextDependent).await.unwrap();
// resolve transient
let mut dependency = root_context.resolve::<SomeComponent1>().await.unwrap();
// resolve singleton
let mut dependency2 = root_context.resolve::<Arc<SomeComponent2>>().await.unwrap();
// resolve local context dependency
let mut dependency3 = root_context.resolve::<Weak<SomeComponent3>>().await.unwrap();
// To get a mutable singleton you need to register with RwLock/Lock
// Constructor trait implemented by default for tokio::sync::RwLock<T>, std::sync::RwLock<T>
}
您可以将组件映射到服务
默认情况下,仅创建组件映射到自身的映射
您在映射数量上没有限制
/* component to service mapping */
async fn _() {
//let root_context = DependencyContext::new_root()
// mapping at creation time
root_context.register_type::<SomeComponent>(DependencyLifeCycle::Transient).await.unwrap()
.map_as::<dyn SomeImplementedTrait1>().await.unwrap();
// map after creation
root_context.map_component::<SomeComponent, dyn SomeImplementedTrait2>().await.unwrap();
}
服务解析在 Box<T>
/* lifetime resolving */
async fn _() {
//let root_context = DependencyContext::new_root()
//root_context.register_type::<SomeComponent>(DependencyLifeCycle::Transient).await.unwrap()
// .map_as::<dyn SomeImplementedTrait>().await.unwrap();
let service = root_context.resolve::<Box<dyn SomeImplementedTrait>>().await.unwrap();
}
上下文相关组件在所有本地上下文的 Arc 被移除之前存活
子实例包含父实例的局部上下文
如果父实例在子实例创建之前更改局部上下文,则子实例将使用新的父实例局部上下文创建
您可以始终创建一个新的局部上下文,或设置一个旧的上下文
/* local context manipulation */
#[async_trait_with_sync::async_trait(Sync)]
impl Constructor for SomeComponent {
async fn ctor(ctx: crate::DependencyContext) -> BuildDependencyResult<Self> {
// take old local context, we can save it for using two local context
let old_context = ctx.get_context();
//instance from old local context
let instance1 = ctx.resolve::<SomeInstance>().await?;
// return new local context after create
let new_context = ctx.set_empty_context();
//instance from new local context
let instance2 = ctx.resolve::<SomeInstance>().await?;
// set old local context
ctx.set_context(old_context);
//instance from old local context
let instance3 = ctx.resolve::<SomeInstance>().await?;
Ok( Self { } )
}
}
您可以删除瞬态和单例组件
/* delete component */
async fn _() {
//let root_context = DependencyContext::new_root()
//root_context.register_type::<SomeComponent>(DependencyLifeCycle::Transient).await.unwrap()
// .map_as::<dyn SomeImplementedTrait>().await.unwrap();
root_context.delete_component::<SomeComponent>().await.unwrap();
}
您可以检查组件/服务的存在
/* check service */
async fn _() {
//let root_context = DependencyContext::new_root()
//root_context.register_type::<SomeComponent>(DependencyLifeCycle::Transient).await.unwrap()
// .map_as::<dyn SomeImplementedTrait>().await.unwrap();
let is_exist = root_context.is_component_exist::<TransientDependency>().await;
let is_exist = root_context.is_component_with_type_id_exist(TypeId::of::<TransientDependency>()).await;
let is_exist = root_context.is_service_exist::<Box<dyn SomeImplementedTrait>>().await;
let is_exist = root_context.is_service_with_type_id_exist(TypeId::of::<Box<dyn SomeImplementedTrait>>()).await;
}
全局上下文验证请求的依赖项的链接,并在出现循环依赖时返回错误
如果检查成功,则后续对此对链接的所有请求将不会检查循环。您可以通过 loop-check
功能禁用此行为
您可以通过添加 debug-type-info
功能来调试内部状态。输出将包含一些额外的 type_info 字段
async fn _() {
let root_context = DependencyContext::new_root()
root_context.register_type::<SomeComponent>(DependencyLifeCycle::Transient).await.unwrap()
.map_as::<dyn SomeImplementedTrait>().await.unwrap();
println!("{root_context:#?}");
}
基本示例
use anthill_di::{
Constructor,
types::BuildDependencyResult,
DependencyContext,
DependencyLifeCycle
};
struct TransientDependency1 {
pub d1: TransientDependency2,
pub d2: TransientDependency2,
}
#[async_trait_with_sync::async_trait(Sync)]
impl Constructor for TransientDependency1 {
async fn ctor(ctx: DependencyContext) -> BuildDependencyResult<Self> {
Ok(Self {
d1: ctx.resolve().await?,
d2: ctx.resolve().await?,
})
}
}
struct TransientDependency2 {
pub str: String,
}
#[async_trait_with_sync::async_trait(Sync)]
impl Constructor for TransientDependency2 {
async fn ctor(_: DependencyContext) -> BuildDependencyResult<Self> {
Ok(Self { str: "test".to_string() })
}
}
#[tokio::main]
fn main() {
let root_context = DependencyContext::new_root();
root_context.register_type::<TransientDependency1>(DependencyLifeCycle::Transient).await.unwrap();
root_context.register_type::<TransientDependency2>(DependencyLifeCycle::Transient).await.unwrap();
let dependency = root_context.resolve::<TransientDependency1>().await.unwrap();
assert_eq!(dependency.d1.str, "test".to_string());
assert_eq!(dependency.d2.str, "test".to_string());
}
更多共享示例在 src/tests 文件夹中
基准测试
在 i5 上测试6500,24gb
设置 | 瞬态设置 | 瞬态获取* | 瞬态删除 | 单例设置 | 单例获取* | 单例删除 | 上下文相关设置 | 上下文相关获取* |
---|---|---|---|---|---|---|---|---|
同步模式 | ~2,1us | ~5,9us | ~1,9us | ~2,1us | ~4,5us | ~2us | ~2us | ~4,9us |
同步模式 + loop-check | ~2,2us | ~6us | ~2us | ~2,3us | ~4,8us | ~2,5us | ~2,3us | ~5us |
异步模式 | ~1,6us | ~6,4us | ~2.3us | ~1,8us | ~4,8us | ~2,4us | ~1,8us | ~5,3us |
异步模式 + loop-check | ~1,8us | ~6,4us | ~2,6us | ~2us | ~4,8us | ~2,7us | ~2us | ~5,3us |
* 第一次请求检查循环并保存结果为已检查,后续请求与不带 loop-check 版本的请求相等
功能
loop-check
- 检查循环解析 [默认启用]debug-type-info
- 添加一些额外的TypeInfo
字段,以进行扩展调试显示async-mode
- 切换到异步模式,如果禁用,则所有功能将不会异步 [默认启用]blocking
- 添加blocking_
函数版本,需要async-mode
小型架构概述
- 注册依赖项 + 如何构建类型 + 生命周期 自动生成组件 + LifecycleBuilder + 将假服务作为组件到组件的组件
- 将附加服务组件注册到实现的特性(可能以后会有其他类似闭包构建的东西)
- 请求依赖项 验证链接 -> 取第一个服务(或未来的集合) -> 通过服务中的 TypeId 调用 LifecycleBuilder -> LifecycleBuilder 构建作为 CycledInstance 的组件(空/Arc/Weak) -> 使用 CycledInstance 调用服务 -> 返回服务
引用
依赖项
~3–10MB
~77K SLoC