exum 0.1.17

A lightweight Axum syntax sugar library
Documentation
# 依赖注入

Exum 提供了依赖注入功能,支持全局状态管理和自动依赖注入。

## 使用建议

建议使用 `#[main]` 宏来启动应用,因为依赖注入功能需要正确的初始化顺序。使用 `#[main]` 宏可以确保:
1. 全局状态容器在应用构建前正确初始化
2. 预热状态在应用启动前完成初始化
3. 配置加载和依赖注入的顺序正确

如果手动初始化,需要调用 `init_global_state().await` 来初始化全局状态容器,如果需要预热状态,还需要调用 `global_container().prewarm_all().await`。

## 状态定义 (`#[state]`)

使用 `#[state]` 宏定义全局状态,这些状态会在应用启动时自动初始化。

### 基本用法

```rust
use exum::*;
use std::sync::atomic::AtomicUsize;

#[derive(Debug)]
pub struct AppConfig {
    pub version: String,
    pub debug: bool,
}

#[derive(Debug)]
pub struct Counter {
    pub value: AtomicUsize,
}

// 定义应用配置状态
#[state]
async fn config() -> AppConfig {
    println!("[config] 初始化配置");
    AppConfig {
        version: "v1.0".into(),
        debug: true,
    }
}

// 定义计数器状态
#[state]
async fn counter() -> Counter {
    println!("[counter] 初始化计数器");
    Counter { value: AtomicUsize::new(0) }
}
```

### 预热状态 (`#[state(prewarm)]`)

使用 `prewarm` 参数可以在应用启动时立即启动线程准备初始化状态,而不是直到第一次使用时才初始化。

```rust
use exum::*;

#[derive(Debug)]
pub struct Database {
    pub connection_string: String,
}

// 预热数据库连接,应用启动时立即准备初始化
#[state(prewarm)]
async fn database() -> Database {
    println!("[database] 预热数据库连接");
    Database {
        connection_string: "postgres://localhost:5432/mydb".into(),
    }
}
```

## 依赖注入

### 线程安全机制

Exum 使用 `Mutex` 包装依赖项,确保线程安全。依赖注入现在直接定义为原始类型,实际上是 `MutexGuard<'_, T>` 类型。


### 自动依赖判断规则

Exum 现在会自动判断哪些参数需要依赖注入,无需使用 `#[dep]` 属性。自动判断规则如下:

