一.   控制反转和依赖注入:

  1. 控制反转的前提, 是依赖倒置原则, 系统架构时,高层模块不应该依赖于低层模块,二者通过抽象来依赖 (依赖抽象,而不是细节)
  2. 如果要想做到控制反转(IOC), 就必须要使用依赖注入(DI), 也就是说控制反转是要实现的目的, 而依赖注入是达到这种目的的一种技术手段
  3. DI依赖注入, 在构造对象时,可以自动的去初始化当前需要构造的这个对象所需要的资源, 将其依赖的所有资源自动全部初始化, 有三种形式,  通过构造函数注入,   通过属性注入   通过方法注入(依赖注入的三种方式)
  4. 有了依赖注入,才可能做到无限层级的依赖抽象,才能做到控制反转

二.   一个简单的容器雏形, 部分代码示例:

  a)  这里相当于UI, 可以看做是高层

 //全部都依赖细节
AndroidPhone phone = new AndroidPhone(); //这边的细节类, 可以看做是低层
//左边依赖于抽象, 但是右边还是在依赖细节
IPhone phone = new AndroidPhone();
//左边依赖于抽象, 右边交给工厂
// 高层本来是依赖低层,但是可以通过工厂(容器)来决定细节,去掉了对低层的依赖, 这就是IOC控制反转:把高层对低层的依赖,转移到第三方决定,避免高层对低层的直接依赖(是一种目的)
IPhone phone = ObjectFactory.CreatePhone();//容器的雏形

  b)  工厂这里相当于第三方

 public class ObjectFactory
{
/// <summary>
/// 简单工厂+配置文件+反射
/// </summary>
/// <returns></returns>
public static IPhone CreatePhone()
{
string classModule = ConfigurationManager.AppSettings["iPhoneType"];
Assembly assemly = Assembly.Load(classModule.Split(',')[]);
Type type = assemly.GetType(classModule.Split(',')[]);
return (IPhone)Activator.CreateInstance(type);
}
}
//配置文件中的内容:
<appSettings>
<add key="iPhoneType" value=" MyIOCTest.Service.ApplePhone, MyIOCTest.Service" />
</appSettings>

三.   使用Unity容器实现IOC

  a)  最简单的容器使用, 给一个接口注册一种类型:

 //最简单的Unity的使用演示, 给一个接口注册一种类型
Console.WriteLine("*************** Unity容器的初步应用***************");
//1 声明一个容器
IUnityContainer container = new UnityContainer();
//2. 初始化容器 注册类型 告诉容器, 如果遇到IPhone, 就帮我创建一个AndroidPhone的实例出来
container.RegisterType<IPhone, AndroidPhone>();
//3 容器内部通过反射创建对象
IPhone phone = container.Resolve<IPhone>();
phone.Call();

  b) 容器生成实例的各种方法 :在Unity容器中, 使用别名创建不同的子类

 //容器的基本使用; 使用别名创建子类
Console.WriteLine("***************1. Unity容器的初步应用; 使用别名创建不同的子类***************");
IUnityContainer container = new UnityContainer(); //只要碰到IPhone这个接口, 都用AndroidPhone这个实例来生成
container.RegisterType<IPhone, AndroidPhone>();//接口 container.RegisterType<AbstractPad, ApplePad>();//抽象类 接口和抽象类,都可以被实例化
//container.RegisterType<AbstractPad, ApplePadChild>();//这个会覆盖上面的, 如果不想覆盖, 就要用别名 //container.RegisterType<ApplePad, ApplePadChild>();//父子类 会覆盖<AbstractPad, ApplePad> 因为这个也是AbstractPad //上面的写法会被覆盖, 所有如果一个接口想生成多个子类的实例, 那么就应该在创建类型的时候, 使用别名, 象下面这样; 在创建的时候使用字符串别名
container.RegisterType<AbstractPad, ApplePad>("child");//1对多, 一个抽象类对应多个子类
container.RegisterType<AbstractPad, ApplePadChild>("grandchild");//1对多 //只要碰到ITV在这个接口, 都使用AppleTV(123)来创建实例, 这种写法跟直接使用细节, 没有什么区别了.
container.RegisterInstance<ITV>(new AppleTV());//注册实例; 不常用,依赖细节 IPhone phone = container.Resolve<IPhone>();
AbstractPad pad = container.Resolve<AbstractPad>();
ApplePad applePad = container.Resolve<ApplePad>(); //调用方法; 调用的时候也要传入别名字符串, 要不会分不清创建的那个类实例
var childPad = container.Resolve<AbstractPad>("child");
var grandchildPad = container.Resolve<AbstractPad>("grandchild");
var tv = container.Resolve<ITV>();

  c) 多层依赖, 依次注入,下面的代码演示, 当需要构造一个ApplePhone的时候, 这个ApplePhone所需要的资源都会被依次的自动注入

