9 个版本
0.2.2-alpha | 2022年4月16日 |
---|---|
0.2.1 | 2022年12月19日 |
0.2.1-alpha | 2022年4月15日 |
0.1.3-alpha | 2022年4月3日 |
#380 in Rust 模式
在 3 crates 中使用
77KB
1.5K SLoC
dilib-rs
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_scoped
和 get_singleton
的名称已经很直观,它们从 scoped
或 singleton
提供者中获取值。
但 get
可以获取任何 scoped
和 singleton
的值,区别在于 get
返回一个 Resolved<T>
,而其他返回 T
或 Arc<T>
(用于单例)。
Resolved<T>
只是一个代表 Scoped(T)
和 Singleton(Arc<T>)
的枚举类型,你可以使用 into_scoped
或 into_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]
宏来将其注册到全局容器。
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