几句代码简单实现IoC容器

前言

  最近在调试EasyNetQ代码的时候发现里面有一段代码,就是IoC容器的简单实现,跟着他的代码敲了一遍,发现了奇妙之处。当然也是因为我才疏学浅导致孤陋寡闻了。他的思路就是通过动态调用构造函数生成对象,然后将对象保存,调用的时候进行单例调用,而且,代码中不会存在 new 字眼。所有实例对象的创建和映射都在容器中实现。当然,还是用第三方的容器比较稳妥,本文中只是很简单的一个示范。具体理解的是否正确,我也不敢说,只不过,能达到一些预期的效果,功能不够强大。

解析

  首先,我们先添加几个接口。IServiceProvider,IServiceRegister,IServiceContainer (继承自前两个)

 /// <summary>
    /// 服务提供接口
    /// </summary>
    public interface IServiceProvider
    {
        /// <summary>
        /// 获取某个类型的服务实例
        /// </summary>
        /// <typeparam name="TService"> TService 接口 </typeparam>
        /// <returns>返回TService类型实例</returns>
        TService Resolve<TService>() where TService : class;
    }
 /// <summary>
    /// 服务注册接口
    /// </summary>
    public interface IServiceRegister
    {
        /// <summary>
        /// 注册一个服务工厂
        /// </summary>
        /// <typeparam name="TService">工厂类</typeparam>
        /// <param name="serviceCreator">创建一个新的接口实例</param>
        /// <returns>返回当前注册工厂,以便调用其他的register方法</returns>
        IServiceRegister Register<TService>(Func<IServiceProvider, TService> serviceCreator) where TService : class;

        /// <summary>
        /// 注册服务类型和服务实例
        /// </summary>
        /// <typeparam name="TService">接口实例</typeparam>
        /// <typeparam name="TImplementation">类型继承自TService</typeparam>
        /// <returns>返回当前注册工厂,以便调用其他的register方法</returns>
        IServiceRegister Register<TService, TImplementation>()
            where TService : class
            //TImplementation 继承自 TService
            where TImplementation : class, TService;
    }
 /// <summary>
    /// 对外接口Container,继承自IServiceProvider,IServiceRegister
    /// </summary>
    public interface IServiceContainer : IServiceProvider, IServiceRegister { }

  当然,最主要的还是 IServiceContainer 的实现,代码中有简单的注释,所以我就直接贴代码了。(看不懂的仔细认真一些就好)

  /// <summary>
    /// 服务提供类的实现,类似服务工厂
    /// </summary>
    public class ServiceProvider : IServiceContainer
    {
        /// <summary>
        /// 锁
        /// </summary>
        private readonly object syncLock = new object();
        /// <summary>
        /// 存储单例工厂
        /// </summary>
        private readonly ConcurrentDictionary<Type, object> factories = new ConcurrentDictionary<Type, object>();
        /// <summary>
        /// 存储注册类型
        /// </summary>
        private readonly ConcurrentDictionary<Type, Type> registrations = new ConcurrentDictionary<Type, Type>();
        /// <summary>
        /// 存储实例
        /// </summary>
        private readonly ConcurrentDictionary<Type, object> instances = new ConcurrentDictionary<Type, object>();

        #region 私有方法
        /// <summary>
        /// 判断服务是否已经被注册过
        /// </summary>
        /// <param name="serviceType"></param>
        /// <returns></returns>
        private bool ServiceIsRegistered(Type serviceType)
        {
            //判断是否在工厂或者注册库内已经注册过该类型
            return factories.ContainsKey(serviceType) || registrations.ContainsKey(serviceType);
        }
        #endregion

        /// <summary>
        /// 注册工厂
        /// </summary>
        /// <typeparam name="TService"></typeparam>
        /// <param name="serviceCreator"></param>
        /// <returns></returns>
        public IServiceRegister Register<TService>(Func<IServiceProvider, TService> serviceCreator) where TService : class
        {
            lock (syncLock) {
                var serviceType = typeof(TService);
                if (ServiceIsRegistered(serviceType)) { return this; }

                //将服务添加到存储器中
                factories.TryAdd(serviceType, serviceCreator);
                return this;
            }
        }

        /// <summary>
        /// 注册实例类
        /// </summary>
        /// <typeparam name="TService">IService 必须实现一个构造器</typeparam>
        /// <typeparam name="TImplementation"></typeparam>
        /// <returns></returns>
        public IServiceRegister Register<TService, TImplementation>() where TService : class where TImplementation : class, TService
        {
            lock (syncLock)
            {
                var serviceType = typeof(TService);
                var implType = typeof(TImplementation);

                if (ServiceIsRegistered(serviceType)) { return this; }

                //如果注册的类不是继承自TService接口,抛出异常
                if (!serviceType.IsAssignableFrom(implType))
                {
                    throw new Exception(string.Format("类型 {0} 不继承接口 {1}", implType.Name, serviceType.Name));
                }
                //获取构造方法,必须只有一个构造方法(why?)
                var constructors = implType.GetConstructors();
                if (constructors.Length != 1)
                {
                    throw new Exception(string.Format("服务实例必须有且只有一个构造方法.当前实例 {0} 拥有 {1} 个", implType.Name, constructors.Length.ToString()));
                }
                //添加
                registrations.TryAdd(serviceType, implType);
                return this;
            }
        }

        /// <summary>
        /// 返回一个实例
        /// </summary>
        /// <typeparam name="TService"></typeparam>
        /// <returns></returns>
        public virtual TService Resolve<TService>() where TService : class
        {
            var serviceType = typeof(TService);
            object service;
            //如果实例存储器中已经存在该实例,直接返回
            if (instances.TryGetValue(serviceType, out service))
                return (TService)service;
            lock (syncLock)
            {
                //加锁,再次判断
                if (instances.TryGetValue(serviceType, out service))
                {
                    return (TService)service;
                }

                //如果注册器中存在该类型,创建该实例,并加入到实例存储器中
                if (registrations.ContainsKey(serviceType))
                {
                    var implementationType = registrations[serviceType];
                    service = CreateServiceInstance(implementationType);
                    instances.TryAdd(serviceType, service);
                }
                else if (factories.ContainsKey(serviceType))
                {
                    service = ((Func<IServiceProvider, TService>)factories[serviceType])(this);
                    instances.TryAdd(serviceType, service);
                }
                else
                {
                    throw new Exception(string.Format("服务类型 {0} 未注册", serviceType.Name));
                }
                return (TService)service;
            }
        }

        private object Resolve(Type serviceType)
        {
            return GetType()
                .GetMethod("Resolve", new Type[0])
                .MakeGenericMethod(serviceType)
                .Invoke(this, new object[0]);
        }

        private object CreateServiceInstance(Type implementationType)
        {
            //获取构造器
            var constructors = implementationType.GetConstructors();
            //遍历构造器中的参数类型,获取参数
            var parameters = constructors[0]
                .GetParameters()
                .Select(parameterInfo => Resolve(parameterInfo.ParameterType))
                .ToArray();

            //创建实例方法
            return constructors[0].Invoke(parameters);
        }
    }

