NopCommerce使用Autofac实现依赖注入

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值