1. **没有使用其他属性宏**:参数没有使用 `#[b]``#[q]` 等属性宏
2. **不在参数路由中**:参数不是路径参数(如 `:id``{param}`3. **不是提取器模式**:参数不是提取器模式,如 `Json(data)`
4. **类型不在排除列表中**:参数类型不在以下排除列表中:

```rust
static NOT_DEPENCENCY_TYPE: &[&str] = &[
    // HTTP 核心类型
    "Method", "Uri", "Version", "HeaderMap",
    // 请求体类型
    "String", "Bytes", "Body",
    // axum 特定类型
    "OriginalUri", "MatchedPath", "RawQuery",
];
```

满足以上条件的参数会自动被当作依赖注入。

### Service 宏 (`#[service]`)

使用 `#[service]` 宏修饰 `impl` 块,自动为服务类提供依赖注入功能:

- 必须包含 `new` 函数
- `new` 函数的参数会被自动依赖注入
- 参数必须是可以被注入的依赖类型

```rust
use exum::*;

// 基础依赖服务
struct DatabaseService {
    connection_string: String,
}

#[service]
impl DatabaseService {
    async fn new() -> Self {
        // 模拟数据库连接初始化
        Self {
            connection_string: "postgres://localhost:5432/mydb".to_string(),
        }
    }
}

// 业务服务,依赖 DatabaseService
#[derive(Debug)]
struct UserService {
    db_service: DatabaseService,
    user_count: usize,
}

#[service]
impl UserService {
    // new 函数的参数会被自动注入
    // DatabaseService 会被框架自动提供
    async fn new(db_service: DatabaseService) -> Self {
        println!("UserService 初始化,使用数据库: {}", db_service.connection_string);
        
        Self {
            db_service,
            user_count: 0, // 模拟用户计数
        }
    }
    
    // 可以添加其他业务方法
    async fn add_user(&mut self) {
        self.user_count += 1;
        println!("添加用户,当前用户数: {}", self.user_count);
    }
}

// 在路由中使用服务
#[get("/users")]
async fn get_users(user_service: UserService) -> String {
    format!(
        "用户服务信息 - 数据库: {}, 当前用户数: {}",
        user_service.db_service.connection_string, user_service.user_count
    )
}

#[post("/users")]
async fn add_user(mut user_service: UserService) -> String {
    // 现在直接使用可变引用,无需处理 Arc
    user_service.add_user().await;
    
    format!("用户添加成功,当前用户数: {}", user_service.user_count)
}
```

### 手动获取依赖

除了自动依赖注入外,也可以手动获取依赖:

```rust
use exum::*;

// 手动获取依赖
async fn some_function() {
    let config = global_container().get::<AppConfig>().await;
    let counter = global_container().get::<Counter>().await;
    
    // 使用获取到的依赖
    println!("版本: {}", config.version);
}
```

手动获取依赖的方式不如路由宏方便,但在某些场景下可能更灵活。

### 基本用法

```rust
use exum::*;

#[get("/config")]
async fn get_config(config: AppConfig) -> String {
    format!("应用配置: {:?}", config)
}

#[get("/counter")]
async fn get_counter(counter: Counter) -> String {
    let current = counter.value.load(std::sync::atomic::Ordering::Relaxed);
    format!("当前计数: {}", current)
}

#[post("/increment")]
async fn increment_counter(counter: Counter) -> String {
    let prev = counter.value.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
    format!("计数从 {} 增加到 {}", prev, prev + 1)
}
```

**注意**:现在依赖注入直接使用原始类型,框架会自动处理 `Mutex` 包装,确保线程安全。参数会自动判断是否需要依赖注入,无需使用 `#[dep]` 属性。

### 注入多个依赖

```rust
use exum::*;

#[get("/status")]
async fn get_status(
    config: AppConfig,
    counter: Counter,
    db: Database,
) -> String {
    let count = counter.value.load(std::sync::atomic::Ordering::Relaxed);
    format!(
        "状态检查 - 版本: {}, 调试: {}, 计数: {}, 数据库: {}",
        config.version, config.debug, count, db.connection_string
    )
}
```

## 完整示例

```rust
use exum::*;
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;

// 状态定义
#[derive(Debug)]
pub struct AppConfig {
    pub version: String,
}

#[derive(Debug)]
pub struct Counter {
    pub value: AtomicUsize,
}

#[derive(Debug)]
pub struct Database {
    pub connection_string: String,
}

// 服务定义
#[derive(Debug)]
pub struct UserService {
    config: AppConfig,
    db: Database,
}

// 使用 service 宏自动注入依赖
#[service]
impl UserService {
    async fn new(config: AppConfig, db: Database) -> Self {
        Self { config, db }
    }
}

// 预热数据库连接
#[state(prewarm)]
async fn database() -> Database {
    println!("[database] 初始化数据库连接");
    Database {
        connection_string: "postgres://localhost:5432/mydb".into(),
    }
}

// 应用配置
#[state]
async fn config() -> AppConfig {
    println!("[config] 初始化应用配置");
    AppConfig { version: "v1.0".into() }
}

// 计数器
#[state]
async fn counter() -> Counter {
    println!("[counter] 初始化计数器");
    Counter { value: AtomicUsize::new(0) }
}

// 用户服务
#[state]
async fn user_service() -> UserService {
    println!("[user_service] 初始化用户服务");
    // 依赖会自动注入到 UserService::new 中
    UserService::new(
        global_container().get::<AppConfig>().await,
        global_container().get::<Database>().await,
    ).await
}

// 路由处理函数
#[get("/")]
async fn index(config: AppConfig) -> String {
    format!("欢迎使用 {} 版本应用", config.version)
}

#[get("/counter")]
async fn get_counter(counter: Counter) -> String {
    let current = counter.value.load(std::sync::atomic::Ordering::Relaxed);
    format!("当前计数: {}", current)
}

#[post("/increment")]
async fn increment_counter(counter: Counter) -> String {
    let prev = counter.value.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
    format!("计数从 {} 增加到 {}", prev, prev + 1)
}

#[get("/status")]
async fn status(
    config: AppConfig,
    counter: Counter,
    db: Database,
) -> String {
    let count = counter.value.load(std::sync::atomic::Ordering::Relaxed);
    format!(
        "应用状态 - 版本: {}, 计数: {}, 数据库: {}",
        config.version, count, db.connection_string
    )
}

// 使用服务
#[get("/users")]
async fn get_users(user_service: UserService) -> String {
    format!(
        "用户服务 - 版本: {}, 数据库: {}",
        user_service.config.version, user_service.db.connection_string
    )
}

// 使用 #[main] 宏自动处理初始化
#[main]
async fn main() {
    println!("服务器启动完成");
}
```

### 状态注册

当使用 `#[state]` 宏时,状态定义会被自动注册到全局依赖容器中。容器会在应用启动时初始化所有状态。

### 依赖解析

在路由处理函数中使用 `#[dep]` 属性时,框架会自动从全局容器中获取对应的状态实例并注入到函数参数中。

### 生命周期管理

- 所有状态都是单例的,在整个应用生命周期内共享
- 状态初始化是惰性的,除非使用 `prewarm` 参数
- 状态是线程安全的,可以在多个请求中安全使用

## 注意事项

- 状态函数必须是 `async`- 状态类型必须实现 `Send + Sync + 'static`
- 预热状态会在应用启动时立即初始化,可能影响启动时间
- **自动依赖判断规则**  - 参数没有使用 `#[b]``#[q]` 等属性宏
  - 参数不是路径参数(如 `:id``{param}`  - 参数不是提取器模式,如 `Json(data)`
  - 参数类型不在排除列表中(HTTP核心类型、请求体类型、axum特定类型)
- **依赖注入机制变更**  - 现在使用 `Mutex` 包装依赖项,确保线程安全
  - 直接使用原始类型,框架会自动处理 `Mutex` 包装
  - 不再支持之前的 `Arc` 自动解析机制
  - 简化了依赖类型判断逻辑
- **Service 宏注意事项**  - 必须包含 `new` 函数
  - `new` 函数的参数必须是可以被注入的依赖类型
  - 参数会被自动依赖注入,无需手动调用 `global_container().get()`