使用 DryIoc 替换 Abp 的 DI 框架

你说我 Castle Windsor 库用得好好的,为啥要大费周章的替换成 DryIoc 库呢?那就是性能,DryIoc 是一款优秀而且轻量级的 DI 框架,整个项目代码就两个文件,加起来代码 1 万行左右(PS: 大部分都是注释)。

在各个 Ioc 容器的性能评测当中,DryIoc 以其优异的性能成为我选择使用他的原因。Abp 使用的 Castle Windsor 在解析复杂对象的时候,速度非常慢,而替换为 DryIoc 之后速度可以提升 150% 以上。

【注意】

本文仅对 .NET Core 相关的库进行更改并测试,.NET Framework 相关的库并没有进行修改测试。

二、准备

你需要准备如下原料:

Abp 源码 一份。

测试用的项目一份。

Visual Studio 2017 或者 Rider 一份。

.NET 程序猿一枚。

三、分析

首先,Abp 框架的大部分动作基本上都是通过 IIocManager 这个接口对象进行实现的,它抽象了一层作为一个 DI 框架的操作类。它的默认实现是使用的 Castle Windsor 来进行组件的注入与解析,所以我们只需要将其改为使用 DryIoc 的容器其进行操作即可。

其次,在 Abp 框架的很多地方都有用到 Castle Windsor 的 IWindsorContainer 对象,但一般用到该方法的地方都是注入或者绑定组件注册事件,这些我们都可以重新实现的。

做完了以上的工作仅仅是代表我们的 Abp 的所有组件都可以通过 DryIoc 来进行注册和解析,不过要和 ASP.NET Core 集成的话,还需要 IServiceProvider 的适配器,针对于适配器 DryIoc 也给我们提供了,拿来用即可。

所以,我们基本确定了需要变更的项目主要是 Abp 这个核心库,还有 Abp.AspNetCore 这个子模块。除了前面两个比较重要的模块之外,还有 Abp.EntityFrameworkCore 相关的库也需要变更,这是因为他们内部都直接使用到了 IWindsorContainer 对象对容器进行操作的。

四、开撸 4.1 Abp 库改造

Abp 本身库里面需要改动的地方基本集中在 Dependency 文件夹里面,这个文件夹我们之前有讲过,基本所有依赖注入相关的类型与接口都存放在这里面的。

除了依赖注入相关的类型需要更改以外,我们还需要更改各个拦截器注入的地方。因为在之前 Abp 如果需要为某个类型注入拦截器的话,是使用到了 IWindsorContainer 接口所提供的组件注入事件来进行拦截器注入的。

首先我们针对于 Abp 库添加 DryIoc 库的 NuGet 包引用,这里我是安装的 3.1.0-preview-06 版本。

4.1.1 IocManger 改造

首先看一下 IIocManager 接口,该接口定义如下:

/// <summary> /// This interface is used to directly perform dependency injection tasks. /// </summary> public interface IIocManager : IIocRegistrar, IIocResolver, IDisposable { /// <summary> /// Reference to the Castle Windsor Container. /// </summary> IWindsorContainer IocContainer { get; } /// <summary> /// Checks whether given type is registered before. /// </summary> /// <param>Type to check</param> new bool IsRegistered(Type type); /// <summary> /// Checks whether given type is registered before. /// </summary> /// <typeparam>Type to check</typeparam> new bool IsRegistered<T>(); }

可以看到他定义了一个 IWindsorContainer 的属性,我们将其改为 IContainer 。基本上做了这一步之后,在 Abp 的其他项目会出现一堆错误提示,先不慌,一步一步来。

接着我们转到 IIocManager 的实现类 IocManager ,一样的更改 IocContainer 的类型为 IContainer 之后,我们继续来到其构造函数,可以看到有如下代码:

public IocManager() { IocContainer = CreateContainer(); _conventionalRegistrars = new List<IConventionalDependencyRegistrar>(); //Register self! IocContainer.Register( Component .For<IocManager, IIocManager, IIocRegistrar, IIocResolver>() .Instance(this) ); }

因为我们的 IocContainer 跟着变更了,这里也不能使用 CreateContainer() 方法来创建 DryIoc 的容器。其次,在下面注册自己的时候,也是使用到了 IWindsorContainer 的注册方法,一样的需要进行更改。变更好的构造函数如下:

public IocManager() { // 这里通过 Rules 启用了瞬态对象跟踪,默认是不启动的。 IocContainer = new Container(Rules.Default.WithTrackingDisposableTransients()); _conventionalRegistrars = new List<IConventionalDependencyRegistrar>(); // 注册自身 IocContainer.UseInstance(typeof(IocManager),this); IocContainer.UseInstance(typeof(IIocManager),this); IocContainer.UseInstance(typeof(IIocRegistrar),this); IocContainer.UseInstance(typeof(IIocResolver),this); }

接着就需要继续看一下报错的方法,另一个需要改的则是注册方法的一个辅助私有方法 ApplyLifestyle,该方法主要作用就是将 Abp 定义的生命周期转换为具体 Ioc 容器的生命周期常量。而且该方法原来是返回的一个 ComponentRegistration<T> 对象,这个对象是 Castle Windsor 的一个专属类,所以需要改造一下,变更之后如下:

private static IReuse ApplyLifestyle(DependencyLifeStyle lifeStyle) { switch (lifeStyle) { case DependencyLifeStyle.Transient: return Reuse.Transient;; case DependencyLifeStyle.Singleton: return Reuse.Singleton; default: return Reuse.Transient; } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyypz.html