i.  ApplePhone代码如下:

 /// <summary>
/// 1. 在构造本类的时候, 优先调用带有[InjectionConstructor]特性的构造函数
/// 2. 容器在完成构造函数之后, 会检查当前类的所有属性(public的), 如果当前属性带有[Dependency]这个特性, 容器就会对其进行构造
/// 3. 构造完 构造函数和属性 之后, 容器开始检查方法, 如果方法是public 且上面添加了[InjectionMethod]特性, 容器也会帮其构造出来
/// </summary>
public class ApplePhone : IPhone
{
[Dependency]//二. 属性注入: 对容器有依赖
public IMicrophone iMicrophone { get; set; }
public IHeadphone iHeadphone { get; set; }
public IPower iPower { get; set; } /// <summary>
/// 这个属性没有添加[Dependency]特性, 所以不会被构造
/// </summary>
public ITV iTV { get; set; } //[InjectionConstructor]
public ApplePhone()
{
Console.WriteLine("{0}构造函数", this.GetType().Name);
} //[InjectionConstructor]
//一. 构造函数注入:最好的, 在没有[InjectionConstructor] 特性的时候,默认找参数最多的构造函数;
public ApplePhone(IHeadphone headphone)
{
this.iHeadphone = headphone;
Console.WriteLine("{0}带参数构造函数", this.GetType().Name);
} public void Call()
{
Console.WriteLine("{0}打电话", this.GetType().Name);
} [InjectionMethod]//三. 方法注入(最不推荐的方法注入):最不好的,增加一个没有意义的方法,破坏封装
public void Init1234(IPower power)
{
this.iPower = power;
} /// <summary>
/// 如果一个方法不添加 [InjectionMethod]特性, 那么它在初始化的时候, 则不会被容器构造
/// </summary>
/// <param name="tV"></param>
public void InitITV(ITV tV)
{
this.iTV = tV;
}
}

    ii) 构造代码如下

 //多层依赖, 依次注入
{
//在构造对象时,可以自动的去初始化,对象需要的对象
//构造函数注入 属性注入 方法注入(依赖注入的三种方式)//多层的依赖注入, 必须逐层都要注入
//不管是构造对象,还是注入对象,这里都是靠反射做到的
Console.WriteLine("***************多层依赖, 依次注入***************");
IUnityContainer container = new UnityContainer();
container.RegisterType<IPhone, ApplePhone>();
container.RegisterType<IMicrophone, Microphone>();
container.RegisterType<IPower, Power>();
container.RegisterType<IHeadphone, Headphone>();
container.RegisterType<IBaseDAL, BaseDAL>();
IPhone phone = container.Resolve<IPhone>();
phone.Call();
}

  d) 生命周期管理; 不通过容器创建的对象, 在脱离作用域之后,这个时候没有任何引用的情况下, 就会被标记为垃圾, 等待GC回收; 在Unity中, 由于容器成了对象创建的入口, 所以可以加入自己对创建出来对象的管理逻辑;

 //使用Unity创建对象的时候, 默认每次创建都是一个全新的对象; 如果想要全局实现唯一(单例), 就要使用参数new ContainerControlledLifetimeManager()来让容器单例创建
