9 个版本

0.2.2-alpha2022年4月16日
0.2.1 2022年12月19日
0.2.1-alpha2022年4月15日
0.1.3-alpha2022年4月3日

#380 in Rust 模式


3 crates 中使用

自定义许可

77KB
1.5K SLoC

dilib-rs

Crates.io License Docs Github-Actions

Rust 的依赖注入库。

用法

[dependencies]
dilib = "0.2.2-alpha"

示例

基本用法

use dilib::Container;

struct Printer;
impl Printer {
    pub fn print(&self, s: &str) {
        println!("{}", s);
    }
}

struct EnglishGreeting;
impl EnglishGreeting {
    pub fn greet(&self) -> String {
        "Hello!".to_string()
    }
}

struct SpanishGreeting;
impl SpanishGreeting {
    pub fn greet(&self) -> String {
        "Hola!".to_string()
    }
}

let mut container = Container::new();
container.add_singleton(Printer).unwrap();
container.add_scoped(|| EnglishGreeting).unwrap();
container.add_scoped_with_name("es", || SpanishGreeting).unwrap();

let printer = container.get::<Printer>().unwrap();
let en = container.get::<EnglishGreeting>().unwrap();
let es = container.get_with_name::<SpanishGreeting>("es").unwrap();

printer.print(&en.greet());
printer.print(&es.greet());

目录

容器

容器是提供的主要存储,它存储两种提供者类型

  • Scoped:每次请求时提供一个新的实例
  • Singleton:提供单个实例

所有这些提供者都可以命名或未命名,使用以 with_name 结尾的方法。

作用域提供者

作用域提供者在每次请求时都提供一个新的实例。

use dilib::Container;

let mut container = Container::new();
container.add_scoped(|| String::from("Apple Pie")).unwrap();

let s = container.get::<String>().unwrap();
assert_eq!(s.as_ref(), "Apple Pie");

单例提供者

单例提供者提供单个实例。

use dilib::Container;
use std::sync::Mutex;

let mut container = Container::new();
container.add_singleton(Mutex::new(0)).unwrap();

{
    let c1 = container.get::<Mutex<i32>>().unwrap();
    *c1.lock().unwrap() = 3;
}

let c2 = container.get::<Mutex<i32>>().unwrap();
assert_eq!(*c2.lock().unwrap(), 3);

注入特质

Inject 特质提供了一种使用容器中的提供者来构造类型的方法。

要将实现 Inject 的类型添加到容器中,您使用 add_deps 方法,这将类型作为 Scoped 提供者添加。

use std::sync::{Mutex, atomic::AtomicUsize};
use dilib::{Container, Inject};

struct IdGenerator(AtomicUsize);
impl IdGenerator {
  pub fn next(&self) -> usize {
    1 + self.0.fetch_add(1, std::sync::atomic::Ordering::SeqCst)
  }
}

#[derive(Clone, Debug)]
struct Fruit {
    id: usize,
    tag: String
}

impl Inject for Fruit {
    fn inject(container: &Container) -> Self {
      let generator = container.get::<IdGenerator>().unwrap();
      let id = generator.next();
      let tag = container.get_with_name::<String>("fruit").unwrap().cloned();
      Fruit { id, tag }
    }
}

let mut container = Container::new();
container.add_singleton(IdGenerator(AtomicUsize::new(0))).unwrap();
container.add_scoped_with_name("fruit", || String::from("b18ap31")).unwrap();
container.add_deps::<Fruit>().unwrap();

let f1 = container.get::<Fruit>().unwrap();
let f2 = container.get::<Fruit>().unwrap();

assert_eq!(f1.id, 1);
assert_eq!(f1.tag, "b18ap31");

assert_eq!(f2.id, 2);
assert_eq!(f2.tag, "b18ap31");

将特质绑定到实现

您可以通过宏将特质绑定到其实例,而不是直接将类型添加到容器中

  • add_scoped_trait!(container,name, trait => impl)
  • add_singleton_trait!(container,name, trait => impl)
  • add_scoped_trait!(container,name, trait @Inject)
  • add_singleton_trait!(container,name, trait @Inject)

名称是可选的。

您可以使用以下方法获取值

  • get_scoped_trait!(container,name, trait)
  • get_singleton_trait!(container,name, trait)
  • get_resolved_trait(container,name, trait)

名称也是可选的。

use dilib::{
  Container,
  add_scoped_trait, 
  add_singleton_trait, 
  get_resolved_trait, 
};

trait Discount {
  fn get_discount(&self) -> f32;
}

trait Fruit {
  fn name(&self) -> &str;
  fn price(&self) -> f32;
}

struct TenPercentDiscount;
impl Discount for TenPercentDiscount {
  fn get_discount(&self) -> f32 {
    0.1
  }
}

struct Apple;
struct Orange;

impl Fruit for Apple {
  fn name(&self) -> &str {
    "Apple"
  }
  
  fn price(&self) -> f32 {
    2.0
  }
}

impl Fruit for Orange {
  fn name(&self) -> &str {
    "Orange"
  }
  
