Rudi
Rudi,一个开箱即用的依赖注入框架。
Rust 代码写多了之后,就想有个依赖注入框架,让代码写起来轻松一点。在社区中找了一些现有的依赖注入框架,但是觉得都不好用,看着就很麻烦,于是就自己写了一个。
我对依赖注入框架的手感,都是来自于 SpringBoot,加些注解,然后一行代码启动,就可以用了。所以我希望 Rudi 也能做到这样的体验,突出一个开箱即用,懒人福音。
刚开始不知道怎么解决自动扫描,自动注册的问题,就参考 Koin的 API,写了一套基于函数和模块,手动注册的 API。后来发现 inventory 这个库,可以解决自动注册的问题,于是又添加了属性宏,实现了自动注册。
一个简单的例子
use rudi::{Context, Singleton, Transient};
// 把 `fn(cx) -> A { A }` 注册为 `A` 的构造函数
#[derive(Debug)]
#[Transient]
struct A;
#[derive(Debug)]
struct B(A);
// 把 `fn(cx) -> B { B(cx.resolve::<A>()) }` 注册为 `B` 的构造函数
#[Transient]
impl B {
fn new(a: A) -> B {
B(a)
}
}
// 把 `fn(cx) -> () { run(cx.resolve::<B>()) }` 注册为 `()` 的构造函数
#[Singleton]
fn run(b: B) {
println!("{:?}", b);
}
fn main() {
// 把标记了 `#[Singleton]` 和 `#[Transient]` 的类型和函数,自动注册到 `Context` 中
let mut cx = Context::auto_register();
// 从 `Context` 中获取 `()` 的实例,这样会调用 `run` 函数
// 这个写法等同于 `cx.resolve::<()>();`
cx.resolve()
}
可以看到 run
函数,是一个接收很多参数,返回一个 ()
的函数,这个函数成为整个程序的入口,想要什么依赖,就写在这个函数的参数就行了,main
函数中,只需要调用 cx.resolve()
就可以了。
除了上面这个简单的例子,Rudi 还支持:
- 注册异步函数。(必须支持!!)
- 手动注册和自动注册。(自动注册依赖于 inventory,如果是 inventory 不支持的平台,比如 WebAssembly,就只能手动注册了)
- 轻松绑定 trait 对象。(其他很多依赖注入框架,在处理 trait 的时候,太拧巴了,在 Rudi 中,不关心 trait,你给我一个 trait 对象就好)
- 用类型和名称区分不同的实例。(有时候,我们需要多个实例,但是类型都是一样的,这时候,就可以用名称来区分了)
- 支持泛型。(但是只能指定类型后手动注册,这很好理解,一个不确定的类型是不可能注册到容器中的)
一个更复杂的例子
use std::{fmt::Debug, rc::Rc};
use rudi::{Context, Singleton, Transient};
// 注册一个异步函数,作为 `i32` 的构造函数,并指定此 `i32` 实例的名称
#[Singleton(name = "number")]
async fn number() -> i32 {
42
}
#[derive(Debug, Clone)]
#[Singleton(async_constructor, name = "foo")] // 注册一个异步构造函数,并指定此 `Foo` 实例的名称
struct Foo {
#[di("number")] // 指定此字段的名称,不仅是要一个 `i32` 类型,且名称必须为 `number` 的实例
number: i32,
}
#[derive(Debug)]
struct Bar(Foo);
impl Bar {
fn into_debug(self) -> Rc<dyn Debug> {
Rc::new(self)
}
}
#[Transient(binds = [Self::into_debug])] // 绑定一个 `Rc<dyn Debug>` 类型到 `Bar` 上,`Rc<dyn Debug>` 会和 `Bar` 一起注入
impl Bar {
async fn new(#[di("foo")] f: Foo) -> Bar { // 注册一个异步构造函数,且指定参数名称
Bar(f)
}
}
#[Singleton]
async fn run(bar: Bar, debug: Rc<dyn Debug>, #[di("foo")] f: Foo) {
println!("{:?}", bar);
assert_eq!(format!("{:?}", bar), format!("{:?}", debug));
assert_eq!(format!("{:?}", bar.0.number), format!("{:?}", f.number));
}
#[tokio::main]
async fn main() {
let mut cx = Context::auto_register();
cx.resolve_async().await
}
更多的信息可以在仓库上看到。
欢迎各位使用,有问题欢迎提 issue,如果觉得好用,可以点个 star。
1
共 9 条评论, 1 页
评论区
写评论谢谢
--
👇
jellybobbin: 赞一个,已star
--
👇
ZihanType: 还是指定名称。
--
👇
jellybobbin: 有多个 () 的函数呢
感觉没有必要
我感觉怎么变得更麻烦了,SpringBoot害人不浅啊[doge]
把actix-web的data拿出来用就好了
有很多example的。
--
👇
gorust21: 我觉得用处不大
赞一个,已star
--
👇
ZihanType: 还是指定名称。
--
👇
jellybobbin: 有多个 () 的函数呢
我觉得用处不大
还是指定名称。
--
👇
jellybobbin: 有多个 () 的函数呢
有多个 () 的函数呢