Skip to main content

Container

Struct Container 

Source
pub struct Container { /* private fields */ }
Expand description

DI 服务容器 — 服务注册/解析/生命周期管理

对齐 PHP app()->bind()/make()/singleton()/instance()/scoped()/alias()。 使用 TypeId 作为 key 实现类型安全的服务解析,避免 PHP 字符串 key 的类型不匹配风险。

§线程安全

  • bindingsinstancesscoped_instancesaliases 均使用 RwLock 保护
  • 单例实例以 Arc 返回,可跨线程共享

Implementations§

Source§

impl Container

Source

pub fn new() -> Self

创建空的服务容器

Source

pub fn bind<T, F>(&self, factory: F)
where T: Send + Sync + 'static, F: Fn() -> T + Send + Sync + 'static,

注册瞬态服务(每次 make 创建新实例)

对齐 PHP app()->bind('key', fn() => new Service())

§类型约束
  • T: Send + Sync + 'static:服务实例必须线程安全
  • F: Fn() -> T + Send + Sync + 'static:工厂必须线程安全
Source

pub fn singleton<T, F>(&self, factory: F)
where T: Send + Sync + 'static, F: Fn() -> T + Send + Sync + 'static,

注册单例服务(整个应用生命周期内只创建一次)

对齐 PHP app()->singleton('key', fn() => new Service())

首次 make 时调用工厂创建实例并缓存,后续 make 返回缓存的同一实例。

Source

pub fn scoped<T, F>(&self, factory: F)
where T: Send + Sync + 'static, F: Fn() -> T + Send + Sync + 'static,

注册请求作用域服务(同一 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);
Source

pub fn instance<T>(&self, instance: T)
where T: Send + Sync + 'static,

直接绑定已创建的实例(绕过工厂)

对齐 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));
Source

pub fn alias<T: 'static>(&self, name: impl Into<String>)

为服务类型注册字符串别名

对齐 PHP app()->alias('name', Service::class)

别名仅用于:

解析时仍用类型安全的 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>());
Source

pub fn resolve_alias(&self, name: &str) -> Option<TypeId>

通过别名查找对应的 TypeId

返回 None 表示别名未注册。

Source

pub fn is_alias(&self, name: &str) -> bool

检查指定别名是否已注册

Source

pub fn debug_aliases(&self) -> Vec<String>

列出所有已注册别名(用于调试)

Source

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 不一致)。

Source

pub fn make_with_scope<T: Send + Sync + 'static>( &self, scope_id: ScopeId, ) -> Option<Arc<T>>

解析服务实例(带作用域 ID)

对齐 PHP app()->make('key') + 请求作用域支持。

§生命周期处理
  • Singleton:忽略 scope_id,返回全局缓存的单例
  • Transient:忽略 scope_id,每次调用工厂创建新实例
  • Scoped:同一 scope_id 内首次调用工厂创建并缓存,后续返回缓存
§返回
  • Some(Arc<T>):服务已注册,返回实例
  • None:服务未注册
§Panics

理论上不会 panic(工厂返回的 Box<dyn Any> 内部类型由编译时泛型保证)。 若发生 panic 说明内部状态被破坏。

Source

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);
Source

pub fn has<T: 'static>(&self) -> bool

检查服务是否已注册

Source

pub fn forget<T: 'static>(&self)

移除指定类型的服务绑定(含单例缓存与所有作用域缓存)

对齐 PHP app()->remove('key')

Source

pub fn clear(&self)

清空所有服务绑定与缓存(含单例、作用域、别名、标签、上下文绑定)

Source

pub fn constructing_depth(&self) -> usize

当前构造栈深度(用于调试,正常应为 0)

Source

pub fn count(&self) -> usize

已注册服务数量(不含别名)

Source

pub fn alias_count(&self) -> usize

已注册别名数量

Source

pub fn active_scope_count(&self) -> usize

当前活跃作用域数量

可用于检测作用域泄漏(如请求结束未调用 clear_scope)。

Source

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);
Source

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 的服务实例向量。若标签不存在或无匹配类型,返回空向量。

Source

pub fn tagged_type_ids(&self, tag: &str) -> Vec<TypeId>

获取标签下所有 TypeId(用于调试)

返回标签下所有已注册的 TypeId 列表。若标签不存在,返回空向量。

Source

pub fn tag_names(&self) -> Vec<String>

获取已注册标签列表

Source

pub fn tag_count(&self, tag: &str) -> usize

获取标签下已注册的类型数量

Source

pub fn forget_tag(&self, tag: &str)

移除指定标签(对齐 PHP app()->forgetTag('tag_name')

Source

pub fn bind_contextual<Consumer: 'static, T: Send + Sync + 'static, F>( &self, factory: F, )
where F: Fn() -> T + Send + Sync + 'static,

注册上下文绑定(对齐 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 调用工厂)。

Source

pub fn make_for<T: Send + Sync + 'static, Consumer: 'static>( &self, ) -> Option<Arc<T>>

为指定消费者解析上下文绑定的服务(对齐 PHP 上下文感知 make

查找 (Consumer, T) 的上下文绑定,若存在则调用工厂返回实例。 若不存在上下文绑定,回退到普通 make::<T>()

§返回
  • Some(Arc<T>):找到上下文绑定或普通绑定
  • None:既无上下文绑定也无普通绑定
Source

pub fn has_contextual<Consumer: 'static, T: 'static>(&self) -> bool

检查指定上下文绑定是否存在

Source

pub fn contextual_count(&self) -> usize

获取上下文绑定数量

Source

pub fn forget_contextual<Consumer: 'static, T: 'static>(&self)

移除指定上下文绑定

Source

pub fn call_method<R, P, F, C>(&self, resolver: C, callback: F) -> R
where F: FnOnce(P) -> R, C: FnOnce(&Self) -> P,

调用闭包并自动注入参数 — 对齐 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")
    },
);
Source

pub fn invoke<R, F>(&self, callback: F) -> R
where F: FnOnce(&Self) -> R,

调用闭包并传入容器引用 — 对齐 PHP app()->invoke($callback)

最灵活的方法调用方式,调用方可以在闭包内自由调用 make() 解析依赖。

§用法
let result: String = container.invoke(|c| {
    let logger = c.make::<Logger>().unwrap();
    let service = c.make::<UserService>().unwrap();
    format!("called with {:?} and {:?}", logger, service)
});
Source

pub fn make_or_panic<T: Send + Sync + 'static>(&self) -> Arc<T>

解析服务,失败时 panic — 用于自动注入场景

对齐 PHP app()->make() 在服务未注册时抛出异常的行为。 Rust 端通过 panic 模拟,调用方应在确保服务已注册时使用。

§Panics

当服务未注册时 panic。

Trait Implementations§

Source§

impl Debug for Container

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Container

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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 more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more