服务依赖注入(Dependency Injection)

简单的静态依赖注入

#![allow(unused)]
fn main() {
use std::{
    any::{Any, TypeId},
    collections::HashMap,
    sync::{Arc, RwLock},
};


pub trait AnyService: Any + Send + Sync + 'static {}
impl<T: Any + Send + Sync + 'static> AnyService for T {}


// 定义一个 Repository Trait
trait UserRepository: Any + Send + Sync {
    fn get_user(&self, id: u32) -> String;
}

// 实现一个具体的 Repository
struct InMemoryUserRepository;

impl UserRepository for InMemoryUserRepository {
    fn get_user(&self, id: u32) -> String {
        format!("User {} from InMemoryRepo", id)
    }
}

// 定义一个 Service,它依赖 UserRepository
struct UserService<R: UserRepository> {
    repo: R,
}

impl<R: UserRepository> UserService<R> {
    fn new(repo: R) -> Self {
        Self { repo }
    }

    fn greet_user(&self, id: u32) -> String {
        let user = self.repo.get_user(id);
        format!("Hello, {}!", user)
    }
}

 // di 通常与异步运行时配合,但这里可以不用 async
 fn dependency_injection_manul_sample() -> anyhow::Result<()> {
    // --- 创建 DI 容器 ---
    let mut services = HashMap::<TypeId, Arc<dyn Any + Send + Sync>>::new();

    services.insert(
        TypeId::of::<InMemoryUserRepository>(),
        Arc::new(InMemoryUserRepository),
    );

    let repo = services
        .get(&TypeId::of::<InMemoryUserRepository>())
        .unwrap();

    let repo = (repo.clone()).downcast::<InMemoryUserRepository>().unwrap();

    services.insert(
        TypeId::of::<UserService<InMemoryUserRepository>>(),
        Arc::new(UserService::new(InMemoryUserRepository)),
    );

    //  --- 从容器中解析出 UserService ---

    let user_serivce = services
        .get(&TypeId::of::<UserService<InMemoryUserRepository>>())
        .unwrap();

    let user_service = user_serivce
        .clone()
        .downcast::<UserService<InMemoryUserRepository>>()
        .unwrap();

    // // 调用业务方法
    let greeting = user_service.greet_user(42);
    println!("{}", greeting);

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_dependency_injection_sample() {
        dependency_injection_manul_sample();
    }
}


}

执行输出结果:

running 1 test
test services::dependency_injection_concrete_sample::tests::test_dependency_injection_sample ... ok

successes:

---- services::dependency_injection_concrete_sample::tests::test_dependency_injection_sample stdout ----
Hello, User 42 from InMemoryRepo!


successes:
    services::dependency_injection_concrete_sample::tests::test_dependency_injection_sample

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 18 filtered out; finished in 0.00s

服务容器的动态依赖注入

#![allow(unused)]
fn main() {
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

// Base trait for all services
trait Service: Send + Sync + 'static {
    fn name(&self) -> &'static str;
}

// Example service interfaces
trait LoggerService: Send + Sync + 'static {
    fn log(&self, message: &str);
}

trait DatabaseService: Send + Sync + 'static {
    fn query(&self, query: &str) -> String;
}

// Concrete service implementations
struct ConsoleLogger;
impl Service for ConsoleLogger {
    fn name(&self) -> &'static str {
        "ConsoleLogger"
    }
}
impl LoggerService for ConsoleLogger {
    fn log(&self, message: &str) {
        println!("Log: {}", message);
    }
}

struct InMemoryDatabase;
impl Service for InMemoryDatabase {
    fn name(&self) -> &'static str {
        "InMemoryDatabase"
    }
}
impl DatabaseService for InMemoryDatabase {
    fn query(&self, query: &str) -> String {
        format!("Query result for: {}", query)
    }
}

// Service with dependencies
struct BusinessService {
    logger: Arc<dyn LoggerService>,
    database: Arc<dyn DatabaseService>,
}
impl Service for BusinessService {
    fn name(&self) -> &'static str {
        "BusinessService"
    }
}
impl BusinessService {
    fn new(logger: Arc<dyn LoggerService>, database: Arc<dyn DatabaseService>) -> Self {
        BusinessService { logger, database }
    }

    fn perform_task(&self, task: &str) {
        self.logger.log(&format!("Performing task: {}", task));
        let result = self.database.query(task);
        self.logger.log(&format!("Task result: {}", result));
    }
}

// Service Container
struct ServiceContainer {
    services: Mutex<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>,
    factories: Mutex<HashMap<TypeId, Box<dyn Fn(&ServiceContainer) -> Arc<dyn Any + Send + Sync>>>>,
}

impl ServiceContainer {
    fn new() -> Self {
        ServiceContainer {
            services: Mutex::new(HashMap::new()),
            factories: Mutex::new(HashMap::new()),
        }
    }