  fn price(&self) -> f32 {
    1.7
  }
}

let mut container = Container::new();
add_singleton_trait!(container, Discount => TenPercentDiscount).unwrap();
add_scoped_trait!(container, "apple", Fruit => Apple).unwrap();
add_scoped_trait!(container, "orange", Fruit => Orange).unwrap();

// All types are returned as `Box<dyn Trait>`
let discount = get_resolved_trait!(container, Discount).unwrap();
let apple = get_resolved_trait!(container, Fruit, "apple").unwrap();
let orange = get_resolved_trait!(container, Fruit, "orange").unwrap();

assert_eq!(discount.get_discount(), 0.1);

assert_eq!(apple.name(), "Apple");
assert_eq!(apple.price(), 2.0);

assert_eq!(orange.name(), "Orange");
assert_eq!(orange.price(), 1.7);

get, get_scoped 和 get_singleton

从容器中检索值有三种方式

  • get
  • get_scoped
  • get_singleton

以及它们命名的变体

  • get_with_name
  • get_scoped_with_name
  • get_singleton_with_name

get_scopedget_singleton 的名称已经很直观,它们从 scopedsingleton 提供者中获取值。

get 可以获取任何 scopedsingleton 的值,区别在于 get 返回一个 Resolved<T>,而其他返回 TArc<T>(用于单例)。

Resolved<T> 只是一个代表 Scoped(T)Singleton(Arc<T>) 的枚举类型,你可以使用 into_scopedinto_singleton 将其转换回来。使用 get 的优势在于它实现了 Deref,这样就可以使用值,并且调用 get 更加简单。

派生 Inject

这需要 derive 功能。

Inject 被实现为所有实现了 Default 的类型,并且可以与 #[derive] 注解一起使用。

use dilib::{Singleton, Inject, Container};
use dilib_derive::*;

#[derive(Inject)]
struct Apple {
  // Singleton is an alias for Arc<T>
  #[inject(name="apple")]
  tag: Singleton<String>,
  #[inject(name="apple_price")]
  price: f32
}

let mut container = Container::new();
container.add_singleton_with_name("apple", String::from("FRUIT_APPLE")).unwrap();
container.add_scoped_with_name("apple_price", || 2.0_f32).unwrap();
container.add_deps::<Apple>();

let apple = container.get::<Apple>().unwrap();
assert_eq!(apple.tag.as_ref(), "FRUIT_APPLE");
assert_eq!(apple.price, 2.0);

全局容器

这需要 global 功能。

dilib 还提供了一个全局容器,因此你不需要声明自己的容器,并且可以通过宏如 get_scoped!get_singleton! 或仅仅是 get_resolved! 更容易地访问容器,你也可以直接使用 get_container() 来访问容器。

use dilib::{global::init_container, resolve};

init_container(|container| {
    container.add_scoped(|| String::from("Orange")).unwrap();
    container.add_singleton_with_name("num", 123_i32).unwrap();
}).expect("unable to initialize the container");

let orange = resolve!(String).unwrap();
let num = resolve!(i32, "num").unwrap();

assert_eq!(orange.as_ref(), "Orange");
assert_eq!(*num, 123);

提供

这需要 unstable_provide 功能。

为什么是 "unstable_provide"?

功能 unstable_provide 使得依赖注入更类似于其他框架,如 C# 的 EF Core 或 Java 的 Sprint

为了在主函数之前运行代码,我们使用了 ctor crate,它在多个操作系统上进行了测试,因此根据你运行应用程序的位置,此功能可能对你不是不稳定的。

provide 宏

你可以在任何实现了 **Inject** 的函数或类型上使用 #[provide] 宏来将其注册到全局容器。

** 实现了 `Default` 的任何类型也实现了 `Inject`。
use std::sync::RwLock;
use dilib::global::init_container;
use dilib::{resolve, Singleton, Inject, provide};

#[allow(dead_code)]
#[derive(Debug, Clone)]
struct User {
    name: &'static str,
    email: &'static str,
}

trait Repository<T> {
    fn add(&self, item: T);
    fn get_all(&self) -> Vec<T>;
}

#[derive(Default)]
#[provide(scope="singleton")]
struct Db(RwLock<Vec<User>>);

#[derive(Inject)]
#[provide(bind="Repository<User>")]
struct UserRepository(Singleton<Db>);
impl Repository<User> for UserRepository {
    fn add(&self, item: User) {
        self.0.0.write().unwrap().push(item);
    }

    fn get_all(&self) -> Vec<User> {
        self.0.0.read().unwrap().clone()
    }
}

// Initialize the container to register the providers
init_container(|_container| {
// Add additional providers
}).unwrap();

let user_repository = resolve!(trait Repository<User>).unwrap();
user_repository.add(User { name: "Marie", email: "[email protected]" });
user_repository.add(User { name: "Natasha", email: "[email protected]" });

let users = user_repository.get_all();
let db = resolve!(Db).unwrap();
println!("Total users: {}", db.0.read().unwrap().len());
println!("{:#?}", users);

依赖项

~0–320KB