看了挺长时间框架搭建,今天看到一篇博客突然顿悟了依赖注入!
控制反转(Ioc)可以看成自来水厂,那自来水厂的运行就可以看作依赖注入(DI),Ioc是一个控制容器,DI就是这个容器的运行机制,有点像国家主席和总理的意思。
( Ioc 主要功能是提供各种方法,但是具体要提供什么样的方法就要靠 DI 依赖注入了。就像自来水厂虽然是提供水的,它控制着水在大众视野中的提供者,但是自来水厂的水却是靠自己的地下水或者是其他方提供的)
构造器注入(Constructor Injection):IoC容器会智能地选择选择和调用适合的构造函数以创建依赖的对象。如果被选择的构造函数具有相应的参数,IoC容器在调用构造函数之前解析注册的依赖关系并自行获得相应参数对象。
1 /// <summary> 2 /// 人接口 3 /// </summary> 4 public interface IPeople 5 { 6 void DrinkWater(); 7 } 8 /// <summary> 9 /// 村民 10 /// </summary> 11 public class VillagePeople : IPeople 12 { 13 IWaterTool _pw; 14 public VillagePeople(IWaterTool pw) 15 { 16 _pw = pw; 17 } 18 public void DrinkWater() 19 { 20 Console.WriteLine(_pw.returnWater()); 21 } 22 } 23 /// <summary> 24 /// 压水井 25 /// </summary> 26 public class PressWater : IWaterTool 27 { 28 public string returnWater() 29 { 30 return "地下水好甜啊!!!"; 31 } 32 } 33 /// <summary> 34 /// 获取水方式接口 35 /// </summary> 36 public interface IWaterTool 37 { 38 string returnWater(); 39 }
简单调用一下
1 static void Main(string[] args) 2 { 3 UnityContainer container = new UnityContainer();//创建容器 4 container.RegisterType<Test01.IWaterTool, Test01.PressWater>();//注册依赖对象 5 Test01.IPeople people = container.Resolve<Test01.VillagePeople>();//返回调用者 6 people.DrinkWater();//喝水 7 }
关于RegisterType和Resolve我们可以用自来水厂的例子来说明,请看下面:
- RegisterType:可以看做是自来水厂决定用什么作为水源,可以是水库或是地下水,我只要“注册”开关一下就行了。
- Resolve:可以看做是自来水厂要输送水的对象,可以是农村或是城市,我只要“控制”输出就行了。
属性注入(Property Injection):如果需要使用到被依赖对象的某个属性,在被依赖对象被创建之后,IoC容器会自动初始化该属性。
属性注入只需要在属性字段前面加[Dependency]标记就行了,如下:
1 /// <summary> 2 /// 村民 3 /// </summary> 4 public class VillagePeople02 : IPeople 5 { 6 [Dependency] 7 public IWaterTool _pw { get; set; } 8 public void DrinkWater() 9 { 10 Console.WriteLine(_pw.returnWater()); 11 } 12 }
调用方式和构造器注入一样,通过RegisterType<Test02.IWaterTool, Test02.PressWater>();注入就可以了,除了使用RegisterType方法注册,我们还可以在配置文件中注册,[Dependency]和RegisterType方式其实都会产生耦合度,我们要添加一个属性或是修改一中注册都会去修改代码,我们要做的就是代码不去修改,只要修改配置文件了,这个在下面有讲解,这边就不多说,我们先看下使用UnityConfigurationSection的Configure方法加载配置文件注册:
1 <unity> 2 <containers> 3 <container name="defaultContainer"> 4 <register type="UnityContainerDemo.IWaterTool,UnityContainerDemo" mapTo="UnityContainerDemo.PressWater,UnityContainerDemo"/> 5 <register type="UnityContainerDemo.IPeople,UnityContainerDemo" mapTo="UnityContainerDemo.VillagePeople02,UnityContainerDemo"/> 6 </container> 7 </containers> 8 </unity>
1 public static void FuTest02() 2 { 3 UnityContainer container = new UnityContainer();//创建容器 4 UnityConfigurationSection configuration = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName); 5 configuration.Configure(container, "defaultContainer"); 6 IPeople people = container.Resolve<IPeople>();//返回调用者 7 people.DrinkWater();//喝水 8 }
方法注入(Method Injection):如果被依赖对象需要调用某个方法进行相应的初始化,在该对象创建之后,IoC容器会自动调用该方法。
方法注入和属性方式使用一样,方法注入只需要在方法前加[InjectionMethod]标记就行了,从方法注入的定义上看,只是模糊的说对某个方法注入,并没有说明这个方法所依赖的对象注入,所依赖的对象无非就三种:参数、返回值和方法内部对象引用,我们做一个示例试下:
1 /// <summary> 2 /// 村民 3 /// </summary> 4 public class VillagePeople03 : IPeople 5 { 6 public IWaterTool tool;//我是对象引用 7 public IWaterTool tool2;//我是参数 8 public IWaterTool tool3;//我是返回值 9 [InjectionMethod] 10 public void DrinkWater() 11 { 12 if (tool == null) 13 { } 14 } 15 [InjectionMethod] 16 public void DrinkWater2(IWaterTool tool2) 17 { 18 this.tool2 = tool2; 19 } 20 [InjectionMethod] 21 public IWaterTool DrinkWater3() 22 { 23 return tool3; 24 } 25 }
1 public static void FuTest03() 2 { 3 UnityContainer container = new UnityContainer();//创建容器 4 UnityConfigurationSection configuration = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName); 5 configuration.Configure(container, "defaultContainer"); 6 VillagePeople03 people = container.Resolve<IPeople>() as VillagePeople03;//返回调用者 7 Console.WriteLine("people.tool == null(引用) ? {0}", people.tool == null ? "Yes" : "No"); 8 Console.WriteLine("people.tool2 == null(参数) ? {0}", people.tool2 == null ? "Yes" : "No"); 9 Console.WriteLine("people.tool3 == null(返回值) ? {0}", people.tool3 == null ? "Yes" : "No"); 10 }
container.Resolve<IPeople>() as VillagePeople03;其实多此一举,因为已经在配置文件注册过了,不需要再进行转化,这边只是转化只是方便访问VillagePeople03对象的几个属性值
除了我们上面使用RegisterType和Resolve泛型方法,我们也可以使用非泛型注入,代码如下:
1 public static void FuTest04() 2 { 3 UnityContainer container = new UnityContainer();//创建容器 4 container.RegisterType(typeof(IWaterTool), typeof(PressWater));//注册依赖对象 5 IPeople people = (IPeople)container.Resolve(typeof(VillagePeople01));//返回调用者 6 people.DrinkWater();//喝水 7 }
为了实现单例模式,我们通常的做法是,在类中定义一个方法如GetInstance,判断如果实例为null则新建一个实例,否则就返回已有实例。但是我觉得这种做法将对象的生命周期管理与类本身耦合在了一起。所以我觉得遇到需要使用单例的地方,应该将生命周期管理的职责转移到对象容器Ioc上,而我们的类依然是一个干净的类,使用Unity创建单例代码:
1 public static void FuTest07() 2 { 3 UnityContainer container = new UnityContainer();//创建容器 4 container.RegisterType<IWaterTool, PressWater>(new ContainerControlledLifetimeManager());//注册依赖对象 5 IPeople people = container.Resolve<VillagePeople01>();//返回调用者 6 people.DrinkWater();//喝水 7 }
上面演示了将IWaterTool注册为PressWater,并声明为单例,ContainerControlledLifetimeManager字面意思上就是Ioc容器管理声明周期,我们也可以不使用类型映射,将某个类注册为单例:
1 container.RegisterType<PressWater>(new ContainerControlledLifetimeManager());
除了将类型注册为单例,我们也可以将已有对象注册为单例,使用RegisterInstance方法,示例代码:
1 PressWater pw = new PressWater(); 2 container.RegisterInstance<IWaterTool>(pw);
上面的代码就表示将PressWater的pw对象注册到Ioc容器中,并声明为单例。
如果我们在注册类型的时候没有指定ContainerControlledLifetimeManager对象,Resolve获取的对象的生命周期是短暂的,Ioc容器并不会保存获取对象的引用,就是说我们再次Resolve获取对象的时候,获取的是一个全新的对象,如果我们指定ContainerControlledLifetimeManager,类型注册后,我们再次Resolve获取的对象就是上次创建的对象,而不是再重新创建对象,这也就是单例的意思。
这种配置都会产生耦合度,比如添加一个属性注入或是方法注入都要去属性或是方法前加[Dependency]和[InjectionMethod]标记,我们想要的依赖注入应该是去配置文件中配置,当系统发生变化,我们不应去修改代码,而是在配置文件中修改,这才是真正使用依赖注入解决耦合度所达到的效果,先看下Unity完整的配置节点:
1 <?xml version="1.0"?> 2 <configuration> 3 <configSections> 4 <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, 5 Microsoft.Practices.Unity.Configuration" /> 6 </configSections> 7 <typeAliases> 8 <!--寿命管理器类型--> 9 <typeAlias alias="singleton" type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager,Microsoft.Practices.Unity" /> 10 <typeAlias alias="external" type="Microsoft.Practices.Unity.ExternallyControlledLifetimeManager, Microsoft.Practices.Unity" /> 11 <!--用户定义的类型别名--> 12 <typeAlias alias="IMyInterface" type="MyApplication.MyTypes.MyInterface, MyApplication.MyTypes" /> 13 <typeAlias alias="MyRealObject" type="MyApplication.MyTypes.MyRealObject, MyApplication.MyTypes" /> 14 <typeAlias alias="IMyService" type="MyApplication.MyTypes.MyService, MyApplication.MyTypes" /> 15 <typeAlias alias="MyDataService" type="MyApplication.MyTypes.MyDataService, MyApplication.MyTypes" /> 16 <typeAlias alias="MyCustomLifetime" type="MyApplication.MyLifetimeManager, MyApplication.MyTypes" /> 17 </typeAliases> 18 <unity> 19 <containers> 20 <container name="containerOne"> 21 <types> 22 <!--类型映射无一生-默认为“瞬时”--> 23 <type type="Custom.MyBaseClass" mapTo="Custom.MyConcreteClass" /> 24 <!--使用上面定义的别名类型的映射--> 25 <type type="IMyInterface" mapTo="MyRealObject" name="MyMapping" /> 26 <!--使用类型别名指定的终身--> 27 <type type="Custom.MyBaseClass" mapTo="Custom.MyConcreteClass"> 28 <lifetime type="singleton" /> 29 </type> 30 <type type="IMyInterface" mapTo="MyRealObject" name="RealObject"> 31 <lifetime type="external" /> 32 </type> 33 <!--使用完整的类型名指定终身经理--> 34 <!--的一生经理指定的任何初始化数据--> 35 <!--将要使用的默认类型转换器转换--> 36 <type type="Custom.MyBaseClass" mapTo="Custom.MyConcreteClass"> 37 <lifetime value="sessionKey" type="MyApplication.MyTypes.MyLifetimeManager,MyApplication.MyTypes" /> 38 </type> 39 <!--使用一个自定义TypeConverter的终身管理器初始化--> 40 <type type="IMyInterface" mapTo="MyRealObject" name="CustomSession"> 41 <lifetime type="MyCustomLifetime" value="ReverseKey" typeConverter="MyApplication.MyTypes.MyTypeConverter,MyApplication.MyTypes" /> 42 </type> 43 <!--对象在配置中定义的注入参数--> 44 <!--使用上面定义的别名类型的映射--> 45 <type type="IMyService" mapTo="MyDataService" name="DataService"> 46 <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration"> 47 <constructor> 48 <param name="connectionString" parameterType="string"> 49 <value value="AdventureWorks"/> 50 </param> 51 <param name="logger" parameterType="ILogger"> 52 <dependency /> 53 </param> 54 </constructor> 55 <property name="Logger" propertyType="ILogger" /> 56 <method name="Initialize"> 57 <param name="connectionString" parameterType="string"> 58 <value value="contoso"/> 59 </param> 60 <param name="dataService" parameterType="IMyService"> 61 <dependency /> 62 </param> 63 </method> 64 </typeConfig> 65 </type> 66 </types> 67 68 <instances> 69 <add name="MyInstance1" type="System.String" value="Some value" /> 70 <add name="MyInstance2" type="System.DateTime" value="2008-02-05T17:50:00" /> 71 </instances> 72 73 <extensions> 74 <add type="MyApp.MyExtensions.SpecialOne" /> 75 </extensions> 76 77 <extensionConfig> 78 <add name="MyExtensionConfigHandler" type="MyApp.MyExtensions.SpecialOne.ConfigHandler" /> 79 </extensionConfig> 80 </container> 81 </containers> 82 </unity> 83 <startup> 84 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> 85 </startup> 86 </configuration>
配置过unity的朋友看一下可能就清楚,这边我们再简单说下:
- Unity的配置节的名称为”Unity",节处理程序的类型为 Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,它包含在程序集Microsoft.Practices.Unity.Configuration 中,当前程序添加该程序集的引用。
- typeAliases管理生命周期类型,以及一些类型别名的设置,方便我们映射对象的编写,比如同一个类型注册多次,我们只要在typeAlias添加一个类型别名,这样我们再添加这个类型映射的时候只要写个别名就可以了。
- containers是容器container集合,我们可以配置多个容器类型,通过Name属性就可以访问,比如访问defaultContainer容器代码:configuration.Configure(container, "defaultContainer");
- container为容器管理,下面包含多个类型映射,我们平常使用的构造器注册、属性注册和方法注册,就可以在constructor、property、method节点进行配置。
资料来源 http://www.cnblogs.com/xishuai/p/3670292.html#xishuai_h8
原文地址:https://www.cnblogs.com/cwmizlp/p/9445316.html