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)) > )
{
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();
}
}

附上项目图,项目结构只为了测试,未做过设计,嘿嘿

测试结果:

基本代码就在这里了,还有很多功能未完善,后续有时间会努力研究完善它,造造轮子。

浅聊IOC的更多相关文章

  1. 浅聊ARP

    今天借用思科公司的Cisco Packet Tracer Student这款软件浅聊ARP 什么是ARP? ARP即地址解析协议(Address Resolution Protocol),是根据Ip地 ...

  2. 浅谈IOC

    一.引言 IOC-Invertion of Control,即控制反转,是一种程序设计思想,世上本没有路,走的人多了便有了路,本文将一步步带你了解IOC设计思想的演进之路. 在学习IOC之前我们先初步 ...

  3. 浅谈(IOC)依赖注入与控制反转(DI)

    前言:参考了百度文献和https://www.cnblogs.com/liuqifeng/p/11077592.html以及http://www.cnblogs.com/leoo2sk/archive ...

  4. require.js(浅聊)

    一.require 了解requirejs之前首先明白什么是模块化: 1.什么是模块化? 模块化设计是指在对一定范围内的不同功能或相同功能不同性能.不同规格的产品进行功能分析的基础上,划分并设计出一系 ...

  5. mysql 系统性浅聊 myisam 存储引擎【原创】

    >>思维导图 >>介绍 mysql中的存储引擎都是以插件的形式存在,目前用的最多存储引擎就是innodb和myisam.MySQL5.5.5以后(包括5.5.5)默认使用Inn ...

  6. 浅聊本人学习React的历程——第一篇生命周期篇

    作为一个前端小白,在踏入前端程序猿行业的第三年接触了React,一直对于框架有种恐惧感,可能是对陌生事物的恐惧心里吧,导致自己一直在使用原生JS和JQ作为开发首选,但是在接触了React之后,发现了其 ...

  7. Sort排序浅聊

    集合是什么?笔者简易描述:数组是不可变的,所以我们使用集合来代替. using.System.Collections; 非泛型集合 using.System.Collections.Gernerc;泛 ...

  8. 浅聊几种主流Docker网络的实现原理

    原文:https://mp.weixin.qq.com/s/Jdxct8qHrBUtkUq-hnxSRw 参考:https://blog.csdn.net/yarntime/article/detai ...

  9. 浅聊标签<include>和<viewStub>

    在开发中我们往往会遇到这种情况,当一个布局文件比较复杂时,我们一个劲地往里面拖各种控件button,textView,imageView阿等等,等过了一段时间后,出现bug,自己都把自己搞懵比啦,特别 ...

随机推荐

  1. PHP的file_get_contents()方法,将整个文件读入字符串中

    <?php $post_data = file_get_contents("e:\\1.txt"); echo $post_data; ?> 更多信息看这里:http: ...

  2. Redis学习笔记--常用命令

    以下为本人学习Redis的备忘录,记录了大部分常用命令 1.客户端连接redis服务端: ===启动Redis服务端 redis-server /yourpath/redis.conf ===启动Re ...

  3. T-SQL语句创建表

    USE E_Market          --指定当前所操作的数据库 GO CREATE TABLE 表名 ( BID int identity (1,1)NOT NULL, BNAME varch ...

  4. 删除trigger 禁用触发器 启用触发器 查看触发器

    删除trigger drop trigger 触发器名 on 在那个表上 禁用触发器 Alter Table 表名 disable trigger 触发器名 启用触发器 Alter Table 表名 ...

  5. 2.Windows服务-->安装卸载服务

    1.使用vs组件“VS2012开发人员命令提示” 工具,进行安装卸载服务(必须以“管理员身份运行") 安装和卸载的时候选择合适的安装程序工具地址,例如: 安装服务:C:\Windows\Mi ...

  6. java 类与对象基础整理

    之前学习javaSE的时候,没有针对性地对对类与对象的一些基础进行整理,下面这些内容是笔记内容整理后的,希望以后自己可以通过这些博客时常复习! 一.类and对象的基础 类似于类的生命啊,类与对象的关系 ...

  7. 设计模式入门,命令模式,c++代码实现

    // test06.cpp : Defines the entry point for the console application.////设计模式第5章 命令模式#include "s ...

  8. docker 容器启动并自启动redis

    centos7.5 背景:每次开机后都要自动启动redis,也就是宿主机开机,启动容器,然后启动redis 按照网上的做法是:修改redis.conf ,修改redis的启动脚本(utils/...s ...

  9. Bzoj3510:首都

    Sol \(LCT\)动态维护树重心 方法一 因为只有加边,所以可以暴力启发式合并,维护重心 维护子树信息,子树大小不超过一半 复杂度两只\(log\) 方法二 扣出两个重心的链,链上二分找 每次\( ...

  10. Android 自定义ScrollView(具有反弹效果的ScrollView,能够兼容横向的滑动)

    package com.itau.jingdong.widgets; import android.content.Context; import android.graphics.Rect; imp ...