几句代码简单实现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 != )
{
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容器的更多相关文章
- IoC原理-使用反射/Emit来实现一个最简单的IoC容器
从Unity到Spring.Net,到Ninject,几年来陆陆续续用过几个IoC框架.虽然会用,但也没有一直仔细的研究过IoC实现的过程.最近花了点时间,下了Ninject的源码,研究了一番,颇有收 ...
- 【最简单IOC容器实现】实现一个最简单的IOC容器
前面DebugLZQ的两篇博文: 浅谈IOC--说清楚IOC是什么 IoC Container Benchmark - Performance comparison 在浅谈IOC--说清楚IOC是什么 ...
- 比Spring简单的IoC容器
比Spring简单的IoC容器 Spring 虽然比起EJB轻量了许多,但是因为它需要兼容许多不同的类库,导致现在Spring还是相当的庞大的,动不动就上40MB的jar包, 而且想要理解Spring ...
- 手写一个最简单的IOC容器,从而了解spring的核心原理
从事开发工作多年,spring源码没有特意去看过.但是相关技术原理倒是背了不少,毕竟面试的那关还是得过啊! 正所谓面试造火箭,工作拧螺丝.下面实现一个最简单的ioc容器,供大家参考. 1.最终结果 2 ...
- .NET实现一个简单的IOC容器
目录 1.主要细节 2.具体示例 参考及示例代码下载 shanzm-2020年3月17日 20:06:01 1.主要细节 使用反射程序集的方式获取对象的类型 通过反射的方式获取指定类型的的所有公共属性 ...
- 自己动手实现一个简单的 IOC容器
控制反转,即Inversion of Control(IoC),是面向对象中的一种设计原则,可以用有效降低架构代码的耦合度,从对象调用者角度又叫做依赖注入,即Dependency Injection( ...
- IL实现简单的IOC容器
既然了解了IL的接口和动态类之间的知识,何不使用进来项目实验一下呢?而第一反应就是想到了平时经常说的IOC容器,在园子里搜索了一下也有这类型的文章http://www.cnblogs.com/kkll ...
- 最简单的ioc容器代码(低仿Spring )
Spring 的一大核心就是IOC,控制反转(依赖注入). 对象交由容器去控制,降低耦合性. Spring 的ioc实现原理其实很简单,容器启动后读取并解析配置文件,根据配置文件中<bean&g ...
- 简单模拟IOC容器:返回对象并能抛出异常
本次要求:已知com.zzj.vo包下分别有Tiger.lion.Elephant三个Java源文件,请据此实现以下功能:①.自定义一个名为Component的注解,要求该注解只能用于类且代码运行时该 ...
随机推荐
- java 错误: 未报告的异常错误Exception; 必须对其进行捕获或声明以便抛出
import java.io.FileInputStream; import java.util.Properties; import java.sql.Connection; import java ...
- div居中方式
1. position: absolute; top:50%:left: 50%; margin-top: -高度的一半; margin-left: -宽度的一半(此方法适用于固定宽高的元素) 注: ...
- MVC4.0 oracle 找不到请求的 .Net Framework Data Provider。可能没有安装.
oracle 11G, MVC4.0 项目,因刚重装系统,重新安装的VS2010, ORACLE 11G 运行项目,后报错 找不到请求的 .Net Framework Data Provider.可能 ...
- vue学习笔记(一)
一.MVC 和 MVVM 的区别 MVC: Model(模型)应用程序中用于处理应用程序数据逻辑的部分(通常模型对象负责在数据库中存取数据). View(视图)显示数据(通常视图是依据模型数据创建的) ...
- Java集合排序
[ 1.对普通的包装类基本数据类型的list数组排序(Integer,Long,Double) ] Collections.sort(List list) [例] List<Long> m ...
- sharePoint查看与更改用户登录账号
PS D:\deployScript> $user=(Get-SPUser -IDENTITY "i:0e.t|xmssts|zhangshan" -Web http://t ...
- switch case 忘记 break
昨天纠结了很久的问题,一直找不到原因,早上发现是一个低级错误: private void selectButton(int id) { switch (id) { case 0: recommend_ ...
- idea 多项目部署JRebel
碰到个问题,Jrebel热部署Idea,两个项目一个可以热部署,一个不行.刚开始发现是其中一个项目没有添加JRebel,于是加上 然而发现还是不行,继续google, 在一篇文章里面发现如下的内容: ...
- 二进制中 1 的个数(C++ 和 Python 实现)
(说明:本博客中的题目.题目详细说明及参考代码均摘自 “何海涛<剑指Offer:名企面试官精讲典型编程题>2012年”) 题目 请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个 ...
- png的故事:隔行扫描算法
转载自AlloyTeam:http://www.alloyteam.com/2017/06/the-story-of-png-deinterlacing-algorithm/ 前言 前文已经讲解过如何 ...