tx-di-core
类型驱动的 Rust 依赖注入(DI)框架核心 crate。
#[derive(Component)]自动注册组件(tx-di-macros提供)linkme编译期收集组件元数据,运行期拓扑排序注入- 类型安全的
Store::inject::<T>()解析依赖 - 完整的生命周期:
build → inner_init → init → async_init → async_run → shutdown - 内置 AOP 拦截器、配置管理、作用域(Singleton/Prototype)
当前版本:0.3.0
安装
[]
= "0.3.0"
工作区内开发通常使用 path 依赖:
[]
= { = "../tx-di-core" }
重要:
tx-di-core依赖linkme做编译期静态注册。若要把某个插件/组件 crate 真正链接进二进制, 必须在代码中use该 crate(仅写进Cargo.toml会被链接器优化掉)。详见下方「插件」与tx-di-macros说明。
核心概念
| 概念 | 说明 |
|---|---|
Component trait |
每个被 DI 管理的类型实现此 trait,用 associated type Deps 声明依赖 |
ComponentMeta |
瘦注册条目,由 #[derive(Component)] 生成,经 linkme 编译期收集 |
Store |
类型擦除的组件存储(DashMap<TypeId, CompRef>),运行期解析依赖 |
BuildContext |
构建阶段:加载配置 → 拓扑排序 → 构建组件 → 缓存单例 |
App |
运行阶段:按序执行 init → async_init → async_run → shutdown |
Scope |
Singleton(默认,全局唯一)或 Prototype(每次注入新建) |
| AOP | Interceptor trait + #[intercept] 方法宏,零额外运行时开销 |
快速上手
use Arc;
use ;
// 无依赖单例(用 Default 自动构造)
;
// 依赖其他组件:字段 Arc<T> 自动从容器注入
async
组件之间的依赖由框架在构建期自动注入,业务代码通常不需要手动 inject。 真正「使用」组件(调用其业务方法)应放在某个组件的
async_run/init回调里, 或在ins_run()返回后通过app.inject::<T>()获取——此时组件已完全初始化。
构建与运行的三种常用形态:
// 形式 A:构建出 App 句柄,自行控制运行(ins_run 返回即代表 init+async_init 完成)
let app = ctx.build?.ins_run.await?;
app.waiting_exit.await;
// 形式 B:一行启动并阻塞到退出(内部等价于 A)
ctx.build_and_run.await?;
// 形式 C:仅构建 App,不运行任何生命周期(测试 / 仅查看组件实例,不可当成品使用)
let app = ctx.build?;
// 注意:此时 init / async_init 未执行,app.inject::<T>() 拿到的实例仍不完整
Component trait
绝大多数情况下直接用 #[derive(Component)] 自动生成实现;需要极致控制时可手写 impl Component。
RIE<T> 是 tx_error::AppResult<T> 的类型别名。错误类型统一为 tx_error::AppError,DI 框架自带 DiErr(RegistryError / AsyncInitError / TaskPanic / InjectError)错误码。
构建期与运行期
BuildContext(构建与解析)
| 方法 | 说明 |
|---|---|
BuildContext::new::<P>(Option<config_path>) |
加载配置 + 自动注册所有组件 |
ctx.inject::<T>() |
注入组件,返回 Arc<T>(未注册则 panic,附已注册列表)。构建期仅完成 inner_init,init/async_init 尚未执行,实例不完整,不可当成品使用 |
ctx.try_inject::<T>() |
同上,失败时返回 None |
ctx.store() |
获取底层 Store 引用 |
ctx.len() / ctx.is_empty() |
已注册组件数量 |
ctx.build() -> RIE<App> |
把 store 移交给 App |
ctx.build_and_run().await |
构建并运行到退出 |
BuildContext::debug_registry() |
打印拓扑排序后的组件清单(调试用) |
App(运行期)
| 方法 | 说明 |
|---|---|
app.inject::<T>() / app.try_inject::<T>() |
注入组件 |
app.ins_run(self).await -> RIE<Arc<App>> |
先完成 init+async_init 再返回可用句柄 |
app.shutdown().await |
逆序优雅关闭所有组件 |
app.waiting_exit().await |
等待退出信号 → 取消 token → 关闭后台任务 → shutdown |
生命周期执行顺序(由拓扑序 + init_sort 共同决定):
init(同步)→ async_init(异步)→ async_run(后台并行任务,直到 CancellationToken 触发)→ shutdown(逆序)。
依赖注入规则
字段类型决定注入行为(由 #[derive(Component)] 推导,详见 tx-di-macros):
| 字段类型 | 行为 |
|---|---|
Arc<T> |
普通组件注入,从容器取 T |
Arc<dyn Trait> |
必选 trait 注入,inner_init 中填充 |
Option<Arc<dyn Trait>> |
可选 trait 注入,找不到保持 None |
Option<T> |
可选普通依赖,保持 None |
#[tx_cst(expr)] |
用表达式赋值,不从容器注入 |
#[tx_cst(skip)] |
跳过注入,使用 Default::default() |
配置组件
通过 #[component(conf = "key")] 让组件从 TOML 配置反序列化。框架在 BuildContext::new 阶段加载配置(默认读取可执行文件同目录 config/config.toml,或显式传入路径),并通过 AppAllConfig 存放于 Store。
use Deserialize;
全局配置也可直接注入后读取:
let cfg = ctx.;
if let Some = cfg.
AOP 拦截器
拦截器本身也是 #[derive(Component)],可依赖其他服务,通过 #[component(intercept(T1, T2))] 声明,
并对目标方法加 #[intercept] 属性宏。详见 tx-di-macros 与 aop 模块文档。
use ;
;
框架内置 LoggingInterceptor、MetricsInterceptor。拦截器链按「组件实例指针」存储,支持同进程多 App 互不干扰。
模块结构
tx-di-core/src/
├── lib.rs # crate 根,统一 re-export
├── component.rs # Component trait + DepsTuple
├── lifecycle.rs # BuildContext / App / 生命周期编排
├── store.rs # Store / CompRef / trait object 注入
├── registry.rs # ComponentMeta + linkme 收集
├── topology.rs # 拓扑排序(Kahn,支持 init_sort 优先级)
├── scope.rs # Scope 枚举
├── config.rs # AppAllConfig 配置管理
├── aop.rs # Interceptor / InterceptorChain / 内置拦截器
├── error.rs # DiErr 错误码(复用 tx_error::AppError)
主要 re-export
// 宏与 trait
pub use ;
// 核心类型
pub use ;
pub use ;
pub use ;
pub use Scope;
pub use ;
pub use topo_sort;
pub use ;
// 错误与通用
pub use ;
pub use ;
pub type RIE<T> = ;
pub use CancellationToken;
约束
| 约束 | 原因 |
|---|---|
组件需 T: Send + Sync + 'static |
存入 Arc<dyn Any + Send + Sync> |
配置组件需 Deserialize + Default |
serde 反序列化 |
trait 注入需 Trait: Any + Send + Sync |
需 TypeId::of::<dyn Trait>() |
| 避免循环依赖 | 拓扑排序检测,启动时返回 RegistryError |
| 最多 16 个依赖 | DepsTuple 元组实现上限(宏元数限制) |
许可证
MIT