2 个版本
0.1.1 | 2023 年 1 月 26 日 |
---|---|
0.1.0 | 2023 年 1 月 25 日 |
#26 in #black-box
在 2 个crate中使用(通过blackbox_core)
15KB
139 行代码(不含注释)
BlackBox DI
Rust 的依赖注入
安装
在项目目录下运行以下 Cargo 命令
cargo add blackbox_di
或添加以下行到你的 Cargo.toml 文件中
blackbox_di = "0.1"
使用方法
提供者创建
使用 #[interface]
注解接口
#[interface]
trait IService {
fn call(&self);
}
使用 #[injectable]
注解服务结构
#[injectable]
struct Service {}
使用 #[implements]
注解 impl 块
#[implements]
impl IService for Service {
fn call() {
println!("Service calling");
}
}
模块创建
使用 #[module]
注解模块结构并指定 Service
结构作为 provider
#[module]
struct RootModule {
#[provider]
service: Service
}
容器创建
#[launch]
async fn launch() {
let app = build::<RootModule>(BuildParams::default()).await;
let service = app
.get_by_token::<Service>(&get_token::<Service>)
.unwrap();
// short (equivalent to above)
let service = app.get::<Service>().unwrap();
service.call();
}
注入引用
按类型注入
为可注入依赖指定 #[inject]
#[injectable]
struct Repo {}
#[injectable]
struct Service {
#[inject]
repo: Ref<Repo>
}
别忘了在 RootModule
模块中指定 Repo
#[module]
struct RootModule {
#[provider]
repo: Repo
#[provider]
service: Service
}
按令牌注入
您可以用令牌代替类型
#[injectable]
struct Repo {}
#[injectable]
struct Service {
#[inject("REPO_TOKEN")]
repo: Ref<Repo>
}
然后
#[module]
struct RootModule {
#[provider("REPO_TOKEN")]
repo: Repo
#[provider]
service: Service
}
或者使用常量作为令牌
const REPO_TOKEN: &str = "REPO_TOKEN";
#[injectable]
struct Repo {}
#[injectable]
struct Service {
#[inject(REPO_TOKEN)]
repo: Ref<Repo>
}
#[module]
struct RootModule {
#[provider(REPO_TOKEN)]
repo: Repo
#[provider]
service: Service
}
使用接口
您还可以使用接口进行可注入依赖项的注入
const REPO_TOKEN: &str = "REPO_TOKEN";
#[interface]
trait IRepo {}
#[injectable]
struct Repo {}
#[implements]
impl IRepo for Repo {}
#[injectable]
struct Service {
#[inject(REPO_TOKEN)]
repo: Ref<dyn IRepo>
}
#[module]
struct RootModule {
#[provider(REPO_TOKEN)]
repo: Repo
#[provider]
service: Service
}
或者直接使用现有接口的实现
#[interface]
trait IRepo {}
#[injectable]
struct Repo {}
#[implements]
impl IRepo for Repo {}
#[injectable]
struct Service {
#[inject(use Repo)]
repo: Ref<dyn IRepo>
}
#[module]
struct RootModule {
#[provider]
repo: Repo
#[provider]
service: Service
}
工厂
如果一个服务有非注入依赖项
#[injectable]
struct Service {
#[inject]
repo: Ref<Repo>
greeting: String
}
您应该指定一个工厂函数
#[implements]
impl Service {
#[factory]
fn new(repo: Ref<Repo>) -> Service {
Service {
repo,
greeting: String::from("Hello")
}
}
}
或者对于接口
#[injectable]
struct Service {
#[inject(use Repo)]
repo: Ref<dyn IRepo>
greeting: String
}
#[implements]
impl Service {
#[factory]
fn new(repo: Ref<dyn IRepo>) -> Service {
Service {
repo,
greeting: String::from("Hello")
}
}
}
带有 non-injectable
依赖的可注入服务必须具有 factory
函数。
为了有可变非注入依赖项,您需要使用 RefMut<...>
#[injectable]
struct Service {
#[inject(use Repo)]
repo: Ref<dyn IRepo>
greeting: RefMut<String>
}
#[implements]
impl Service {
#[factory]
fn new(repo: Ref<dyn Repo>) -> Service {
Service {
repo,
greeting: RefMut::new(String::from("Hello"))
}
}
fn set_greeting(&self, msg: String) {
*self.greeting.as_mut() = msg;
}
fn print_greeting(&self) {
println!("{}", self.greeting.as_ref());
}
}
模块
您可以指定多个模块并将它们导入
#[module]
struct UserModule {
#[provider]
user_service: UserService,
}
#[module]
struct RootModule {
#[import]
user_module: UserModule
}
要使用导入模块中的提供者,您应该将这些提供者指定为 exported
#[module]
struct UserModule {
#[provider]
#[export]
user_service: UserService,
}
此外,您还可以将您的模块指定为 global
,这样您就不必直接导入它们。只需在 root
模块中指定它们即可
#[module]
#[global]
struct UserModule {
#[provider]
#[export]
user_service: UserService,
}
#[module]
struct AccountModule {
#[provider]
#[export]
account_service: AccountService,
}
#[module]
struct RootModule {
#[import]
user_module: UserModule
#[import]
account_module: AccountModule
}
依赖循环
在模块导入时使用 Lazy
来解决依赖循环
#[module]
struct UserModule {
#[import]
account_module: Lazy<AccountService>,
#[provider]
#[export]
user_service: UserService,
}
#[module]
struct AccountModule {
#[import]
user_module: Lazy<UserModule>,
#[provider]
#[export]
account_service: AccountService,
}
#[module]
struct RootModule {
#[import]
user_module: UserModule
#[import]
account_module: AccountModule
}
生命周期事件
当容器完全初始化后,系统触发事件 on_module_init
#[implements]
impl OnModuleInit for Service {
async fn on_module_init(&self) {
...
}
}
和 on_module_destroy
#[implements]
impl OnModuleDestroy for Service {
async fn on_module_destroy(&self) {
...
}
}
日志记录器
日志记录器用于显示关于应用程序构建的信息。要使用自定义日志记录器,实现 ILogger
特性
pub trait ILogger {
fn log<'a>(&self, level: LogLevel, msg: &'a str);
fn log_with_ctx<'a>(&self, level: LogLevel, msg: &'a str, ctx: &'a str);
fn set_context<'a>(&self, ctx: &'a str);
fn get_context(&self) -> String;
}
然后更改构建参数
let app = build::<RootModule>(
BuildParams::default().buffer_logs()
).await;
let custom_logger = app.get::<CustomLogger>().unwrap();
app.use_logger(custom_logger.cast::<dyn ILogger>().unwrap());
许可证
BlackBox DI 的许可证为
- MIT 许可证(LICENSE-MIT 或 https://opensource.org/licenses/MIT)
依赖项
~2MB
~45K SLoC