2 个稳定版本

1.2.4 2022年6月26日
1.2.3 2022年5月4日
1.2.1 2022年4月10日
1.1.0 2022年2月26日
0.1.6 2021年9月27日

#13 in #async-trait

Download history 63/week @ 2024-03-11 85/week @ 2024-03-18 22/week @ 2024-03-25 40/week @ 2024-04-01 24/week @ 2024-04-08 10/week @ 2024-04-15 18/week @ 2024-04-22 13/week @ 2024-04-29 15/week @ 2024-05-06 15/week @ 2024-05-13 20/week @ 2024-05-20 9/week @ 2024-05-27 16/week @ 2024-06-03 15/week @ 2024-06-10 9/week @ 2024-06-17 14/week @ 2024-06-24

55 每月下载量
用于 5 crates

MIT 许可证

315KB
4.5K SLoC

build & tests

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

小型架构概述

  1. 注册依赖项 + 如何构建类型 + 生命周期 自动生成组件 + LifecycleBuilder + 将假服务作为组件到组件的组件
  2. 将附加服务组件注册到实现的特性(可能以后会有其他类似闭包构建的东西)
  3. 请求依赖项 验证链接 -> 取第一个服务(或未来的集合) -> 通过服务中的 TypeId 调用 LifecycleBuilder -> LifecycleBuilder 构建作为 CycledInstance 的组件(空/Arc/Weak) -> 使用 CycledInstance 调用服务 -> 返回服务

引用

依赖项

~3–10MB
~77K SLoC