pub struct Container { /* private fields */ }Expand description
DI 服务容器 — 服务注册/解析/生命周期管理
对齐 PHP app()->bind()/make()/singleton()/instance()/scoped()/alias()。
使用 TypeId 作为 key 实现类型安全的服务解析,避免 PHP 字符串 key
的类型不匹配风险。
§线程安全
bindings、instances、scoped_instances、aliases均使用RwLock保护- 单例实例以
Arc返回,可跨线程共享
Implementations§
Source§impl Container
impl Container
Sourcepub fn bind<T, F>(&self, factory: F)
pub fn bind<T, F>(&self, factory: F)
注册瞬态服务(每次 make 创建新实例)
对齐 PHP app()->bind('key', fn() => new Service())。
§类型约束
T: Send + Sync + 'static:服务实例必须线程安全F: Fn() -> T + Send + Sync + 'static:工厂必须线程安全
Sourcepub fn singleton<T, F>(&self, factory: F)
pub fn singleton<T, F>(&self, factory: F)
注册单例服务(整个应用生命周期内只创建一次)
对齐 PHP app()->singleton('key', fn() => new Service())。
首次 make 时调用工厂创建实例并缓存,后续 make 返回缓存的同一实例。
Sourcepub fn scoped<T, F>(&self, factory: F)
pub fn scoped<T, F>(&self, factory: F)
注册请求作用域服务(同一 ScopeId 内单例)
对齐 PHP app()->scoped('key', fn() => new Service())。
与 singleton 不同:scoped 服务在 Container::make_with_scope 调用时,
同一 scope_id 内首次调用工厂创建并缓存,后续返回缓存;
不同 scope_id 各自创建独立实例;请求结束后调用
Container::clear_scope 清理对应作用域的缓存。
§用法
use sz_rust_core::container::{Container, ScopeId};
let container = Container::new();
container.scoped(|| RequestCache::new());
// 请求 A(scope_id=1)
let cache_a1 = container.make_with_scope::<RequestCache>(1).unwrap();
let cache_a2 = container.make_with_scope::<RequestCache>(1).unwrap();
assert!(Arc::ptr_eq(&cache_a1, &cache_a2)); // 同一作用域:同一实例
// 请求 B(scope_id=2)
let cache_b = container.make_with_scope::<RequestCache>(2).unwrap();
assert!(!Arc::ptr_eq(&cache_a1, &cache_b)); // 不同作用域:不同实例
// 请求 A 结束
container.clear_scope(1);Sourcepub fn instance<T>(&self, instance: T)
pub fn instance<T>(&self, instance: T)
直接绑定已创建的实例(绕过工厂)
对齐 PHP app()->instance('key', $obj)。
将一个已创建的实例直接注册为单例,后续 make 返回此实例。
适用于:
- 实例已在其他地方创建(如配置加载时初始化的服务)
- 实例创建过程复杂、不适合用闭包表达
- 测试中注入 mock 实例
§用法
use sz_rust_core::container::Container;
let container = Container::new();
let logger = Arc::new(FileLogger::new("/var/log/app.log"));
container.instance(logger.clone());
let resolved = container.make::<FileLogger>().unwrap();
assert!(Arc::ptr_eq(&logger, &resolved));Sourcepub fn alias<T: 'static>(&self, name: impl Into<String>)
pub fn alias<T: 'static>(&self, name: impl Into<String>)
为服务类型注册字符串别名
对齐 PHP app()->alias('name', Service::class)。
别名仅用于:
- 调试输出(
Container::debug_aliases列出所有别名) - 反向查找(
Container::resolve_alias通过别名获取 TypeId)
解析时仍用类型安全的 make::<T>(),不支持通过字符串别名解析
(Rust 类型系统要求编译时已知类型,字符串 key 解析会引入不安全的 downcast)。
§用法
use sz_rust_core::container::Container;
let container = Container::new();
container.singleton(|| MyService::new());
container.alias::<MyService>("my_service");
assert!(container.is_alias("my_service"));
let type_id = container.resolve_alias("my_service").unwrap();
assert_eq!(type_id, std::any::TypeId::of::<MyService>());Sourcepub fn resolve_alias(&self, name: &str) -> Option<TypeId>
pub fn resolve_alias(&self, name: &str) -> Option<TypeId>
通过别名查找对应的 TypeId
返回 None 表示别名未注册。
Sourcepub fn debug_aliases(&self) -> Vec<String>
pub fn debug_aliases(&self) -> Vec<String>
列出所有已注册别名(用于调试)
Sourcepub fn make<T: Send + Sync + 'static>(&self) -> Option<Arc<T>>
pub fn make<T: Send + Sync + 'static>(&self) -> Option<Arc<T>>
解析服务实例(无作用域)
对齐 PHP app()->make('key')。
等价于 Container::make_with_scope 传入 scope_id = 0。
对于 Scoped 生命周期服务,会使用 scope_id = 0 作为默认作用域。
§返回
Some(Arc<T>):服务已注册,返回实例(单例返回缓存实例,瞬态返回新实例)None:服务未注册
§Panics
理论上不会 panic(工厂返回的 Box<dyn Any> 内部类型由编译时泛型保证)。
若发生 panic 说明内部状态被破坏(bindings 与 instances 不一致)。
Sourcepub fn make_with_scope<T: Send + Sync + 'static>(
&self,
scope_id: ScopeId,
) -> Option<Arc<T>>
pub fn make_with_scope<T: Send + Sync + 'static>( &self, scope_id: ScopeId, ) -> Option<Arc<T>>
Sourcepub fn clear_scope(&self, scope_id: ScopeId)
pub fn clear_scope(&self, scope_id: ScopeId)
清理指定作用域的所有缓存实例
应在请求结束时调用(如 axum 中间件在请求处理完毕后调用), 释放该作用域内创建的所有 Scoped 服务实例。
§用法
use sz_rust_core::container::Container;
let container = Container::new();
container.scoped(|| RequestCache::new());
let scope_id = generate_scope_id(); // 如从 axum State 获取
let _cache = container.make_with_scope::<RequestCache>(scope_id);
// 请求结束
container.clear_scope(scope_id);Sourcepub fn alias_count(&self) -> usize
pub fn alias_count(&self) -> usize
已注册别名数量
Sourcepub fn active_scope_count(&self) -> usize
pub fn active_scope_count(&self) -> usize
当前活跃作用域数量
可用于检测作用域泄漏(如请求结束未调用 clear_scope)。
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for Container
impl !RefUnwindSafe for Container
impl !UnwindSafe for Container
impl Send for Container
impl Sync for Container
impl Unpin for Container
impl UnsafeUnpin for Container
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<R, P> ReadPrimitive<R> for P
impl<R, P> ReadPrimitive<R> for P
Source§fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
ReadEndian::read_from_little_endian().Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.