调用方式

写个测试:IAnimal 接口中有Shout方法。在创建两个类继承自IAnimal,Dog,Cat。 IPeople中有个Pet方法,在创建两个类,Man,Women 继承自IPeople。 其中IPeople中初始化要传入IAnimal的实例,来确定养的宠物是猫还是狗.

代码如下:

    public interface IAnimal
    {
        void Shout();
    }

    public class Dog : IAnimal
    {
        public void Shout()
        {
            Console.WriteLine("汪汪汪");
        }
    }

    public class Cat : IAnimal
    {
        public void Shout()
        {
            Console.WriteLine("喵喵喵");
        }
    }
    public interface IPeople
    {
        void Pet();
    }

    public class Man : IPeople
    {
        private IAnimal _animal;

        public Man(IAnimal animal)
        {
            _animal = animal;
        }
        public void Pet()
        {
            Console.WriteLine("[男]我有一个宠物,它在:");
            _animal.Shout();
        }
    }

    public class Woman : IPeople
    {
        private IAnimal _animal;

        public Woman(IAnimal animal)
        {
            _animal = animal;
        }
        public void Pet()
        {
            Console.WriteLine("[女]我有一个宠物,它在:");
            _animal.Shout();
        }
    }

测试代码

  static void Main(string[] args)
        {
            Func<IServiceContainer> getContainer = () => new ServiceProvider();
            IServiceContainer container = getContainer();

            //注册
            container.Register(_ => container)
                //注册Dog实例
                .Register<IAnimal, Dog>()
                //注册Woman实例
                .Register<IPeople, Woman>();

            //得到people
            var people = container.Resolve<IPeople>();
            //调用pet方法
            people.Pet();

            Console.Read();
        }

执行结果:

同样,当我们把 Dog换成Cat

我们把animal的注册去掉,由于people依赖于animal,所以会报错误。

总结

  这是一个简单的IoC容器的实现,相信真正的要复杂的很多,不过看EasyNetQ就直接这么用的,我觉得如果需求不高的话,也可以用这种方法。而且,代码看起来清洁了许多,不用自己new对象,而且,内部依赖也可以轻松解决。我该去看看真正的容器是怎么实现的了,byebye

时间: 2024-10-26 01:35:56

几句代码简单实现IoC容器的相关文章

比Spring简单的IoC容器