IUnityContainer container = new UnityContainer();
container.RegisterType<IPhone, AndroidPhone>();//使用容器创建对象时, 默认的是瞬时生命周期, 也就是每次创建都是一个全新的对象
container.RegisterType<IPhone, AndroidPhone>(new TransientLifetimeManager());//Unity中默认创建的就是瞬时对象, 在构造的时候有没有 new TransientLifetimeManager() 都是一样
var phone1 = container.Resolve<IPhone>();
var phone2 = container.Resolve<IPhone>();
Console.WriteLine(object.ReferenceEquals(phone1, phone2)); //false; 创建的是两个不同的对象 //容器实现的单例
container.RegisterType<IPhone, AndroidPhone>(new ContainerControlledLifetimeManager());
var phone3 = container.Resolve<IPhone>();
var phone4 = container.Resolve<IPhone>();
Console.WriteLine(object.ReferenceEquals(phone3, phone4));//由于单例, 创建的是相同的对象 // 线程单例: 相同线程的实例相同, 不同线程的实例不同 应用场景:web请求 / 多线程操作
container.RegisterType<IPhone, AndroidPhone>(new PerThreadLifetimeManager()); IPhone iphone1 = null;
Action act1 = new Action(() =>
{ //使用单独的线程初始化IPhone1
iphone1 = container.Resolve<IPhone>();
Console.WriteLine($"iphone1由线程id={Thread.CurrentThread.ManagedThreadId}");
}); var result1 = act1.BeginInvoke(null, null); IPhone iphone2 = null;
Action act2 = new Action(() =>
{
//单独线程初始化iphone2
iphone2 = container.Resolve<IPhone>();
Console.WriteLine($"iphone2由线程id={Thread.CurrentThread.ManagedThreadId}");
}); IPhone iphone3 = null;
var result2 = act2.BeginInvoke(t => //BeginInvoke是回调线程, 这将会和上面的Action act2的线程属于同一个线程, 注意BeginInvoke是当当前线程完成后, 再用当前的线程来执行现在的任务; 也就是act2完成会后, 再执行这里面的
{
iphone3 = container.Resolve<IPhone>();
Console.WriteLine($"iphone3由线程id={Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine($"object.ReferenceEquals(iphone2, iphone3)={object.ReferenceEquals(iphone2, iphone3)}"); //结果为True
}, null); act1.EndInvoke(result1); //调用EndInvoke获得执行解脱
act2.EndInvoke(result2); Console.WriteLine($"object.ReferenceEquals(iphone1, iphone2)={object.ReferenceEquals(iphone1, iphone2)}");//结果为false //了解知识:
//关于单例还可以使用分级容器来创建; 不同子容器创建的单例属于不同的实例, 相同子容器创建的单例对象属于同一个实例对象
container.RegisterType<IPhone, AndroidPhone>(new HierarchicalLifetimeManager());//分级容器单例
//获取子容器
IUnityContainer childContainer = container.CreateChildContainer(); //外部可释放单例 单例是全局唯一;一旦释放大家都没了;
container.RegisterType<IPhone, AndroidPhone>(new ExternallyControlledLifetimeManager()); //当真的发生了循环使用可以使用这个来创建 不推荐; 不要使用;
container.RegisterType<IPhone, AndroidPhone>(new PerResolveLifetimeManager());

四.  摆脱细节, 使用配置文件实现IOC:

a) Unity.Config配置文件代码:

 <configuration>
<!--配置文件的路径到底应该放到哪个目录下: 配置文件最好是写到项目里, 而不是写到类库中; 始终复制-->
<!--当所有的引用都被移除之后, 注意一定要复制对应dll文件到当前程序运行目录下-->
<configSections>
<!--固定写法; -->
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
</configSections>
<unity>
<!--AOP扩展-->
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
<containers> <!--容器集合, 存放一个个container容器, 所有的容器名称不能重复-->
<container name="testContainer">
<!--type 使用逗号分隔, 前面是完整的接口名称, 后面是接口所在的dll文件名 -->
<!--mapTo 表示映射名称, 想生成那个类, 就在这里配置一下; 当前这一行是想利用IPhone来生成ApplePhone这个类; name="Android" 表示别名-->
<register type="MyIOCTest.Interface.IPhone, MyIOCTest.Interface" mapTo=" MyIOCTest.Service.ApplePhone, MyIOCTest.Service"/>
<register type=" MyIOCTest.Interface.IPhone, MyIOCTest.Interface" mapTo=" MyIOCTest.Service.AndroidPhone, MyIOCTest.Service" name="Android"/>
<register type=" MyIOCTest.Interface.IMicrophone, MyIOCTest.Interface" mapTo=" MyIOCTest.Service.Microphone, MyIOCTest.Service"/>
<register type=" MyIOCTest.Interface.IHeadphone, MyIOCTest.Interface" mapTo=" MyIOCTest.Service.Headphone, MyIOCTest.Service"/>
<register type=" MyIOCTest.Interface.IPower, MyIOCTest.Interface" mapTo=" MyIOCTest.Service.Power, MyIOCTest.Service"/>
<register type=" MyIOCTest.IDAL.IBaseDAL, MyIOCTest.IDAL" mapTo=" MyIOCTest.DAL.BaseDAL, MyIOCTest.DAL"/>
</container>
</containers>
</unity>
</configuration>

  b)  实现代码, 程序运行必须是依赖细节的, 但是编码过程又不想依赖细节, 所以只能通过配置文件:

  // ExeConfigurationFileMap  配置文件操作对象
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
//配置文件的路径到底应该放到哪个目录下: 配置文件最好是写到项目里, exe所在的目录, 而不是写到类库中; 注意始终复制
fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\Unity.Config");//找配置文件的路径
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName); IUnityContainer container = new UnityContainer();
section.Configure(container, "testContainer"); //testContainer 容器的名称
IPhone phone = container.Resolve<IPhone>();
phone.Call(); //使用别名构造
IPhone android = container.Resolve<IPhone>("Android");
android.Call(); //4.1 使用配置文件的时候, 不需要依赖细节类, 只需要引用接口的dll即可; 但是要注意的是, 还是必须要将细节类的 dll复制 到项目运行的根目录下的; 要不然就会报错

