Unity框架的依赖注入Dependency injection(DI)
发表于2018-11-13
如果大家是第一次接触依赖注入这个概念,可能会不了解如何去使用它,其实只要知道依赖注入是一种方式、方法或者说手段,是让被注入者和注入者之间建立关联的手段。依赖注入的目的是松耦合,是交互对象之间的松耦合。了解这些之后,大家在掌握依赖注入就会非常容易了。
一、构造注入 Constructors injection
构造注入适用于对象强依赖的情况,需要在构造函数中实例化别外一个类型,以控制对象的实例化顺序。已经存在的实例是不能使用构造注入,即不能使用构造注入改变实例属性。
以下情况适合使用构造注入
- 在实例化父对象时自动实例化子对象
- 想用一个简单的方法表示代码是类的依赖关系
- 父对象有能在太多的构造函数
- 父对象的构造函数不能有太多的参数
- 需要隐藏对象内部字段的值,而用属性或方法会使其显露
- 要控制父对象依赖的子对象,需要将依赖从代码中移出时
Unity建议当不确定使用哪种注入时,使用构造注入,除非是要在一个已有的对象实例改变属性时才使用其它注入。
DEMO:
namespace ConsoleUnityDemo.ConstractrInjection { public class Parent { private SubClass subClass; public Parent(SubClass subInstance) { subClass = subInstance; } public string SubClassName { get {return subClass.ClassName;} } } public class SubClass { public string ClassName{get;set;} } public class SubClassConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(string)) return true; return base.CanConvertFrom(context, sourceType); } public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { return base.ConvertTo(context, culture, value, destinationType); } public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { if (value is string) { SubClass model = new SubClass(); model.ClassName = value.ToString(); return model; } return base.ConvertFrom(context, culture, value); } } class Program { static void Main(string[] args) { UnityConfigurationSection unitySection = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); UnityContainer unityContainer = new UnityContainer(); unitySection.Configure(unityContainer, "ConstractInject"); //获取容器 ConstractrInjection.Parent parent = unityContainer.Resolve<ConstractrInjection.Parent>("ParentClass"); //取实例 Console.WriteLine(string.Format(parent.SubClassName)); } } }
Unity配置
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/> </configSections> <unity xmlns="http://schemas.microsoft.com/practices/2010/unity"> <alias alias="Parent" type="ConsoleUnityDemo.ConstractrInjection.Parent, ConsoleUnityDemo" /> <alias alias="SubClass" type="ConsoleUnityDemo.ConstractrInjection.SubClass, ConsoleUnityDemo" /> <alias alias="SubClassConverter" type="ConsoleUnityDemo.ConstractrInjection.SubClassConverter, ConsoleUnityDemo" /> <container name="ConstractInject"> <register type="Parent" name="ParentClass" mapTo ="Parent"> <constructor> <param name="subInstance"> <value value="This is SubClass" typeConverter="SubClassConverter"/> </param> </constructor> </register> </container> </unity> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <appSettings> </appSettings> </configuration>
构造注入存在以下三种情况:
1、单个构造函数时自动注入
当对象只有一个构造函数,Unity会自动实例化构造函数参数所依赖的对象,参数必需是个实体类,不能为接口或抽象类。如
public class BoxA { public BoxA(BoxB box) { } }
使用Unity注入时会自动实例化BoxB,在上面的Demo中可以不使用<constructor>节点
2、当参数为抽象类或接口时,可以用[Dependency("objectName")]来指定所依赖的对象,Unity在构造MyObject对象时会自动将参数映射到DataServices对象
public class MyObject { public MyObject([Dependency("DataService")] IMyService myDataService) { // work with the service here } }
3、当有多个构造函数,且构造函数的参数个数一样时,使用InjectionConstructor属性指定Unity默认调用的构造函数。Unity对实例化对象时会自动调用标记为InjectionConstructor的构造函数
public class MyObject { public MyObject(SomeOtherClass myObjA) { ... } [InjectionConstructor] public MyObject(MyDependentClass myObjB) { ... } }
二、属性注入Sitter injection
属性注入通入对象公开的属性改变对象的值,比如记录日志时常用的方式,ILogger抽象了日志实体,再根据需要实例化不同的日志实体
public class LogManager { ILogger logInstance; [Dependency] public ILoger { get{return logInstance;} set{logInstance= value;} } public void WriteLog() { logInstance.WriteLog(); } }
用Dependency属性向Unity暴露这是一个依赖注入的属性。
属性注入注意时应该始终在要依赖注入的属性上加上Dependency 标签,当然不加也是可以使用的。
三、方法注入Method Call Injection
属性注入时必需要将对象内部的字段对外公开,但某些时候为了封装原则,不想对外公开这些属性时,可以用方法注入。如用Initialize方法代码Set属性
public class LogManager { ILogger logInstance; [InjectionMethod] public void Initialize(ILogger instance) { logInstance = instance } public void WriteLog() { logInstance.WriteLog(); } }