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