    // Register a concrete service
    fn register<T: Service + 'static>(&self, service: T) {
        let type_id = TypeId::of::<T>();
        let service: Arc<dyn Any + Send + Sync> = Arc::new(service);
        self.services.lock().unwrap().insert(type_id, service);
    }

    // Register a trait object
    fn register_trait<T: ?Sized + Any + Send + Sync + 'static>(
        &self,
        service: Arc<dyn Any + Send + Sync>,
        type_id: TypeId,
    ) {
        // Ensure the service is a trait object
        // Insert the service into the container
        let service: Arc<dyn Any + Send + Sync> = service;
        // let type_id = TypeId::of::<T>();

        println!("Registering trait object: {:?}", type_id);

        // Insert the service into the services map
        self.services.lock().unwrap().insert(type_id, service);
    }

    // Register a factory for lazy initialization
    fn register_factory<
        T: Service + 'static,
        F: Fn(&ServiceContainer) -> Arc<T> + Send + Sync + 'static,
    >(
        &self,
        factory: F,
    ) {
        let type_id = TypeId::of::<T>();
        let wrapped_factory: Box<dyn Fn(&ServiceContainer) -> Arc<dyn Any + Send + Sync>> =
            Box::new(move |container| {
                let service = factory(container);
                service as Arc<dyn Any + Send + Sync>
            });
        self.factories
            .lock()
            .unwrap()
            .insert(type_id, wrapped_factory);
    }

    // Resolve a concrete service
    fn resolve<T: Service + 'static>(&self) -> Option<Arc<T>> {
        let type_id = TypeId::of::<T>();
        if let Some(service) = self.services.lock().unwrap().get(&type_id) {
            return service.clone().downcast::<T>().ok();
        }
        if let Some(factory) = self.factories.lock().unwrap().get(&type_id) {
            let service = factory(self);
            self.services
                .lock()
                .unwrap()
                .insert(type_id, service.clone());
            return service.downcast::<T>().ok();
        }
        None
    }

    // Resolve a trait object (accepts Arc<dyn Trait> types); trait objects themselves (dyn Trait) are unsized,
    // so call this with Arc<dyn Trait> as the type parameter (e.g. resolve_trait::<Arc<dyn LoggerService>>()).
    fn resolve_trait<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
        let type_id = TypeId::of::<T>();

        println!("resolve trait trait object: {:?}", type_id);
        if let Some(service) = self.services.lock().unwrap().get(&type_id) {
            let downcast = service.clone().downcast::<T>().ok();

            println!("resolve trait trait object: {:?}", downcast.is_some());

            return downcast;
        }

        None
    }
}

fn container_injection_main() {
    let container = Arc::new(ServiceContainer::new());

    // Register concrete services
    container.register(ConsoleLogger);
    container.register(InMemoryDatabase);

    // Register trait objects
    // using a box or reference package to dyn trait objects
    container.register_trait::<Arc<dyn LoggerService>>(
        Arc::new(Arc::new(ConsoleLogger) as Arc<dyn LoggerService>),
        TypeId::of::<Arc<dyn LoggerService>>(),
    );

    container.register_trait::<Arc<dyn DatabaseService>>(
        Arc::new(Arc::new(InMemoryDatabase) as Arc<dyn DatabaseService>),
        TypeId::of::<Arc<dyn DatabaseService>>(),
    );

    // Register a factory for BusinessService (resolve concrete implementations and coerce to trait objects)
    container.register_factory::<BusinessService, _>(|container| {
        let logger_concrete = container
            .resolve::<ConsoleLogger>()
            .expect("LoggerService not found");
        let logger: Arc<dyn LoggerService> = logger_concrete;

        let database_concrete = container
            .resolve::<InMemoryDatabase>()
            .expect("DatabaseService not found");
        let database: Arc<dyn DatabaseService> = database_concrete;

        Arc::new(BusinessService::new(logger, database))
    });

    // Resolve and use BusinessService
    let business_service = container
        .resolve::<BusinessService>()
        .expect("BusinessService not found");
    business_service.perform_task("Process data");

    // Resolve and use a trait object (request Arc<dyn LoggerService> as the type parameter)
    let logger = container
        .resolve_trait::<Arc<dyn LoggerService>>()
        .expect("LoggerService not found");
    logger.log("Direct logger access");

    // Resolve and use a trait object (request Arc<dyn DatabaseService> as the type parameter)
    let database_service = container
        .resolve_trait::<Arc<dyn DatabaseService>>()
        .expect("DatabaseService not found");
    let result = database_service.query("Direct database access");

    println!("query: {}", result);
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_injection_main() {
        container_injection_main();
    }
}


}

执行输出结果:


running 1 test
test services::dependency_injection_dynmaic_sample::tests::test_injection_main ... ok

successes:

---- services::dependency_injection_dynmaic_sample::tests::test_injection_main stdout ----
Registering trait object: TypeId(0x56cbfa3cabf188feb9d956576e38424a)
Registering trait object: TypeId(0x8ffe278beee70f55493c3bb2f23af8b3)
Log: Performing task: Process data
Log: Task result: Query result for: Process data
resolve trait trait object: TypeId(0x56cbfa3cabf188feb9d956576e38424a)
resolve trait trait object: true
Log: Direct logger access
resolve trait trait object: TypeId(0x8ffe278beee70f55493c3bb2f23af8b3)
resolve trait trait object: true
query: Query result for: Direct database access


successes:
    services::dependency_injection_dynmaic_sample::tests::test_injection_main

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 18 filtered out; finished in 0.00s