NopCommerce源码架构详解--Autofac依赖注入分析
NopCommerce整个代码里面都贯彻了一个面向接口编程的思想。nopCommerce商城系统是通过Autofac对接口依赖的实现进行依赖注入的。今天我们就来分析一下里面的源码,学习一下Nop在依赖注入方面设计思想,希望在以后的架构设计中对你有启发和帮助。
下面是Nop依赖注入相关类图。
可以看到这里面相关的类为
mvcApplication
IEngine,NopEngine
EngineContext
IDependencyRegistrar,DependencyRegistrar
下面我们再来看看这些类的核心代码。
我们打开Nop.Web项目的所有的Controller源码,可以看到基本上都是通过构造函数引用抽象的接口。如CustomerController的构造函数的参数类型都是某一个接口,如下图:
那我们要想看这些接口到底依赖的是哪一个真正的实现类呢?
在项目Nop.Web.Framework中有一个类DependencyRegistrar,其作用就是调用Autofac相关的API进行依赖注入,我们只要打开这个类文件,搜索一个指定的接口,就可以找到它依赖的具体类。例如,我们想看到ICategoryService接口是依赖哪一个实现,在这个类中搜索ICategoryService,会找到下面的代码:
- builder.RegisterType<CategoryService>().As<ICategoryService>().InstancePerLifetimeScope();
可以看到这个接口对应的实现为为CategoryService(Nop.Services.Catalog命名空间下的类CategoryService)。
下面我们来看看Nop在网站启动是怎么调用这个DependencyRegistrar类呢?
在项目Nop.Web也就是前台的起始项目有一个文件Global.asax里有一个函数Application_Start,这函数的意义这里就不用说了,我相信学过ASP.NET同学都知道它的作用,下面是函数Application_Start的完整代码:
- protected void Application_Start()
- {
- //初始化上下文引擎
- EngineContext.Initialize(false);
- //model binders
- ModelBinders.Binders.Add(typeof(BaseNopModel), new NopModelBinder());
- bool databaseInstalled = DataSettingsHelper.DatabaseIsInstalled();
- if (databaseInstalled)
- {
- //remove all view engines
- ViewEngines.Engines.Clear();
- //except the themeable razor view engine we use
- ViewEngines.Engines.Add(new ThemeableRazorViewEngine());
- }
- //Add some functionality on top of the default ModelMetadataProvider
- ModelMetadataProviders.Current = new NopMetadataProvider();
- //Registering some regular mvc stuff
- AreaRegistration.RegisterAllAreas();
- RegisterRoutes(RouteTable.Routes);
- //fluent validation
- DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
- ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(new NopValidatorFactory()));
- //start scheduled tasks
- if (databaseInstalled)
- {
- TaskManager.Instance.Initialize();
- TaskManager.Instance.Start();
- }
- //log application start
- if (databaseInstalled)
- {
- try
- {
- //log
- var logger = EngineContext.Current.Resolve<ILogger>();
- logger.Information("Application started", null, null);
- }
- catch (Exception)
- {
- //don't throw new exception if occurs
- }
- }
- }
里面有第一行代码就调用了上下文引擎初始化。
- EngineContext.Initialize(false);
下面我们来看看这个EngineContext的静态方法Initialize:
- [MethodImpl(MethodImplOptions.Synchronized)]
- public static IEngine Initialize(bool forceRecreate)
- {
- if (Singleton<IEngine>.Instance == null || forceRecreate)
- {
- var config = ConfigurationManager.GetSection("NopConfig") as NopConfig;
- Singleton<IEngine>.Instance = CreateEngineInstance(config);
- Singleton<IEngine>.Instance.Initialize(config);
- }
- return Singleton<IEngine>.Instance;
- }
- protected static IEngine CreateEngineInstance(NopConfig config)
- {
- if (config != null && !string.IsNullOrEmpty(config.EngineType))
- {
- var engineType = Type.GetType(config.EngineType);
- if (engineType == null)
- throw new ConfigurationErrorsException("The type '" + config.EngineType + "' could not be found. Please check the configuration at /configuration/nop/engine[@engineType] or check for missing assemblies.");
- if (!typeof(IEngine).IsAssignableFrom(engineType))
- throw new ConfigurationErrorsException("The type '" + engineType + "' doesn't implement 'Nop.Core.Infrastructure.IEngine' and cannot be configured in /configuration/nop/engine[@engineType] for that purpose.");
- return Activator.CreateInstance(engineType) as IEngine;
- }
- return new NopEngine();
- }
从上面可以看到Initialize返回一个IEngine的实例,并调用其方法Initialize。创建IEngine的实例是根据配置文件来获取哪一个IEngine的具体类,如果没有配置这个结点信息或者为空,则返回默认的IEngine的实现NopEngine。
下面我们来看看Nop.Core.Infrastructure.NopEngine里面的核心代码:
- public void Initialize(NopConfig config)
- {
- //注册依赖
- RegisterDependencies(config);
- //startup tasks
- if (!config.IgnoreStartupTasks)
- {
- RunStartupTasks();
- }
- }
- /// <summary>
- /// 注册依赖
- /// </summary>
- /// <param name="config">Config</param>
- protected virtual void RegisterDependencies(NopConfig config)
- {
- var builder = new ContainerBuilder();
- var container = builder.Build();
- //we create new instance of ContainerBuilder
- //because Build() or Update() method can only be called once on a ContainerBuilder.
- //dependencies
- var typeFinder = new WebAppTypeFinder(config);
- builder = new ContainerBuilder();
- builder.RegisterInstance(config).As<NopConfig>().SingleInstance();
- builder.RegisterInstance(this).As<IEngine>().SingleInstance();
- builder.RegisterInstance(typeFinder).As<ITypeFinder>().SingleInstance();
- builder.Update(container);
- //register dependencies provided by other assemblies
- builder = new ContainerBuilder();
- //查找所有实现了接口IDependencyRegistrar的类
- var drTypes = typeFinder.FindClassesOfType<IDependencyRegistrar>();
- var drInstances = new List<IDependencyRegistrar>();
- foreach (var drType in drTypes)
- drInstances.Add((IDependencyRegistrar) Activator.CreateInstance(drType));
- //排序
- drInstances = drInstances.AsQueryable().OrderBy(t => t.Order).ToList();
- //依次调用实现IDependencyRegistrar接口的类的方法Register
- foreach (var dependencyRegistrar in drInstances)
- dependencyRegistrar.Register(builder, typeFinder);
- builder.Update(container);
- this._containerManager = new ContainerManager(container);
- //set dependency resolver
- DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
- }