依赖注入

spring-rs提供了一个简单的编译期依赖注入的实现

编译期依赖注入

spring-rs提供了一种特殊的Component——Service,它支持在编译期注入依赖的组件。

像下面的例子UserService只需派生Service特征,为了区分注入的依赖,你需要通过属性宏#[inject(component)]#[inject(config)]指定依赖是一个Component还是一个Config。

use spring_sqlx::ConnectPool;
use spring::config::Configurable;
use serde::Deserialize;

#[derive(Clone, Configurable, Deserialize)]
#[config_prefix = "user"]
struct UserConfig {
    username: String,
    project: String,
}

#[derive(Clone, Service)]
struct UserService {
    #[inject(component)]
    db: ConnectPool,
    #[inject(config)]
    config: UserConfig,
}

#[derive(Clone, Service)]
struct UserWithOptionalComponentService {
    #[inject(component)]
    db: Option<ConnectPool>, // 如果ConnectPool不存在,则输入None
    #[inject(config)]
    config: UserConfig,
}

完整代码参考dependency-inject-example

Service还支持grpc模式,可结合spring-grpc插件一起使用

嵌套依赖注入(Nested dependency inject)

spring-rs 支持多层级的依赖注入。 例如,如果 UserService 依赖于 OtherService,而 OtherService 又依赖于 DatabaseService, 那么当你注入 UserService 时,OtherServiceDatabaseService 也会被自动注入。

use spring::plugin::LazyComponent;
use spring::plugin::service::Service;

#[derive(Clone, Service)]
struct DatabaseService {
    // ...
}
#[derive(Clone, Service)]
struct OtherService {
    #[inject(component)]
    db: DatabaseService,
}
#[derive(Clone, Service)]
struct UserService {
    #[inject(component)]
    other_service: OtherService,
}

完整代码请参见 nested-dependency-inject-example


循环依赖注入(Circular dependency inject)

当两个服务互相引用时,Rust 的类型系统会阻止直接的循环依赖。 为了解决这个问题,你可以使用 LazyComponent<T> 来打破循环依赖。

use spring::plugin::LazyComponent;
use spring::plugin::service::Service;

use spring::plugin::LazyComponent;
#[derive(Clone, Service)]
struct UserService {
    #[inject(component)] // 在此情况下可选
    other_service: LazyComponent<OtherService>,  // ✅ 延迟解析
}

#[derive(Clone, Service)]
struct OtherService {
    #[inject(component)]
    user_service: UserService,  // ✅ 可直接注入
    // 如果需要,也可以使用延迟注入
    // user_service: LazyComponent<UserService>,
}

这样,UserService 就可以持有一个指向 OtherService 的轻量级引用, 只有在真正需要时才会进行解析。 若要访问实际的组件,请调用 .get() 方法。

如果需要,循环依赖的两方都可以设置为延迟注入,但只需一方使用延迟注入即可。 建议将访问频率较低的服务设为延迟注入。

使用 LazyComponent<T> 时不必显式添加 #[inject] 属性,框架会自动检测。 在内部,它只是对 Arc<RwLock<...>> 的封装,因此是线程安全的。

完整代码请参见 circular-dependency-injection-example