手动实现一个简单的IOC容器
1.概述
IOC:有很多人把控制反转和依赖注入混为一谈,虽然在某种意义上来看他们是一体的,但好像又有些不同。
1. IOC(控制反转)是一个控制容器,DI(依赖注入)就是这个容器的运行机制。
2. IOC就是一种软件设计思想,DI是这种软件设计思想的一个实现。
关于Ioc的框架有很多,比如astle Windsor、Unity、Spring.NET、StructureMap,我们这边使用微软提供的Unity做示例,你可以使用 Nuget 添加 Unity ,
也可以引用Microsoft.Practices.Unity.dll和Microsoft.Practices.Unity.Configuration.dll。
2.代码演示
a. 先介绍常规做法,代码比较简单,稍微看一下:
public interface IBaseRepository { void DoSomething(); } public interface IRespository : IBaseRepository { } public interface IUserRepository : IBaseRepository { } public interface IShopRepository : IBaseRepository { } public class Repository : IRespository { public void DoSomething() { Console.WriteLine("I am a Repository"); } } public class UserRepository : IUserRepository { public void DoSomething() { Console.WriteLine("I am a UserRepository"); } } public class ShopRepository : IShopRepository { public void DoSomething() { Console.WriteLine("I am a ShopRepository"); } } public interface ITestService { void DoSomething(); } public class TestService : ITestService { private IRespository respository; /// <summary> /// 构造注入 /// </summary> /// <param name="_respository"></param> public TestService(IRespository _respository) { this.respository = _respository; } /// <summary> /// 属性注入 /// </summary> [Dependency] public IUserRepository UserRepository { get; set; } public IShopRepository ShopRepository { get; set; } /// <summary> /// 方法注入 /// </summary> /// <param name="_shopRepository"></param> [InjectionMethod] public void SetShopRepository(IShopRepository _shopRepository) { this.ShopRepository = _shopRepository; } public void DoSomething() { respository.DoSomething(); UserRepository.DoSomething(); ShopRepository.DoSomething(); } } class Program { static void Main(string[] args) { UnityContainer container = new UnityContainer(); container.RegisterType<IRespository, Repository>(); container.RegisterType<IUserRepository, UserRepository>(); container.RegisterType<IShopRepository, ShopRepository>(); container.RegisterType<ITestService, TestService>(); TestService testService = container.Resolve<ITestService>() as TestService; testService.DoSomething(); } }
b. 还有一种是配置文件做法
这个是配置文件:
<?xml version="1.0" encoding="utf-8"?> <configuration> <!--要保证configSections为configuration第一个节点--> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/> </configSections> <unity> <containers> <!--定义了一个名称为defaultContainer的Unity容器--> <container name="defaultContainer"> <register type="Demo.IRespository,Demo" mapTo="Demo.Respository, Demo"/> <register type="Demo.IUserRespository,Demo" mapTo="Demo.UserRespository, Demo"/> <register type="Demo.IShopRespository,Demo" mapTo="Demo.ShopRespository, Demo"/> <register type="Demo.ITestService,Demo" mapTo="Demo.TestService, Demo"/> </container> </containers> </unity> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> </startup> </configuration>
对应的代码也得改改:
InitializeComponent(); //创建容器 UnityContainer container = new UnityContainer(); UnityConfigurationSection configuration = ConfigurationManager.GetSection(UnityConfigurationSection.SectionName) as UnityConfigurationSection; configuration.Configure(container, "defaultContainer"); //通过Resolve<ITestService>方法返回的是一个类型为TestService的对象,该对象的三个属性被进行了有效的初始化。 //这个简单的程序分别体现了接口注入(通过相应的接口根据配置解析出相应的实现类型)、构造器注入(属性IRespository)、属性注入(属性IUserRepository)和方法注入(属性IShopRepository) TestService t = container.Resolve<ITestService>() as TestService ; if (null != t) { t.DoSomething(); }
3. Unity的生命周期
Unity有三种注入方式,构造,属性和方法注入,Unity注入也有自己的生命周期(默认瞬时生命周期:每次都是构造一个新的),下面就介绍下生命周期。
IUnityContainer container = new UnityContainer(); //默认瞬时生命周期,每次都是构造一个新的 container.RegisterType<IA, A>(new TransientLifetimeManager()); //每线程生命周期管理器,就是保证每个线程返回同一实例 container.RegisterType<IA, A>(new PerThreadLifetimeManager()); //容器控制生命周期管理,这个生命周期管理器是RegisterInstance默认使用的生命周期管理器,也就是单件实例,UnityContainer会维护一个对象实例的强引用,每次调用的时候都会返回同一对象 container.RegisterType<IA, A>(new ContainerControlledLifetimeManager()); //分层生命周期管理器,这个管理器类似于ContainerControlledLifetimeManager,也是由UnityContainer来管理,也就是单件实例,针对某个层单例 //不过与ContainerControlledLifetimeManager不同的是,这个生命周期管理器是分层的,因为Unity的容器时可以嵌套的,所以这个生命周期管理器就是针对这种情况,当使用了这种生命周期管理器,父容器和子容器所维护的对象的生命周期是由各自的容器来管理 container.RegisterType<IA, A>(new HierarchicalLifetimeManager()); //这个生命周期是为了解决循环引用而重复引用的生命周期 container.RegisterType<IA, A>(new PerResolveLifetimeManager()); //外部控制生命周期管理器,这个生命周期管理允许你使用RegisterType和RegisterInstance来注册对象之间的关系,但是其只会对对象保留一个弱引用,其生命周期交由外部控制,也就是意味着你可以将这个对象缓存或者销毁而不用在意UnityContainer,而当其他地方没有强引用这个对象时,其会被GC给销毁掉。 //在默认情况下,使用这个生命周期管理器,每次调用Resolve都会返回同一对象(单件实例),如果被GC回收后再次调用Resolve方法将会重新创建新的对象 container.RegisterType<IA, A>(new ExternallyControlledLifetimeManager());
4. 自己手动实现简单IOC容器
怎么自己实现一个简单的IOC容器呢?本次只实现一个简单的版本,生命周期只实现了三种,分别是瞬时,单例和线程单例。
a.首先定义一个生命周期的枚举 :
public enum LifeTimeType { /// <summary> /// 瞬时 /// </summary> Transient, /// <summary> /// 单例 /// </summary> Singleton, /// <summary> /// 线程单例 /// </summary> PerThread }
b. 定义一个保存注册映射信息的对象:
public class RegisterInfo { /// <summary> /// 目标类型 /// </summary> public Type TargetType { get; set; } /// <summary> /// 生命周期 /// </summary> public LifeTimeType LifeTime { get; set; } }
c. 定义三个Attribute,直接写一起了:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] public class DependencyAttribute : Attribute { public LifeTimeType _lifeTimeType { get; set; } public DependencyAttribute(LifeTimeType lifeTimeType = LifeTimeType.Transient) { this._lifeTimeType = lifeTimeType; } } [AttributeUsage(AttributeTargets.Constructor)] public class InjectionConstructorAttribute : Attribute { public InjectionConstructorAttribute() { } } [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class InjectionMethodAttribute : Attribute { private LifeTimeType _lifeTimeType { get; set; } public InjectionMethodAttribute(LifeTimeType lifeTimeType = LifeTimeType.Transient) { this._lifeTimeType = lifeTimeType; } }
d. 定义一个容器的接口:
public interface IIOCContainer { void RegisterType<TFrom, TTo>(LifeTimeType lifeTimeType = LifeTimeType.Transient); T Resolve<T>(); }
e. 实现该接口的方法,这是整个容器的关键代码,代码比较长,写的可能有错误,欢迎指正:
public class IOCContainer : IIOCContainer { /// <summary> /// 缓存注入的配置 /// </summary> private Dictionary<string, RegisterInfo> ContainerDictionary = new Dictionary<string, RegisterInfo>(); /// <summary> /// 缓存起来,类型的对象实例 /// </summary> private Dictionary<Type, object> TypeObjectDictionary = new Dictionary<Type, object>(); public void RegisterType<TFrom, TTo>(LifeTimeType lifeTimeType = LifeTimeType.Transient) { ContainerDictionary.Add(typeof(TFrom).FullName, new RegisterInfo() { TargetType = typeof(TTo), LifeTime = lifeTimeType }); } public T Resolve<T>() { RegisterInfo info = ContainerDictionary[typeof(T).FullName]; Type type = ContainerDictionary[typeof(T).FullName].TargetType; T result = default(T); result = (T)CreateTypeByRegisterType(info, type); return result; } private object CreateObject(Type type) { ConstructorInfo[] ctorArray = type.GetConstructors(); ConstructorInfo ctor = null; if (ctorArray.Count(c => c.IsDefined(typeof(InjectionConstructorAttribute), true)) > 0) { ctor = ctorArray.FirstOrDefault(c => c.IsDefined(typeof(InjectionConstructorAttribute), true)); } else { ctor = ctorArray.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault(); } List<object> paraList = new List<object>(); foreach (var parameter in ctor.GetParameters()) { Type paraType = parameter.ParameterType; RegisterInfo info = ContainerDictionary[paraType.FullName]; Type targetType = info.TargetType; object para = null; para = CreateTypeByRegisterType(info, targetType); //递归:隐形的跳出条件,就是GetParameters结果为空,targetType拥有无参数构造函数 paraList.Add(para); } object objType = Activator.CreateInstance(type, paraList.ToArray()); //属性注入 var properties = type.GetProperties() .Where(p => p.IsDefined(typeof(DependencyAttribute), false)).ToList(); foreach (var item in properties) { var customAttributes = item.GetCustomAttributes(typeof(DependencyAttribute), false) as DependencyAttribute[]; if (customAttributes != null) { Type t = item.PropertyType; RegisterInfo info = ContainerDictionary[t.FullName]; info.LifeTime = customAttributes.FirstOrDefault()._lifeTimeType; var value = CreateObject(info.TargetType); item.SetValue(objType, value); } } //字段注入 var filds = type.GetFields().Where(f => f.IsDefined(typeof(DependencyAttribute), false)).ToList(); foreach (var fild in filds) { var attribute = fild.GetCustomAttribute(typeof(DependencyAttribute)) as DependencyAttribute; if (attribute != null) { Type t = fild.DeclaringType; RegisterInfo info = ContainerDictionary[t.FullName]; info.LifeTime = attribute._lifeTimeType; var value = CreateObject(info.TargetType); fild.SetValue(objType, value); } } //方法注入 var methods = type.GetMethods().Where(m => m.IsDefined(typeof(InjectionMethodAttribute), false)).ToList(); List<object> paramrterList = new List<object>(); foreach (var item in methods) { var attribute = item.GetCustomAttribute(typeof(InjectionMethodAttribute)) as InjectionMethodAttribute; if (attribute != null) { var parameters = item.GetParameters(); foreach (var parameter in parameters) { var paraType = parameter.ParameterType; RegisterInfo info = ContainerDictionary[paraType.FullName]; Type targetType = info.TargetType; object para = CreateTypeByRegisterType(info, targetType); //递归:隐形的跳出条件,就是GetParameters结果为空,targetType拥有无参数构造函数 paramrterList.Add(para); } item.Invoke(objType, paramrterList.ToArray()); } } return objType; } private static readonly object obj = new object(); /// <summary> /// 根据注入类别创建对象 /// </summary> /// <param name="info"></param> /// <param name="targetType"></param> /// <returns></returns> private object CreateTypeByRegisterType(RegisterInfo info, Type targetType) { object para = null; switch (info.LifeTime) { case LifeTimeType.Transient: para = this.CreateObject(targetType); break; case LifeTimeType.Singleton: //需要线程安全 双if+lock if (para == null) { lock (obj) { if (this.TypeObjectDictionary.ContainsKey(targetType)) { para = this.TypeObjectDictionary[targetType]; } else { if (para == null) { para = this.CreateObject(targetType); this.TypeObjectDictionary[targetType] = para; } } } } break; case LifeTimeType.PerThread: //线程单例:线程槽,把数据存在这里 { string key = targetType.FullName; object oValue = CallContext.GetData(key); if (oValue == null) { para = this.CreateObject(targetType); CallContext.SetData(key, para); } else { para = oValue; } } break; default: throw new Exception("wrong LifeTime"); } return para; } }
以上代码经过测试,可以使用。
public interface IImitateScene { void DoSomeThing(); } public class ImitateScene: IImitateScene { private IUserService userService; public ImitateScene(IUserService _userService) { this.userService = _userService; } [Dependency(LifeTimeType.Transient)] public IShopService ShopService { get; set; } public IStoreService StoreService { get; set; } [InjectionMethod(LifeTimeType.Transient)] public void SetStoreService(IStoreService _storeService) { this.StoreService = _storeService; } public void DoSomeThing() { this.userService.DoSomeThing(); this.StoreService.DoSomeThing(); this.ShopService.DoSomeThing(); } }
附上项目图,项目结构只为了测试,未做过设计,嘿嘿
测试结果:
基本代码就在这里了,还有很多功能未完善,后续有时间会努力研究完善它,造造轮子。