Prism框架中的Ioc容器
背景
我们知道在Prism
框架中,框架中最重要的一个组件就是依赖注入框架,这个框架在一定程度上能够通过一个容器去管理整个框架中所有类的对象及生命周期,并且在引用的时候只需要通过注入接口框架就能够自动根据接口类型找到特定的实例,这个会省掉大量的创建对象操作,而且在在软件设计过程中通过IOC容器实现依赖注入能够最大程度上实现最终的控制反转,从而保证软件设计的时候巨大灵活性和扩展性,几乎在现在所有大型软件设计中都包含这个思想,所以说项目中使用什么类型的IOC容器能够决定代码的设计的效率以及代码的灵活性,今天通过这篇文章我们来一起了解下在Prism框架中是怎么使用IOC容器的,后面我们重点来分析下这种设计代码的思想从而为以后的代码设计提供思路和借鉴,如果对IOC内部实现还有疑惑可以通过我之前写的一篇MinIoc文章来了解一下IOC容器内部实现的一些关键内容。
代码分析
按照之前解析代码的思路,我们还是先来看一看在Prism.Core中定义的IOC容器相关的类图,通过这个类图首先对框架中的概念有一个大题上面的认知。
1 类图
通过分析这个类图我们发现Prism.Core中主要是定义一些基础的接口,这些接口主要为了定义一种规范,这个其实也很好理解Prism.Core中并不会定义某一种具体的实现,这些IOC接口的实现应该是具体实现中做的事情,因为子类会很方便的进行扩展,特别是现在有很多的IOC框架可以供我们进行选择,我们知道在Prism 8.x版本以后,Prism框架提供了两种具体的IOC依赖注入框架,分别是DryIOC和Unity,这个我们会在稍后的部分进行解读。我们这里先来解析这几个接口的定义从而对整个体系上面有一个清晰的认知。
2 接口分析
2.1 IContainerRegistry接口
这个接口的作用主要是通过将一系列的类型和对应的实体注入到容器里面,从而方便我们在使用的时候通过类型查找到注入的对象实体,当然这个接口中会定义各种类型的注入方式,这里主体包括几个部分:1 Register、2 RegisterSingleton、3 RegisterScoped,这几个用来定义当前定义的注册对象的生命周期,这个是IOC容器注册的永恒话题,另外这个接口中还定义了IsRegistered方法用来判断某个对象是否已经进行过注册。
我们先来看一下任意一个方法的定义
/// <summary>
/// Registers an instance of a given <see cref="Type"/>
/// </summary>
/// <param name="type">The service <see cref="Type"/> that is being registered</param>
/// <param name="instance">The instance of the service or <see cref="Type" /></param>
/// <returns>The <see cref="IContainerRegistry" /> instance</returns>
IContainerRegistry RegisterInstance(Type type, object instance);
- 接口方法返回值
这里有个关键点就是定义的接口返回类型都是
IContainerRegistry
,这样定义接口方法的返回值有什么好处?其实这样做的好处就是能够连续进行类型注册,这样能够让代码的结构更加灵活代码也显得更加规范。
- RegisterMany方法
对于这个接口其它方法我们都能够快速理解其意图但是RegisterMany方法我们不能够很快的推测其意图,下面我们通过框架中的单元测试来进行说明,我们先来看看RegisterMany这个方法的单元测试。
[Fact]
public void RegisterWithManyInterfaces()
{
var mock = new Mock<IContainerRegistry>();
mock.Setup(x => x.RegisterMany(It.IsAny<Type>(), It.IsAny<Type[]>()))
.Returns(mock.Object);
var services = new[] { typeof(ITestService), typeof(ITest2Service) };
var cr = mock.Object.RegisterMany<TestService>(services);
Assert.Same(mock.Object, cr);
mock.Verify(x => x.RegisterMany(typeof(TestService), services));
}
我们再来看看这个TestService和ITestService和ITest2Service之间的关系:
private interface ITestService { }
private interface ITest2Service { }
private class TestService : ITestService, ITest2Service { }
也就是说这个接口用于注册一个对象从多个接口继承的情况,这个注册的好处就是后面我们无论从其中哪一个接口作为类型都能够获取到当前的TestService对象,这样就能够实现最大程度的灵活性。
- RegisterSingle的用法
注册单例我们也通过几个单元测试来说明一下,下面通过注册一个具体类型和通过注册一个泛型类型来分别进行演示其具体的用法。
[Fact]
public void RegisterFromConcreteType()
{
var mock = new Mock<IContainerRegistry>();
mock.Setup(x => x.Register(typeof(TestService), typeof(TestService)))
.Returns(mock.Object);
var cr = mock.Object.Register(typeof(TestService));
Assert.Same(mock.Object, cr);
mock.Verify(x => x.Register(typeof(TestService), typeof(TestService)));
}
[Fact]
public void RegisterFromGenericConcreteType()
{
var mock = new Mock<IContainerRegistry>();
mock.Setup(x => x.Register(typeof(TestService), typeof(TestService)))
.Returns(mock.Object);
var cr = mock.Object.Register<TestService>();
Assert.Same(mock.Object, cr);
mock.Verify(x => x.Register(typeof(TestService), typeof(TestService)));
}
2.2 IContainerProvider接口
这个接口和IContainerRegistry接口的作用刚好相反,主要是通过刚才具体的类型从具体的容器中找到之前注册的对象。这个里面我们重点关注的是在这个接口中定义的IScopeProvider对象,具体我们来看下在接口中这个部分的定义。
/// <summary>
/// Creates a new scope
/// </summary>
IScopedProvider CreateScope();
/// <summary>
/// Gets the Current Scope
/// </summary>
IScopedProvider CurrentScope { get; }
这个IScopedProvider接口是如何定义的?
/// <summary>
/// Defines a Container Scope
/// </summary>
public interface IScopedProvider : IContainerProvider, IDisposable
{
/// <summary>
/// Gets or Sets the IsAttached property.
/// </summary>
/// <remarks>
/// Indicates that Prism is tracking the scope
/// </remarks>
bool IsAttached { get; set; }
}
这个接口是从IContainerProvider接口继承过来的,从类型上面讲也是一个IContainerProvider,后面关于这个具体的实现我们可以看看具体的源码是如何实现CreateScope的,另外这个和前面讲的IContainerRegistry中RegisterScoped
方法有什么联系,后面我们将带着这些疑问来一一进行解答。
2.3 ContainerLocator
这个是在Prism.Core中定义的一个静态类,我们来看看这个静态类有些什么作用?我们先来看看这个类的代码?
/// <summary>
/// The <see cref="ContainerLocator" /> tracks the current instance of the Container used by your Application
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class ContainerLocator
{
private static Lazy<IContainerExtension> _lazyContainer;
private static IContainerExtension _current;
/// <summary>
/// Gets the current <see cref="IContainerExtension" />.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static IContainerExtension Current =>
_current ?? (_current = _lazyContainer?.Value);
/// <summary>
/// Gets the <see cref="IContainerProvider" />
/// </summary>
public static IContainerProvider Container =>
Current;
/// <summary>
/// Sets the Container Factory to use if the Current <see cref="IContainerProvider" /> is null
/// </summary>
/// <param name="factory"></param>
/// <remarks>
/// NOTE: We want to use Lazy Initialization in case the container is first created
/// prior to Prism initializing which could be the case with Shiny
/// </remarks>
public static void SetContainerExtension(Func<IContainerExtension> factory) =>
_lazyContainer = new Lazy<IContainerExtension>(factory);
/// <summary>
/// Used for Testing to Reset the Current Container
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static void ResetContainer()
{
_current = null;
_lazyContainer = null;
}
}
这个静态类用于定义一个全局唯一的IContainerExtension对象,到了这里我们还有最后的一个部分就是IContainerExtension对象,我们先来看这个对象的定义。
/// <summary>
/// A strongly typed container extension
/// </summary>
/// <typeparam name="TContainer">The underlying root container</typeparam>
public interface IContainerExtension<TContainer> : IContainerExtension
{
/// <summary>
/// The instance of the wrapped container
/// </summary>
TContainer Instance { get; }
}
/// <summary>
/// A generic abstraction for what Prism expects from a container
/// </summary>
public interface IContainerExtension : IContainerProvider, IContainerRegistry
{
/// <summary>
/// Used to perform any final steps for configuring the extension that may be required by the container.
/// </summary>
void FinalizeExtension();
}
这个接口同时继承上面的两个接口,表面继承的对象同时拥有注册类型对象和解析类型对象的两种能力,另外还定义了一个FinalizeExtension方法用于做一些容器终结的操作,除了这个接口以外还定义了一个泛型IContainerExtension
总结
上面的整篇文章就Prism.Core类库中关于整个IOC的重点内容做了一个分析,在这个部分就整个IOC容器要实现的内容做了一个详细的接口定义,定义了该如何注册类型和解析类型,后面的文章中我们会选择DryIOC容器作为这个容器接口的实现来就每一个部分来进行说明,当然Prism.Core作为一个接口定义在这里已全部分析完毕,并且在Prism.Core这个类库中的职责已全部完毕后面整个Prism框架内部提供了DryIOC和Unity两种具体的实现,后面的文章会就这个部分来做一个更加详细的说明。