2个版本

0.2.1 2024年7月23日
0.2.0 2024年7月23日

#252 in HTTP服务器

Download history 210/week @ 2024-07-21 8/week @ 2024-07-28

每月 218 次下载

MIT/Apache

49KB
675

teloc

Teloc是一个简单的Rust编译时DI框架,灵感来源于C#依赖注入框架

什么是DI?

维基百科链接

依赖注入(DI)是一种技术,其中对象接收其依赖的其他对象。这些其他对象称为依赖项。在典型的“使用”关系中,接收对象称为客户端,传递(即“注入”)的对象称为服务。将服务传递给客户端的代码可以是许多种类,称为注入器。客户端不需要指定它将使用哪种服务,注入器告诉客户端使用哪种服务。“注入”指的是将依赖项(服务)传递给将使用它的对象(客户端)。

亮点

  • 编译时 - teloc使用强大的Rust类型系统在编译时检查依赖项的存在及其正确的生命周期。这意味着如果所需的依赖项尚未注册或其生命周期短于请求的生命周期,则无法编译代码。如果您的代码可以编译,则意味着它将运行!
  • 零开销 - teloc仅使用零开销抽象,如特质、泛型、新类型和单元类型,以及编译时依赖项解析,因此您无需担心运行时的开销。
  • 简单的API - teloc提供了一个简单的API,仅需要一个结构和一个属性宏即可与库一起工作。
  • 与现有环境的集成 - teloc可以与任何现有的框架(如actix-web、warp、rocket)一起使用。现在仅支持actix-web,如示例所示。

如何使用

有一个类型可以作为服务的提供者:ServiceProvider。它用作存储依赖项的仓库,具有InstanceSingleton生命周期,并使用.add_*()方法声明所有依赖项。它可以分叉以创建具有局部实例的局部作用域。

依赖项有四种生命周期

  1. Transient. 服务将在解析时创建。可以依赖任何生命周期依赖。
  2. Singleton. 当解析时,服务将在ServiceProvider中一次性创建(懒加载)。可以依赖任何生命周期依赖。但不能依赖来自分叉的ServiceProvider实例的服务。
  3. Instance. 依赖在ServiceProvider外部创建,并可被其他依赖使用。

工作方式

  1. 声明你的结构体。
  2. 创建构造函数,并在其上添加#[inject]宏。
  3. 创建一个ServiceProvider对象。
  4. 使用ServiceProvider::add_*方法添加服务和依赖。
  5. 如果需要创建局部作用域,请分叉ServiceProvider
  6. 使用.resolve()方法从提供者获取服务。
  7. 与服务交互。

示例

use teloc::*;

// Declare your structs
struct ConstService {
    number: i32,
}
// #[inject] macro is indicate that dependency can be constructed using this function
#[inject]
impl ConstService {
    pub fn new(number: i32) -> Self {
        ConstService { number }
    }
}

// Derive macro can be used when all fields implement `Dependency` trait, but 
// we recommend using the #[inject] macro it in production code instead.
#[derive(Dependency)]
struct Controller {
    number_service: ConstService,
}

fn main() {
    // Create `ServiceProvider` struct that store itself all dependencies
    let sp = ServiceProvider::new()
        // Add dependency with `Singleton` lifetime. More about lifetimes see above.
        .add_singleton::<ConstService>()
        // Add dependency with `Transient` lifetime. More about lifetimes see above.
        .add_transient::<Controller>();
    // Fork `ServiceProvider`. It creates a new `ServiceProvider` which will have
    // access to the dependencies from parent `ServiceProvider`.
    let scope = sp
        // .fork() method creates a local mutable scope with self parent immutable `ServiceProvider`.
        .fork()
        // Add an instance of `i32` that will be used when `ConstService` will be initialized.
        .add_instance(10);
    // Get dependency from `ServiceProvider`
    let controller: Controller = scope.resolve();
    assert_eq!(controller.number_service.number, 10);
}

有关文档,请参阅docs.rs上的页面

有关更多示例,请参阅示例文件夹测试文件夹

与其他DI框架的比较

特性 teloc shaku waiter_di
编译时检查
可以在不使用dyn traits的情况下使用
一个应用中可以有多个服务提供者
不同的生命周期

如何读取错误

有时teloc可能会给出奇怪的大错误。但不要恐慌!你可以通过阅读阅读错误的说明来定义你的问题。

专业提示

本节包含了一些你可能希望在处理库时使用的专业提示。

获取ServiceProvider实例的类型

当你想将ServiceProvider的实例存储在结构体中或从函数返回或作为参数传递时,这会有用。

你需要什么

  1. ServiceProvider初始化后粘贴以下代码:let () = service_provider;
  2. 编译器将给出一个非常大的可怕类型,以teloc::ServiceProvider<...>开头。
  3. 将此类型复制到类型别名中,例如type ConcreteSP = /*编译器输出*/;
  4. 当你想写ServiceProvider实例类型时使用ConcreteSP
  5. 如果你更改ServiceProvider初始化,请重复步骤1-4。

许可

根据你的选择,在Apache License, Version 2.0MIT许可下许可。
除非你明确表示,否则根据Apache-2.0许可中定义的,你故意提交给此项目的任何贡献,都应如上双许可,不附加任何额外的条款或条件。

依赖关系

~1–6MB
~124K SLoC