比Spring简单的IoC容器 Spring 虽然比起EJB轻量了许多,但是因为它需要兼容许多不同的类库,导致现在Spring还是相当的庞大的,动不动就上40MB的jar包, 而且想要理解Spring的内部运行机制,阅读它的代码非常重要, 但是往往它的代码非常的"多". 现在根据Spring对Bean的生命周期的处理, 编写出一款非常小的IoC容器, 没有了对XML的解析,而是通过对Config对象的构造而完成IoC配置文件的声明, 相比较XML的方式, 对重构软件非常具有好处, 并且

最简单的ioc容器代码(低仿Spring )

Spring 的一大核心就是IOC,控制反转(依赖注入). 对象交由容器去控制,降低耦合性. Spring 的ioc实现原理其实很简单,容器启动后读取并解析配置文件,根据配置文件中<bean>标签的class属性,通过反射生成类,将各个属性都通过反射设置好值,最后将已经创建好的bean保存到容器中,等待注入. 详细的说明在这里:http://www.bccn.net/Article/web/jsp/jszl/200810/8059.html 参考这个帖子自己动手写了写,感觉对ioc理解又深入了

C#使用读写锁三句代码简单解决多线程并发写入文件时提示“文件正在由另一进程使用,因此该进程无法访问此文件”的问题

在开发程序的过程中,难免少不了写入错误日志这个关键功能.实现这个功能,可以选择使用第三方日志插件,也可以选择使用数据库,还可以自己写个简单的方法把错误信息记录到日志文件. 选择最后一种方法实现的时候,若对文件操作与线程同步不熟悉,问题就有可能出现了,因为同一个文件并不允许多个线程同时写入,否则会提示“文件正在由另一进程使用,因此该进程无法访问此文件”. 这是文件的并发写入问题,就需要用到线程同步.而微软也给进程同步提供了一些相关的类可以达到这样的目的,本文使用到的 System.Threadin

简单模拟IOC容器:返回对象并能抛出异常

本次要求:已知com.zzj.vo包下分别有Tiger.lion.Elephant三个Java源文件,请据此实现以下功能:①.自定义一个名为Component的注解,要求该注解只能用于类且代码运行时该注解依然有效:②.为Tiger和Lion类添加component注解③.在Application类中定义静态代码块,该代码块可自动将有Component注解修饰的类创建对象并放在Map集合中:然后定义一个名为getBean的static方法,要求传入Class类实例时返回该Class类对应类的对象,

简单模拟IOC容器:为添加了@Autowired的属性赋值(初始值)

创建@Autowired注解 package com.zzj.test; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //设置范围和生存周期 @Target({ElementType.TYPE,ElementType.FI

自己动手开发IOC容器

前两天写简历,写了一句:精通Spring IoC容器.怎么个精通法?还是自己动手写个IOC容器吧. 什么是IoC(Inversion of Control)?什么是DI(Dependency Injection)?不多解释,自己Google!希望你先明确几个概念,该文章不做这方面的阐述,重点关注如何实现.我将用简要的语言从需求,设计,代码三方面来描述一个简单的IoC容器,来说明IoC容器是如何帮我们创建类的实例.如何实现依赖注入,最后会奉上一个完整的IoC容器实例. 一.需求,实现一个IoC容器

[译]6-Spring IOC容器

Spring容器是Spring框架的核心.Spring容器负责对象的创建.装配.以及生命周期的管理.Spring容器使用 依赖注入来管理组成应用程序的组件.Spring容器管理的这些对象我们称之为Bean. Spring容器通过读取配置元数据得知需要对哪些对象进行初始化.配置.装配等.配置元数据可以是xml配置文 件.java注解.java代码.下面这张图从较高的层面展示了Spring容器的工作原理,Spring容器使用POJO和配 置元数据来产生经过充分配置的.可运行的系统或应用程序. Spr

spring源码解析之IOC容器(一)

学习优秀框架的源码,是提升个人技术水平必不可少的一个环节.如果只是停留在知道怎么用,但是不懂其中的来龙去脉,在技术的道路上注定走不长远.最近,学习了一段时间的spring源码,现在整理出来,以便日后温故知新. IOC容器是spring最核心的模块之一,是整个spring体系的基石,spring其他模块中,都需要用到IOC容器的功能.spring框架为我们提供了多种IOC容器,DefaultableBeanFact ory.FileSystemXmlApplicationContext.Class

spring源码解析之IOC容器(二)------加载和注册

上一篇跟踪了IOC容器对配置文件的定位,现在我们继续跟踪代码,看看IOC容器是怎么加载和注册配置文件中的信息的.开始之前,首先我们先来了解一下IOC容器所使用的数据结构-------BeanDefinition,它是一个上层接口,有很多实现类,分别对应不同的数据载体.我们平时开发的时候,也会定义很多pojo类,来作为获取数据的载体.最常见的就是,从数据库中获取数据之后,使用一个定义的pojo来装载,然后我们就可以在程序中使用这个pojo类来编写各种业务逻辑.同样,IOC容器首先会读取配置的XML