## `make_di_all` 依赖注入代码生成原理详解
### 一、整体架构概览
整个 `rudi` 模块是一个**编译期无侵入、启动期自动注册**的依赖注入框架。它的核心思想是:**扫描 Rust 源码 → 找到标记了特定 Trait 的 Struct → 自动生成 `_init.rs` 注册文件**。
```
┌─────────────────────────────────────────────────────────────────┐
│ make_di_all("./src") │
├─────────────────────────────────────────────────────────────────┤
│ 1. fill_template() 读取模板文件 │
│ 2. parse_all_files() AST 解析扫描所有 .rs 文件 │
│ 3. build_init() 模板变量替换生成代码 │
│ 4. create_init() 写入 _init.rs 文件 │
└─────────────────────────────────────────────────────────────────┘
```
---
### 二、核心调用链
[make_di_all](file:///E:/soft/gitee.com/ruwebframe/src/rudi/difactroy/difactroy.rs#L148-L150) 是入口方法,它直接委托给 [make_di_if_force](file:///E:/soft/gitee.com/ruwebframe/src/rudi/difactroy/difactroy.rs#L114-L135):
```rust
pub fn make_di_all(&self, dir: &str) {
self.make_di_if_force(dir, false); // if_force=false,已存在的跳过
}
```
---
### 三、Step 1:模板加载 (`fill_template`)
[fill_template](file:///E:/soft/gitee.com/ruwebframe/src/rudi/difactroy/difactroy.rs#L72-L79) 从 `./config/rudi/template/struct_init.template` 读取模板文件,缓存到 `self.template` 中。
模板文件内容如下([struct_init.template](file:///E:/soft/gitee.com/ruwebframe/config/rudi/template/struct_init.template)):
```rust
// pkg {{StructPkg}}
use std::sync::Arc;
use ctor::ctor;
use crate::rudi::dicontainer::direg;
use crate::{{StructPkg}}::{{RuSingle}};
#[ctor(unsafe)] // ← ctor 宏:程序启动时自动执行
pub fn init() {
register_bean_{{rusingle}}();
}
const SINGLE_BEAN_NAME: &str = "{{SINGLE_BEAN_NAME}}";
pub fn find_bean_{{rusingle}}() -> Option<Arc<{{RuSingle}}>> {
direg::get_bean::<{{RuSingle}}>(SINGLE_BEAN_NAME)
}
pub fn register_bean_{{rusingle}}() {
direg::{{register_singleton}}(SINGLE_BEAN_NAME, {{RuSingle}}::new);
}
```
其中 `{{xxx}}` 和 `{{xxx}}` 是占位符,后续会被替换。
---
### 四、Step 2:AST 解析扫描 (`parse_all_files`)
[parse_all_files](file:///E:/soft/gitee.com/ruwebframe/src/rudi/difactroy/difactroy.rs#L64-L71) 是核心扫描逻辑:
```rust
pub fn parse_all_files(&self, dir: &str) {
let ua = ruast::RuAst::new();
let mut results = Vec::<didto::StructInfo>::new();
for f in fileutils::list_all_files_rs(dir) {
let content = fileutils::read_file(f.as_str()).unwrap();
let v = ua.find_entity_all(f.as_str(), content.as_str());
results.extend(v);
}
*self.structs.lock().unwrap() = results;
}
```
**关键原理**:使用 `syn` crate(Rust 官方 AST 解析库)来解析每个 `.rs` 文件的源码,寻找实现了特定 Trait 的 Struct。
#### 4.1 标记 Trait 体系
框架定义了两种标记 Trait:
| `BaseEntity` | 多例(每次获取都创建新实例) | `direg::register()` |
| `BaseEntitySingle` | 单例(全局唯一实例) | `direg::register_singleton()` |
[ruast.rs](file:///E:/soft/gitee.com/ruwebframe/src/rudi/difactroy/ruast/ruast.rs#L46-L60) 中通过 `syn` 遍历 AST 的 `Item::Impl` 节点:
```rust
pub fn find_base_entity(&self, file: &File) -> Vec<String> {
for item in &file.items {
if let Item::Impl(item_impl) = item {
if let Some((_, trait_path, _)) = &item_impl.trait_ {
if let Some(seg) = trait_path.segments.last() {
if seg.ident == "BaseEntity" { // ← 匹配 trait 名称
if let Some(name) = type_to_string(&item_impl.self_ty) {
results.push(name); // ← 提取 Struct 名称
}
}
}
}
}
}
}
```
#### 4.2 生成 StructInfo 元数据
[find_entity_all](file:///E:/soft/gitee.com/ruwebframe/src/rudi/difactroy/ruast/ruast.rs#L116-L143) 将找到的每个 Struct 封装为 `StructInfo`:
```rust
pub fn find_entity_all(&self, fileName: &str, content: &str) -> Vec<didto::StructInfo> {
let s1 = self.find_base_entity_content(content); // BaseEntity 多例
let s2 = self.find_base_entity_single_content(content); // BaseEntitySingle 单例
for a in s1 {
si.set_if_single(false); // 多例模式
si.set_struct_name(a);
si.set_path_file(fileName);
si.set_struct_pkg(si.build_struct_pkg()); // 构建 Rust 包路径
// ...
}
for a in s2 {
si.set_if_single(true); // 单例模式
// ...
}
}
```
`StructInfo` DTO 包含以下关键字段([didto.rs](file:///E:/soft/gitee.com/ruwebframe/src/rudi/didto/didto.rs#L8-L22)):
| `pathFile` | 源文件路径 | `./src/database/database.rs` |
| `structName` | 结构体名称 | `Database` |
| `structPkg` | Rust 包路径 | `database::database` |
| `ifSingle` | 是否单例 | `true` |
| `structIniFile` | 生成的 init 文件名 | `database_init.rs` |
| `single_bean_name` | 全局唯一 Bean 名称 | `Database:uuid:single` |
---
### 五、Step 3:代码生成 (`build_init`)
[build_init](file:///E:/soft/gitee.com/ruwebframe/src/rudi/difactroy/difactroy.rs#L81-L103) 执行模板变量替换:
```rust
pub fn build_init(&self, stru: &didto::StructInfo) -> String {
let mut template_str = self.template.lock().unwrap().clone();
// 1. 根据 ifSingle 决定注册方式
if stru.ifSingle {
template_str = template_str.replace("{{register_singleton}}", "register_singleton");
} else {
template_str = template_str.replace("{{register_singleton}}", "register");
}
// 2. 替换结构体名称
template_str = template_str.replace("{{RuSingle}}", stru.structName.as_str());
template_str = template_str.replace("{{rusingle}}", stru.structName.to_lowercase());
// 3. 替换唯一 Bean 名称
template_str = template_str.replace("{{SINGLE_BEAN_NAME}", stru.build_single_bean_name());
// 4. 替换包路径
template_str = template_str.replace("{{StructPkg}}", stru.structPkg.as_str());
// 5. 替换元信息(公司、作者、时间)
template_str = template_str.replace("{{Corp}}", cfg.software.corp.as_str());
template_str = template_str.replace("{{Author}}", cfg.software.author.as_str());
template_str = template_str.replace("{{Now}}", rutils::local_now2str().as_str());
}
```
**替换前后对比示例**(以 `Database` 结构体为例):
```
替换前: 替换后:
{{StructPkg}} → database::database
{{RuSingle}} → Database
{{rusingle}} → database
{{register_singleton}} → register_singleton (ifSingle=true)
{{SINGLE_BEAN_NAME}} → Database:uuid-xxxx:single
{{Corp}} → akunlong
{{Author}} → leijmdas@163.com
{{Now}} → 2026-08-09 14:04:28
```
---
### 六、Step 4:写入文件 (`create_init`)
生成的代码被写入到与源文件同目录的 `_init.rs` 文件中:
```
./src/database/database.rs → ./src/database/database_init.rs
```
---
### 七、运行时 DI 容器机制
生成的 `_init.rs` 文件使用 `#[ctor]` 宏实现**程序启动时自动注册**:
```rust
#[ctor(unsafe)] // 程序启动时自动调用
pub fn init() {
register_bean_database(); // 向全局容器注册
}
```
#### 7.1 全局容器
[direg.rs](file:///E:/soft/gitee.com/ruwebframe/src/rudi/dicontainer/direg.rs) 使用 `OnceLock<RwLock<Container>>` 实现线程安全的全局单例容器:
```rust
static GLOBAL: OnceLock<RwLock<dicontainer::Container>> = OnceLock::new();
fn global() -> &'static RwLock<dicontainer::Container> {
GLOBAL.get_or_init(|| {
let mut c = dicontainer::Container::new();
dicontainer::bootstrap(&mut c); // 自动收集所有注册项
RwLock::new(c)
})
}
```
#### 7.2 容器内部存储
[dicontainer.rs](file:///E:/soft/gitee.com/ruwebframe/src/rudi/dicontainer/dicontainer.rs#L10-L18) 的 `Entry` 枚举区分两种模式:
```rust
enum Entry {
Factory(Box<dyn Fn() -> Box<dyn Any> + Send + Sync>), // 多例:每次调用工厂
Singleton {
factory: Box<dyn Fn() -> Box<dyn Any> + Send + Sync>, // 单例:工厂 + 缓存
instance: Mutex<Option<Arc<dyn Any + Send + Sync>>>,
},
}
```
#### 7.3 获取 Bean
业务代码通过 `find_bean_xxx()` 函数获取实例:
```rust
pub fn find_bean_database() -> Option<Arc<Database>> {
direg::get_bean::<Database>(SINGLE_BEAN_NAME)
}
```
[get_bean](file:///E:/soft/gitee.com/ruwebframe/src/rudi/dicontainer/dicontainer.rs#L121-L137) 统一处理工厂和单例:
```rust
pub fn get_bean<T: Any + Send + Sync>(&self, key: &str) -> Option<Arc<T>> {
match entry {
Entry::Factory(factory) => {
// 多例:每次都 new
Some(Arc::new(factory()))
}
Entry::Singleton { factory, instance } => {
// 单例:首次 new 并缓存,之后复用
if guard.is_none() {
*guard = Some(Arc::new(factory()));
}
guard.as_ref().clone()
}
}
}
```
---
### 八、完整流程图
```
make_di_all("./src")
│
├─ 1. fill_template()
│ └─ 读取 config/rudi/template/struct_init.template
│
├─ 2. parse_all_files("./src")
│ ├─ 遍历所有 .rs 文件
│ ├─ syn::parse_file() 解析 AST
│ ├─ 查找 impl BaseEntity for XXX → 多例 (ifSingle=false)
│ ├─ 查找 impl BaseEntitySingle for XXX → 单例 (ifSingle=true)
│ └─ 生成 Vec<StructInfo>
│
├─ 3. 遍历每个 StructInfo
│ ├─ build_init(stru)
│ │ ├─ 替换 {{register_singleton}} → register / register_singleton
│ │ ├─ 替换 {{RuSingle}} → 结构体名
│ │ ├─ 替换 {{SINGLE_BEAN_NAME}} → 结构体名:UUID:single
│ │ └─ 替换 {{StructPkg}} → Rust包路径
│ │
│ └─ create_init(ini_pathfile, content)
│ └─ 写入 xxx_init.rs 文件
│
└─ 运行时
├─ #[ctor] 自动调用 init() → register_bean_xxx()
├─ 注册到全局 Container (OnceLock<RwLock<Container>>)
└─ 业务代码通过 find_bean_xxx() 获取 Arc<T>
```
---
### 九、设计亮点总结
| **无侵入标记** | 只需 `impl BaseEntity for Xxx {}`,无需宏或注解 |
| **AST 级别解析** | 使用 `syn` crate 精确解析 Rust 源码结构 |
| **模板化生成** | 通过占位符替换,灵活生成初始化代码 |
| **启动时自动注册** | `#[ctor]` 宏确保 main 之前完成注册 |
| **线程安全** | `OnceLock` + `RwLock` 保证全局容器安全 |
| **UUID 唯一命名** | 每个 Bean 名称包含 UUID,避免命名冲突 |
| **单例/多例双模式** | `BaseEntitySingle` vs `BaseEntity` 区分 |
| **增量生成** | `existIniFile` 检查,已存在的跳过(非 force 模式) |
ru_single_init::find_bean_rusingle() 和rusingle目录下mod.rs如何优化 包路径
18:05
Read 7 files, Browsed 1 folder, Searched files 2 times
分析结果
我发现了以下几个问题:
问题清单
重复定义:mod.rs 和 ru_single_init.rs 都定义了 find_bean_rusingle()、register_bean_rusingle() 和 SINGLE_BEAN_NAME
可见性错误:ru_single_init.rs 中引用 crate::rudomain::rusingle::SINGLE_BEAN_NAME,但 mod.rs 中 SINGLE_BEAN_NAME 不是 pub 的
路径混乱:测试文件同时使用 rusingle::find_bean_rusingle() 和 ru_single_init::find_bean_rusingle()
优化方案
参照项目中 rudi/rucontainer/ 的模式:mod.rs 只做模块声明 + re-export,实现集中到 ru_single_init.rs。
下面是具体修改:
1. 改造 ru_single_init.rs —— 成为唯一实现文件
Edited 1 file
2. 简化 mod.rs —— 只做模块声明 + re-export
Edited 1 file
3. 简化 rudomain_test.rs —— 使用统一路径
Edited 1 file
Let me check if there are any other files that reference these modules to ensure the full refactoring is complete.
Read 2 files, Searched files 1 time
Good catch — I found an additional bug while tracing through the code. Let me re-examine the ruregister API.
Looking at ruregister.rs:
register() → 注册多例(每次创建新实例)
register_singleton() → 注册单例
当前 mod.rs 用的是 register(多例),但 ru_single_init.rs 用的是 register_single(拼写错误,实际不存在此函数)。正确的应该是 register_singleton,因为 RuSingle 是单例模式。
我来更新 ru_single_init.rs 的 diff,修正为 register_singleton:
Edited 1 file
完整优化总结
当前问题一览
# 问题 位置 严重性
1 重复定义 mod.rs 和 ru_single_init.rs 都定义了相同的函数和常量 🔴 高
2 路径错误 ru_single_init.rs 引用 crate::rudomain::rusingle::SINGLE_BEAN_NAME,但常量不是 pub 的 🔴 高
3 注册方式错误 mod.rs 用 register(多例),但单例应用 register_singleton 🔴 高
4 拼写错误 ru_single_init.rs 调用了不存在的 register_single(应为 register_singleton) 🔴 高
5 API 路径不统一 测试文件同时使用 rusingle:: 和 ru_single_init:: 访问 🟡 中
6 常量命名不规范 SINGLE_BEAN_NAME 应为 SINGLE_BEAN_NAME(Rust 常量风格) 🟡 中
优化后的架构
Plain Text
rusingle/
├── mod.rs ← 模块入口:声明子模块 + re-export
└── ru_single_init.rs ← 唯一实现:RuSingle 结构体 + 注册/查找函数
优化前调用方式:
Rust
// 方式1:通过父模块
rusingle::find_bean_rusingle()
// 方式2:通过子模块(路径冗长)
ru_single_init::find_bean_rusingle()
优化后调用方式(统一):
Rust
// 干净的统一路径
rusingle::find_bean_rusingle()
rusingle::RuSingle::new()
修改涉及的文件
ru_single_init.rs — 成为唯一实现文件,包含 RuSingle 结构体、常量和函数
mod.rs — 简化为纯入口,只做 pub mod 声明和 pub use re-export
rudomain_test.rs — 去除冗余 import 和重复测试用例
请 Review 并 Accept 以上三个 diff 以应用修改。