前言

  最近在调试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 != )
{
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[])
.MakeGenericMethod(serviceType)
.Invoke(this, new object[]);
} private object CreateServiceInstance(Type implementationType)
{
//获取构造器
var constructors = implementationType.GetConstructors();
//遍历构造器中的参数类型,获取参数
var parameters = constructors[]
.GetParameters()
.Select(parameterInfo => Resolve(parameterInfo.ParameterType))
.ToArray(); //创建实例方法
return constructors[].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

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

  1. IoC原理-使用反射/Emit来实现一个最简单的IoC容器

    从Unity到Spring.Net,到Ninject,几年来陆陆续续用过几个IoC框架.虽然会用,但也没有一直仔细的研究过IoC实现的过程.最近花了点时间,下了Ninject的源码,研究了一番,颇有收 ...

  2. 【最简单IOC容器实现】实现一个最简单的IOC容器

    前面DebugLZQ的两篇博文: 浅谈IOC--说清楚IOC是什么 IoC Container Benchmark - Performance comparison 在浅谈IOC--说清楚IOC是什么 ...

  3. 比Spring简单的IoC容器

    比Spring简单的IoC容器 Spring 虽然比起EJB轻量了许多,但是因为它需要兼容许多不同的类库,导致现在Spring还是相当的庞大的,动不动就上40MB的jar包, 而且想要理解Spring ...

  4. 手写一个最简单的IOC容器,从而了解spring的核心原理

    从事开发工作多年,spring源码没有特意去看过.但是相关技术原理倒是背了不少,毕竟面试的那关还是得过啊! 正所谓面试造火箭,工作拧螺丝.下面实现一个最简单的ioc容器,供大家参考. 1.最终结果 2 ...

  5. .NET实现一个简单的IOC容器

    目录 1.主要细节 2.具体示例 参考及示例代码下载 shanzm-2020年3月17日 20:06:01 1.主要细节 使用反射程序集的方式获取对象的类型 通过反射的方式获取指定类型的的所有公共属性 ...

  6. 自己动手实现一个简单的 IOC容器

    控制反转,即Inversion of Control(IoC),是面向对象中的一种设计原则,可以用有效降低架构代码的耦合度,从对象调用者角度又叫做依赖注入,即Dependency Injection( ...

  7. IL实现简单的IOC容器

    既然了解了IL的接口和动态类之间的知识,何不使用进来项目实验一下呢?而第一反应就是想到了平时经常说的IOC容器,在园子里搜索了一下也有这类型的文章http://www.cnblogs.com/kkll ...

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

    Spring 的一大核心就是IOC,控制反转(依赖注入). 对象交由容器去控制,降低耦合性. Spring 的ioc实现原理其实很简单,容器启动后读取并解析配置文件,根据配置文件中<bean&g ...

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

    本次要求:已知com.zzj.vo包下分别有Tiger.lion.Elephant三个Java源文件,请据此实现以下功能:①.自定义一个名为Component的注解,要求该注解只能用于类且代码运行时该 ...

随机推荐

  1. CSS3——transform2D的应用

    前言: 关于CSS3,我想最让人感到有意思的就是2D和3D的技术,这让我们的网页立马丰富起来,可以让我们完成一些很酷很炫的效果,比如旋转木马.经过一段时间的学习,让我对CSS3有了更近一步的了解,在此 ...

  2. ngnix优化【转】

    nginx的优化 1. gzip压缩优化 2. expires缓存有还 3. 网络IO事件模型优化 4. 隐藏软件名称和版本号 5. 防盗链优化 6. 禁止恶意域名解析 7. 禁止通过IP地址访问网站 ...

  3. golang学习之go简单博客应用

    先说说golang的语法吧,个人觉得有以下特点: 简洁,不管是变量.方法声明,还是代码编写,均十分简洁,效率也比较高 非纯粹面向对象,但是go的struct类似c的struct,go的结构体还可以进行 ...

  4. Heka 的编译

    Heka是Mozilla开源的,基于Go语言实现的,插件式log收集和分析系统. 已经编译好的 release 版本可以在下面地址下载: https://github.com/mozilla-serv ...

  5. spring实现可重置时间定时器

    此文章是基于 搭建Jquery+SpringMVC+Spring+Hibernate+MySQL平台 一. jar包介绍 1. spring-framework-4.3.4.RELEASE 的 lib ...

  6. java 生成和解析xml

    本文主要使用的是Jdom.jar包(包的下载百度一下)实现了生成xml文件和解析xml文件 下面是生成xml的实现 说明:stuLists集合是一个存放着Student对象的集合 import jav ...

  7. JS常见的几种数组去重方法

    总结一下JS中用到的数组去重的方法  方法一: 该方法利用对象的属性值不能相同: function arrDelLikeElement (array) { const result = []; con ...

  8. Python基础-月考

    1. 8<<2等于? # 解释:将8按位左移2位 # 8 0 0 0 0 1 0 0 0 # 32 0 0 1 0 0 0 0 0 2. 通过内置函数计算5除以2的余数 print(div ...

  9. Windows 8 Metro风格颜色表-Metro colours

    http://huaban.com/pins/538986818

  10. Android weex的集成和开发

    最近为了项目需要(实际上是为了年底KPI),领导要求用3天时间,学习并使用weex开发一个页面,说实话,压力山大.在这之前压根儿就没听说过啊,一脸懵逼 无奈之余只能Google了,惊喜的发现weex的 ...