NopCommerce使用Autofac实现依赖注入

来源:互联网 发布:九年级下册历史书淘宝 编辑:程序博客网 时间:2024/04/20 07:11

NopCommerce使用Autofac实现依赖注入

文章内容:

  • 什么是依赖注入
  • NopCommerce网站中使用Autofac

什么是依赖注入

 依赖注入也叫"控制反转(IOC)",目的在于将组件的配置与使用分离开来。

举个简单的例子:我饿了,要叫外卖吃,我只要打开APP,选好吃的,然后付钱,等着外卖到了就可以吃了。至于外卖员怎么送外卖,是步行送还是骑车送, 是走什么路线,这些我不需要知道,也无法知道。

我们从代码来看,理解起来会更清晰一点
首先,我给送餐定义了一个接口ITakeout:

//送餐接口,SendFood送餐的方法定义 public interface ITakeout {    string SendFood() }

然后,订餐的实现类:

  public class OnlineOrdering    {        private ITakeout _takeoutService;        public string Ordering()        {            APP订餐付款();            //调用_takeoutService的SendFood方法获得食物food, _takeoutService的具体实现类我是不知道的。              string food = _takeoutService.SendFood();            return food;        }    }

在上面的例子中,虽然我知道有送餐这个过程,但是我并不知道它的具体实现方式,所以我不能new一个实例给_takeoutService属性。退一步讲,就算我知道一个类TakeoutByFoot实现了这个接口,于是我将它赋值给 _takeoutService, 将构造函数改为如下代码:

public OnlineOrdering(){    this._takeoutService=new TakeoutByFoot();}

这样做的问题是,如果明天换了个外卖员,他是骑车送餐的,后天又换了一个,他是开车送的。那我不是要频繁的更改OnlineOrdering类。这就造成了耦合。

依赖注入的好处在于我只要定义一个送餐的接口,然后就可以使用它,至于接口的实现类是什么我不需要知道,因为IOC框架会自动为接口赋值。将OnlineOrdering构造方法改为:

  public OnlineOrdering(ITakeout takeoutService)  {     this._takeoutService = takeoutService;  }

这样IOC框架就可以用构造子注入的方式,为 _takeoutService 添加具体实现类。至于是实现那个类,这个可以交给配置来做。如何配置这个后面会讲。这就是”使用”与 “配置”分离。

有人会说为什么不直接在外部给 _takeoutService 属性赋值,那样的话 _takeoutService 属性就必须是public ,可我并不想将这个属性暴露给外部。另外,如果ITakeout实现类中还定义了其他引用型属性,我就必须一层层赋值,这不是理想的方式。

NopCommerce网站中使用Autofac

官方网站地址
NopCommerce的官方网站,可以下载源码,详见[http://www.nopcommerce.com/][1].
Autofac的官方网站,详见[http://autofac.org/][1].

NopCommerce是国外的一个高质量的开源b2c网站系统。该网站使用了名为Autofac的依赖注入框架。Autofac的使用方法这里就不做介绍了,可以去官网上看,我只是简单分析一下NopCommerce是如何使用Autofac框架的。(本人水平有限,这只是我自己的理解,如果有不对的地方,欢迎指正)

代码在Nop.Core.Infrastructure和Nop.Core.Infrastructure.DependencyManagement命名空间。主要分为两个模块:

主要模块

ContainerManager 的功能有:管理依赖注入的容器,实例化服务。
Engine 的功能有:管理ContainerManager, 实例化服务,在程序启动时注册各种服务。
另外还有一些工具模块,这里只简单说明。具体实现可以看代码:

模块 作用 TypeFinder 通过反射,找到某个Type的类,子类或实现类 Singleton 用于存储单例 Config 系统设置

首先,来看ContainerManager 类,部分代码:

    public class ContainerManager    {        private readonly IContainer _container;        public ContainerManager(IContainer container)        {            this._container = container;        }        public virtual IContainer Container        {            get            {                return this._container;            }        }        //........各种Resolve方法,略   }

ContainerManager 中有个只读属性IContainer _container这是依赖注入容器,我们注册的服务都有保存到 _container中,同时实例化服务也是从 _container中获取。它相当于一个仓库。ContainerManager 通过在构造函数中传入IContainer 的实例,为 _container赋值。同时,ContainerManager 也提供了Resolve方法,为在 _container中注册的服务提供实例。

再来看下Engion模块,Engion模块定义了一个接口IEngine,该接口的实现类NopEngine,以及用来保存NopEngine单例的EngineContext。
IEngine定义如下:

 public interface IEngine    {        ContainerManager ContainerManager { get; }        void Initialize(NopConfig config);        T Resolve<T>() where T : class;        object Resolve(Type type);        T[] ResolveAll<T>();    }

很清楚看到,IEngion有个ContainerManager 类型的属性,通过这个属性我们可以获得IContainer , 看一下NopEngine的代码就会发现ContainerManager 是在执行Initialize方法的时候赋值的。IEngion还提供了Resolve方法,其实就是调用ContainerManager 的Resolve方法,向外界提供服务。

IEngion的实现类NopEngine:

  public class NopEngine : IEngine    {        private ContainerManager _containerManager;        protected virtual void RegisterDependencies(NopConfig config)        {            // ①            var builder = new ContainerBuilder();            var container = builder.Build();            this._containerManager = new ContainerManager(container);            //dependencies WebAppTypeFinder是ITypeFinder的实现类,属于TypeFinder 模块。暂时知道它是干什么的就行了。            var typeFinder = new WebAppTypeFinder();            //因为ContainerBuilder只能Build()或Update()一次,所以要新建一个            builder = new ContainerBuilder();            // ②            builder.RegisterInstance(this).As<IEngine>().SingleInstance();            builder.RegisterInstance(typeFinder).As<ITypeFinder>().SingleInstance();            builder.Update(container);            //register dependencies provided by other assemblies ③            builder = new ContainerBuilder();            var drTypes = typeFinder.FindClassesOfType<IDependencyRegistrar>();            var drInstances = new List<IDependencyRegistrar>();            foreach (var drType in drTypes)                drInstances.Add((IDependencyRegistrar) Activator.CreateInstance(drType));            //sort            drInstances = drInstances.AsQueryable().OrderBy(t => t.Order).ToList();            foreach (var dependencyRegistrar in drInstances)                dependencyRegistrar.Register(builder, typeFinder, config);            builder.Update(container);            //set dependency resolver ④            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));        }        public void Initialize(NopConfig config)        {            //register dependencies            RegisterDependencies(config);            //.......执行启动任务,与依赖注入无关,略        }        //.............Resolve方法,略        public ContainerManager ContainerManager        {            get { return _containerManager; }        }    }

以上代码我只截取了比较重要的部分,NopEngine类的重点在Initialize方法,该方法调用了RegisterDependencies方法,从方法名可以知道用来注册依赖项(服务)。

详细过程如下:

① build 了一个IContainer 容器,然后用该容器初始化ContainerManager ,以后注册的服务都将保存在这个容器中。由于NopEngine是单例,所以IContainer 容器也只有一个。

②将自己注册为一个单例,TypeFinder 模块也注册一个单例。注意builder.Update(container)这句一定要加,它用来将注册的服务保存到容器中去。

③首先来看这句代码var drTypes = typeFinder.FindClassesOfType<IDependencyRegistrar>(); 这句的意思是找出所有IDependencyRegistrar接口的实现类。IDependencyRegistrar的定义如下:

    public interface IDependencyRegistrar    {        void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config);        int Order { get; }    }

然后再看这两句代码:

1.foreach (var drType in drTypes)   drInstances.Add((IDependencyRegistrar) Activator.CreateInstance(drType));2.foreach (var dependencyRegistrar in drInstances)   dependencyRegistrar.Register(builder, typeFinder, config);  builder.Update(container);

1表示实例化所有IDependencyRegistrar实现类,2表示执行IDependencyRegistrar实现类的Register方法,并且将builder作为参数传进去。然后将builder更新到container容器中。
这步操作有什么用呢? 举例一个IDependencyRegistrar的实现类:

    public class DependencyRegistrar : IDependencyRegistrar    {        public void Register(ContainerBuilder builder, ITypeFinder finder ,FLOWConfig config)        {            builder.RegisterType<TakeoutByFoot>().As<ITakeout>().InstancePerLifetimeScope();        }        public int Order        {            get { return 1; }        }    }

DependencyRegistrar 可以通过Register方法注册自己需要的服务。这样做的好处是,不同的服务使用者可以定义自己的IDependencyRegistrar实现类,然后注册自己需要的服务,这样服务使用起来非常方便,更换服务也很快捷。而服务提供者,我只要提供一个接口给使用者,至于使用者如何配置,我可以不用考虑。拿开始的例子说就是,外卖员如何送餐由他自己选择,而不是我来选择一个骑摩托的外卖员。

④set dependency resolver

NopEngine理解了以后,剩下就很好理解了。EngionContext提供了NopEngine的单例,代码如下:

 public class EngineContext    {        #region Methods        [MethodImpl(MethodImplOptions.Synchronized)]        public static IEngine Initialize(bool forceRecreate)        {            if (Singleton<IEngine>.Instance == null || forceRecreate)            {                Singleton<IEngine>.Instance = new NopEngine();                var config = ConfigurationManager.GetSection("NopConfig") as NopConfig;                Singleton<IEngine>.Instance.Initialize(config);            }            return Singleton<IEngine>.Instance;        }        public static void Replace(IEngine engine)        {            Singleton<IEngine>.Instance = engine;        }        #endregion        #region Properties        public static IEngine Current        {            get            {                if (Singleton<IEngine>.Instance == null)                {                    Initialize(false);                }                return Singleton<IEngine>.Instance;            }        }        #endregion    }

可以看出,Singleton.Instance保存了IEngine的单例,Current用来获取IEngine单例。Initialize方法在IEngine单例为空或强制Recreate的情况下,初始化IEngine引擎。Singleton<IEngine>.Instance.Initialize(config); 这句代码表示IEngine引擎初始化,在初始化过程中,会注册服务。

最后,在程序启动时初始化IEngine引擎就会自动注册服务了:

  protected void Application_Start()  {        ........        //initialize engine context        EngineContext.Initialize(false);        .......  }

0 0
原创粉丝点击