五.   AOP和IOC的组合使用:

  a)   配置文件代码:

 <!--Unity 5.8.6
Unity.Interception 5.5.3 配置文件中的dll名称改了
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
改为
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/> <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration"/>
改为
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
--> <configuration>
<!--配置文件的路径到底应该放到哪个目录下: 配置文件最好是写到项目里, 而不是写到类库中; 始终复制-->
<!--当所有的引用都被移除之后, 注意一定要复制对应dll文件到当前程序运行目录下-->
<configSections>
<!--固定写法; -->
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
</configSections>
<unity>
<!--AOP扩展-->
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
<containers> <!--容器集合, 里面放着一个个container容器-->
<!--这下面有一个个容器, 所有的容器名称不能重复--> <!--name表示容器的名字, 前端使用的时候要传入-->
<container name="testContainerAOP">
<extension type="Interception"/>
<register type=" MyIOCTest.Interface.IPhone, MyIOCTest.Interface" mapTo=" MyIOCTest.Service.AndroidPhone, MyIOCTest.Service.Extend">
<interceptor type="InterfaceInterceptor"/> <!--加入AOP扩展, 访问AndroidPhone的时候, 执行AOP-->
<!--权限认证-->
<interceptionBehavior type=" MyIOCTest.Framework.AOP.AuthorizeBehavior, MyIOCTest.Framework"/>
<!--发送短信-->
<interceptionBehavior type=" MyIOCTest.Framework.AOP.SmsBehavior, MyIOCTest.Framework"/>
<!--异常捕获-->
<interceptionBehavior type=" MyIOCTest.Framework.AOP.ExceptionLoggingBehavior, MyIOCTest.Framework"/>
<!--缓存-->
<interceptionBehavior type=" MyIOCTest.Framework.AOP.CachingBehavior, MyIOCTest.Framework"/>
<!--调用前写日志-->
<interceptionBehavior type=" MyIOCTest.Framework.AOP.LogBeforeBehavior, MyIOCTest.Framework"/>
<!--参数检查-->
<interceptionBehavior type=" MyIOCTest.Framework.AOP.ParameterCheckBehavior, MyIOCTest.Framework"/>
<!--调用后写日志-->
<interceptionBehavior type=" MyIOCTest.Framework.AOP.LogAfterBehavior, MyIOCTest.Framework"/>
</register>
<register type=" MyIOCTest.Interface.IPhone, MyIOCTest.Interface" mapTo=" MyIOCTest.Service.AndroidPhone, MyIOCTest.Service.Extend" name="Android"/>
<register type=" MyIOCTest.Interface.IMicrophone, MyIOCTest.Interface" mapTo=" MyIOCTest.Service.Microphone, MyIOCTest.Service.Extend"/>
<register type=" MyIOCTest.Interface.IHeadphone, MyIOCTest.Interface" mapTo=" MyIOCTest.Service.Headphone, MyIOCTest.Service.Extend"/>
<register type=" MyIOCTest.Interface.IPower, MyIOCTest.Interface" mapTo=" MyIOCTest.Service.Power, MyIOCTest.Service.Extend"/>
<register type=" MyIOCTest.IDAL.IBaseDAL, MyIOCTest.IDAL" mapTo=" MyIOCTest.DAL.BaseDAL, MyIOCTest.DAL">
</register>
</container>
</containers>
</unity>
</configuration>

  b)  调用示例:

 ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\Unity.Config");//找配置文件的路径
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName); IUnityContainer container = new UnityContainer();
section.Configure(container, "testContainerAOP"); //AOP的容器扩展
IPhone phone = container.Resolve<IPhone>();
phone.Call(); IPhone android = container.Resolve<IPhone>("Android");
android.Call();

