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 constructing_depth(&self) -> usize
pub fn constructing_depth(&self) -> usize
当前构造栈深度(用于调试,正常应为 0)
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)。
Sourcepub fn tag<T: 'static>(&self, tag: impl Into<String>)
pub fn tag<T: 'static>(&self, tag: impl Into<String>)
给类型 T 打标签(对齐 PHP app()->tag(['Service'], 'tag_name'))
PHP 用法:
$this->app->tag(['Logger', 'Mailer', 'Notifier'], 'reporters');Rust 端由于类型安全,每次调用只能给一个类型打标签。 多次调用同一标签名会追加到标签列表。
§用法
use sz_rust_core::container::Container;
let container = Container::new();
container.singleton(|| FileLogger::new());
container.singleton(|| MailLogger::new());
container.tag::<FileLogger>("reporters");
container.tag::<MailLogger>("reporters");
let reporters = container.tagged::<FileLogger>("reporters");
assert_eq!(reporters.len(), 1);Sourcepub fn tagged<T: Send + Sync + 'static>(&self, tag: &str) -> Vec<Arc<T>>
pub fn tagged<T: Send + Sync + 'static>(&self, tag: &str) -> Vec<Arc<T>>
获取标签下所有 T 类型实例(对齐 PHP app()->tagged('tag_name'))
遍历标签下所有 TypeId,对每个匹配 T 的 TypeId 调用 make::<T>()。
§返回
标签下所有类型为 T 的服务实例向量。若标签不存在或无匹配类型,返回空向量。
Sourcepub fn tagged_type_ids(&self, tag: &str) -> Vec<TypeId>
pub fn tagged_type_ids(&self, tag: &str) -> Vec<TypeId>
获取标签下所有 TypeId(用于调试)
返回标签下所有已注册的 TypeId 列表。若标签不存在,返回空向量。
Sourcepub fn forget_tag(&self, tag: &str)
pub fn forget_tag(&self, tag: &str)
移除指定标签(对齐 PHP app()->forgetTag('tag_name'))
Sourcepub fn bind_contextual<Consumer: 'static, T: Send + Sync + 'static, F>(
&self,
factory: F,
)
pub fn bind_contextual<Consumer: 'static, T: Send + Sync + 'static, F>( &self, factory: F, )
注册上下文绑定(对齐 PHP app()->when(Consumer)->needs(Need)->give(impl))
PHP 用法:
$this->app->when(PhotoController::class)
->needs(Filesystem::class)
->give(function () { return new S3Filesystem(); });Rust 端通过泛型参数指定消费者类型 Consumer、需求类型 T,
并提供工厂闭包创建 T 实例。
§用法
use sz_rust_core::container::Container;
let container = Container::new();
// 为 PhotoController 注入 S3Filesystem 作为 Filesystem
container.bind_contextual::<PhotoController, Filesystem, _>(|| {
S3Filesystem::new()
});
// 解析:为 PhotoController 创建 Filesystem 实例
let fs = container.make_for::<Filesystem, PhotoController>();§注意
上下文绑定不会缓存实例(每次 make_for 调用工厂)。
Sourcepub fn make_for<T: Send + Sync + 'static, Consumer: 'static>(
&self,
) -> Option<Arc<T>>
pub fn make_for<T: Send + Sync + 'static, Consumer: 'static>( &self, ) -> Option<Arc<T>>
为指定消费者解析上下文绑定的服务(对齐 PHP 上下文感知 make)
查找 (Consumer, T) 的上下文绑定,若存在则调用工厂返回实例。
若不存在上下文绑定,回退到普通 make::<T>()。
§返回
Some(Arc<T>):找到上下文绑定或普通绑定None:既无上下文绑定也无普通绑定
Sourcepub fn has_contextual<Consumer: 'static, T: 'static>(&self) -> bool
pub fn has_contextual<Consumer: 'static, T: 'static>(&self) -> bool
检查指定上下文绑定是否存在
Sourcepub fn contextual_count(&self) -> usize
pub fn contextual_count(&self) -> usize
获取上下文绑定数量
Sourcepub fn forget_contextual<Consumer: 'static, T: 'static>(&self)
pub fn forget_contextual<Consumer: 'static, T: 'static>(&self)
移除指定上下文绑定
Sourcepub fn call_method<R, P, F, C>(&self, resolver: C, callback: F) -> R
pub fn call_method<R, P, F, C>(&self, resolver: C, callback: F) -> R
调用闭包并自动注入参数 — 对齐 PHP app()->call($callback, $parameters)
PHP 的 app()->call() 通过反射自动解析方法参数类型并从容器获取实例。
Rust 是静态类型语言,无法运行时反射,因此通过 resolver 闭包手工指定
如何从 Container 解析参数。
§参数
resolver: 参数解析器,接收&Container引用,返回参数元组callback: 业务回调,接收解析后的参数,返回业务结果
§返回
业务回调的返回值
§用法
use sz_rust_core::container::Container;
struct UserService;
struct Logger;
let container = Container::new();
container.singleton(Logger::new);
container.singleton(UserService::new);
// 自动注入 Logger 和 UserService
let result: String = container.call_method(
|c| (c.make::<Logger>().unwrap(), c.make::<UserService>().unwrap()),
|(logger, service)| {
format!("called with logger and service")
},
);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.