20181123_控制反转(IOC)和依赖注入(DI)的更多相关文章

  1. 控制反转IOC与依赖注入DI

    理解 IOC  http://www.cnblogs.com/zhangchenliang/archive/2013/01/08/2850970.html IOC 相关实例      的http:// ...

  2. 控制反转(Ioc)和依赖注入(DI)

    控制反转IOC, 全称 “Inversion of Control”.依赖注入DI, 全称 “Dependency Injection”. 面向的问题:软件开发中,为了降低模块间.类间的耦合度,提倡基 ...

  3. iOS控制反转(IoC)与依赖注入(DI)的实现

    背景 最近接触了一段时间的SpringMVC,对其控制反转(IoC)和依赖注入(DI)印象深刻,此后便一直在思考如何使用OC语言较好的实现这两个功能.Java语言自带的注解特性为IoC和DI带来了极大 ...

  4. 轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI) 依赖注入和控制反转的理解,写的太好了。

    轻松学,浅析依赖倒置(DIP).控制反转(IOC)和依赖注入(DI) 2017年07月13日 22:04:39 frank909 阅读数:14269更多 所属专栏: Java 反射基础知识与实战   ...

  5. 控制反转IOC与依赖注入DI【转】

    转自:http://my.oschina.net/1pei/blog/492601 一直对控制反转.依赖注入不太明白,看到这篇文章感觉有点懂了,介绍的很详细. 1. IoC理论的背景我们都知道,在采用 ...

  6. 【转载】浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)

    原文地址 http://blog.csdn.net/briblue/article/details/75093382 写这篇文章的原因是这两天在编写关于 Dagger2 主题的博文时,花了大量的精力来 ...

  7. 控制反转IOC与依赖注入DI - 理论篇

    学无止境,精益求精 十年河东十年河西,莫欺少年穷 昨天是五一小长假归来上班的第一天,身体疲劳,毫无工作热情.于是就看看新闻,喝喝茶,荒废了一天 也就在昨天,康美同事张晶童鞋让我学习下IOC的理论及实现 ...

  8. 依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)

    原文: https://blog.csdn.net/briblue/article/details/75093382 写这篇文章的原因是这两天在编写关于 Dagger2 主题的博文时,花了大量的精力来 ...

  9. Spring框架学习笔记(1)——控制反转IOC与依赖注入DI

    Spring框架的主要作用,就是提供了一个容器,使用该容器就可以创建并管理对象.比如说Dao类等,又或者是具有多依赖关系的类(Student类中包含有Teacher类的成员变量) Spring有两个核 ...

随机推荐

  1. lxml.etree去除子节点

    去除etree中的某个子节点有两种方法: 1.parentnode.remove(node) 2.etree.strip_elements(html, 'element_name', with_tag ...

  2. adb 安装软件

    一.连接 adb connect 192.168.1.10 输出 connected to 二.查看设备 adb devices 输出 List of devices attached device ...

  3. python 随机选择字符串中的一个字符

    import random print(random.choice('abcdefghijklm'))

  4. JS学习笔记(模态框JS传参)

    博主最近基于django框架的平台第一版差不多完成了 今天整理下开发过程中遇到的前端知识 基于前端bootstrap框架模态框传参问题 上前端html代码: <div class="m ...

  5. python学习笔记(locust性能测试模块)

    locust是基于python的性能测试工具.支持python2.7及其以上的版本.相对于主流的LR与Jmeter工具使用的方式不一样.locust是通过编写python代码来完成性能测试的. 通过L ...

  6. HTML DOM知识点补充:

    DOM Console 控制台对象提供了浏览器的debug的方法支持. 常用的:console.log(). ⚠️coffeescript中,这个方法不加括号. DOM Document 当一个HTM ...

  7. PHP--------解决网址URL编码问题

    在PHP中有urlencode().urldecode().rawurlencode().rawurldecode()这些函数来解决网页URL编码解码问题. 理解urlencode: urlencod ...

  8. 微信小程序引入md5.js

    今天给大家安利一下微信小程序引入md5.js的方法,不多说 md5.js在下面 直接复制到项目的utils/md5.js即可 /* * A JavaScript implementation of t ...

  9. spring boot 之热部署

    热部署:当发现程序修改时自动启动应用程序. spring boot使用的是spring-boot-devtools是一个为开发者服务的一个模块.其原理用了classLoader 其中一个加载不变的类, ...

  10. oracle用户 密码永不过期

    ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED;