• 我的NopCommerce之旅(7): 依赖注入(IOC/DI)


    一、基础介绍

    依赖注入,Dependency Injection,权威解释及说明请自己查阅资料。
    这里简单说一下常见使用:在mvc的controller的构造方法中定义参数,如ICountryService countryService,然后直接使用countryService(构造注入)。
     1         public CountryController(ICountryService countryService, 
     2             IStateProvinceService stateProvinceService, 
     3             ILocalizationService localizationService, 
     4             IWorkContext workContext,
     5             ICacheManager cacheManager)
     6         {
     7             this._countryService = countryService;
     8             this._stateProvinceService = stateProvinceService;
     9             this._localizationService = localizationService;
    10             this._workContext = workContext;
    11             this._cacheManager = cacheManager;
    12         }

    显然,你并没有在controller调用前,new一个ICountryService的对象给controller做参数,你也不知道该在哪去写这个调用。没关系,这些都交给系统处理,你不必理会,这就是依赖注入。

    二、承上启下

    上一篇大致分析了应用启动,而NopCommerce完成注册依赖就在初始化上下文部分。通过代码跟踪,即可发现Nop.Core.Infrastructure.NopEngine.RegisterDependencies()。

    1             //initialize engine context 初始化上下文
    2             EngineContext.Initialize(false);
     1         /// <summary>
     2         /// Initializes a static instance of the Nop factory.
     3         /// </summary>
     4         /// <param name="forceRecreate">Creates a new factory instance even though the factory has been previously initialized.</param>
     5         [MethodImpl(MethodImplOptions.Synchronized)]
     6         public static IEngine Initialize(bool forceRecreate)
     7         {
     8             if (Singleton<IEngine>.Instance == null || forceRecreate)
     9             {
    10                 Singleton<IEngine>.Instance = new NopEngine();
    11 
    12                 var config = ConfigurationManager.GetSection("NopConfig") as NopConfig;
    13                 Singleton<IEngine>.Instance.Initialize(config);
    14             }
    15             return Singleton<IEngine>.Instance;
    16         }
     1         /// <summary>
     2         /// Initialize components and plugins in the nop environment.
     3         /// </summary>
     4         /// <param name="config">Config</param>
     5         public void Initialize(NopConfig config)
     6         {
     7             //register dependencies
     8             RegisterDependencies(config);
     9 
    10             //startup tasks
    11             if (!config.IgnoreStartupTasks)
    12             {
    13                 RunStartupTasks();
    14             }
    15 
    16         }
     1         /// <summary>
     2         /// Register dependencies
     3         /// </summary>
     4         /// <param name="config">Config</param>
     5         protected virtual void RegisterDependencies(NopConfig config)
     6         {
     7             var builder = new ContainerBuilder();
     8             var container = builder.Build();
     9             this._containerManager = new ContainerManager(container);
    10 
    11             //we create new instance of ContainerBuilder
    12             //because Build() or Update() method can only be called once on a ContainerBuilder.
    13 
    14             //dependencies
    15             var typeFinder = new WebAppTypeFinder();
    16             builder = new ContainerBuilder();
    17             builder.RegisterInstance(config).As<NopConfig>().SingleInstance();
    18             builder.RegisterInstance(this).As<IEngine>().SingleInstance();
    19             builder.RegisterInstance(typeFinder).As<ITypeFinder>().SingleInstance();
    20             builder.Update(container);
    21 
    22             //register dependencies provided by other assemblies
    23             builder = new ContainerBuilder();
    24             var drTypes = typeFinder.FindClassesOfType<IDependencyRegistrar>();
    25             var drInstances = new List<IDependencyRegistrar>();
    26             foreach (var drType in drTypes)
    27                 drInstances.Add((IDependencyRegistrar) Activator.CreateInstance(drType));
    28             //sort
    29             drInstances = drInstances.AsQueryable().OrderBy(t => t.Order).ToList();
    30             foreach (var dependencyRegistrar in drInstances)
    31                 dependencyRegistrar.Register(builder, typeFinder, config);通过反射,按顺序注册依赖
    32             builder.Update(container);
    33 
    34             //set dependency resolver
    35             DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    36         }

    三、NopCommerce的结构分析

      本文通过从依赖注入框架Autofac的DependencyRegistrar方法来分析NopCommerce的结构。

    1.分析方法

      在vs里Solution Explorer的查找栏输入DependencyRegistrar,即可看到所有实现DependencyRegistrar()方法的。

      2.总结

        2.1 Nop.Core定义了接口;

        2.2 Plugins注册自己需要的类;

        2.3 Nop.Admin和Nop.Web注册Controller所要使用的缓存类;

        2.4 Nop.Web.Framework注册了NopCommerce的核心类。

    四、代码分析

      1.这里主要分析Nop.Web.Framework的DependencyRegistrar()

      1.1.注册http上下文;

      1.2.注册web helper、user agent helper;

      1.3.注册mvc的controllers;

      1.4.注册数据层类,如dbcontext,manager,repository,供持久化层的操作;

      1.5.注册插件plugins;

      1.6.注册缓存管理的相关类;

      1.7.注册work context和store context;

      1.8.注册业务层services,部分需注册缓存参数,可以看到WithParameter( ResolvedParameter.ForNamed< ICacheManager >("nop_cache_static" ));

      1.9.注册用户事件。

    2.代码如下

      1         /// <summary>
      2         /// Register services and interfaces
      3         /// </summary>
      4         /// <param name="builder">Container builder</param>
      5         /// <param name="typeFinder">Type finder</param>
      6         /// <param name="config">Config</param>
      7         public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config)
      8         {
      9             //HTTP context and other related stuff
     10             builder.Register(c => 
     11                 //register FakeHttpContext when HttpContext is not available
     12                 HttpContext.Current != null ?
     13                 (new HttpContextWrapper(HttpContext.Current) as HttpContextBase) :
     14                 (new FakeHttpContext("~/") as HttpContextBase))
     15                 .As<HttpContextBase>()
     16                 .InstancePerLifetimeScope();
     17             builder.Register(c => c.Resolve<HttpContextBase>().Request)
     18                 .As<HttpRequestBase>()
     19                 .InstancePerLifetimeScope();
     20             builder.Register(c => c.Resolve<HttpContextBase>().Response)
     21                 .As<HttpResponseBase>()
     22                 .InstancePerLifetimeScope();
     23             builder.Register(c => c.Resolve<HttpContextBase>().Server)
     24                 .As<HttpServerUtilityBase>()
     25                 .InstancePerLifetimeScope();
     26             builder.Register(c => c.Resolve<HttpContextBase>().Session)
     27                 .As<HttpSessionStateBase>()
     28                 .InstancePerLifetimeScope();
     29 
     30             //web helper
     31             builder.RegisterType<WebHelper>().As<IWebHelper>().InstancePerLifetimeScope();
     32             //user agent helper
     33             builder.RegisterType<UserAgentHelper>().As<IUserAgentHelper>().InstancePerLifetimeScope();
     34 
     35             
     36             //controllers
     37             builder.RegisterControllers(typeFinder.GetAssemblies().ToArray());
     38 
     39             //data layer
     40             var dataSettingsManager = new DataSettingsManager();
     41             var dataProviderSettings = dataSettingsManager.LoadSettings();
     42             builder.Register(c => dataSettingsManager.LoadSettings()).As<DataSettings>();
     43             builder.Register(x => new EfDataProviderManager(x.Resolve<DataSettings>())).As<BaseDataProviderManager>().InstancePerDependency();
     44 
     45 
     46             builder.Register(x => x.Resolve<BaseDataProviderManager>().LoadDataProvider()).As<IDataProvider>().InstancePerDependency();
     47 
     48             if (dataProviderSettings != null && dataProviderSettings.IsValid())
     49             {
     50                 var efDataProviderManager = new EfDataProviderManager(dataSettingsManager.LoadSettings());
     51                 var dataProvider = efDataProviderManager.LoadDataProvider();
     52                 dataProvider.InitConnectionFactory();
     53 
     54                 builder.Register<IDbContext>(c => new NopObjectContext(dataProviderSettings.DataConnectionString)).InstancePerLifetimeScope();
     55             }
     56             else
     57             {
     58                 builder.Register<IDbContext>(c => new NopObjectContext(dataSettingsManager.LoadSettings().DataConnectionString)).InstancePerLifetimeScope();
     59             }
     60 
     61 
     62             builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();
     63             
     64             //plugins
     65             builder.RegisterType<PluginFinder>().As<IPluginFinder>().InstancePerLifetimeScope();
     66             builder.RegisterType<OfficialFeedManager>().As<IOfficialFeedManager>().InstancePerLifetimeScope();
     67 
     68             //cache managers
     69             if (config.RedisCachingEnabled)
     70             {
     71                 builder.RegisterType<RedisCacheManager>().As<ICacheManager>().Named<ICacheManager>("nop_cache_static").InstancePerLifetimeScope();
     72             }
     73             else
     74             {
     75                 builder.RegisterType<MemoryCacheManager>().As<ICacheManager>().Named<ICacheManager>("nop_cache_static").SingleInstance();
     76             }
     77             builder.RegisterType<PerRequestCacheManager>().As<ICacheManager>().Named<ICacheManager>("nop_cache_per_request").InstancePerLifetimeScope();
     78 
     79             if (config.RunOnAzureWebsites)
     80             {
     81                 builder.RegisterType<AzureWebsitesMachineNameProvider>().As<IMachineNameProvider>().SingleInstance();
     82             }
     83             else
     84             {
     85                 builder.RegisterType<DefaultMachineNameProvider>().As<IMachineNameProvider>().SingleInstance();
     86             }
     87 
     88             //work context
     89             builder.RegisterType<WebWorkContext>().As<IWorkContext>().InstancePerLifetimeScope();
     90             //store context
     91             builder.RegisterType<WebStoreContext>().As<IStoreContext>().InstancePerLifetimeScope();
     92 
     93             //services
     94             builder.RegisterType<BackInStockSubscriptionService>().As<IBackInStockSubscriptionService>().InstancePerLifetimeScope();
     95             builder.RegisterType<CategoryService>().As<ICategoryService>().InstancePerLifetimeScope();
     96             builder.RegisterType<CompareProductsService>().As<ICompareProductsService>().InstancePerLifetimeScope();
     97             builder.RegisterType<RecentlyViewedProductsService>().As<IRecentlyViewedProductsService>().InstancePerLifetimeScope();
     98             builder.RegisterType<ManufacturerService>().As<IManufacturerService>().InstancePerLifetimeScope();
     99             builder.RegisterType<PriceFormatter>().As<IPriceFormatter>().InstancePerLifetimeScope();
    100             builder.RegisterType<ProductAttributeFormatter>().As<IProductAttributeFormatter>().InstancePerLifetimeScope();
    101             builder.RegisterType<ProductAttributeParser>().As<IProductAttributeParser>().InstancePerLifetimeScope();
    102             builder.RegisterType<ProductAttributeService>().As<IProductAttributeService>().InstancePerLifetimeScope();
    103             builder.RegisterType<ProductService>().As<IProductService>().InstancePerLifetimeScope();
    104             builder.RegisterType<CopyProductService>().As<ICopyProductService>().InstancePerLifetimeScope();
    105             builder.RegisterType<SpecificationAttributeService>().As<ISpecificationAttributeService>().InstancePerLifetimeScope();
    106             builder.RegisterType<ProductTemplateService>().As<IProductTemplateService>().InstancePerLifetimeScope();
    107             builder.RegisterType<CategoryTemplateService>().As<ICategoryTemplateService>().InstancePerLifetimeScope();
    108             builder.RegisterType<ManufacturerTemplateService>().As<IManufacturerTemplateService>().InstancePerLifetimeScope();
    109             builder.RegisterType<TopicTemplateService>().As<ITopicTemplateService>().InstancePerLifetimeScope();
    110             //use static cache (between HTTP requests)
    111             builder.RegisterType<ProductTagService>().As<IProductTagService>()
    112                 .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
    113                 .InstancePerLifetimeScope();
    114 
    115             builder.RegisterType<AddressAttributeFormatter>().As<IAddressAttributeFormatter>().InstancePerLifetimeScope();
    116             builder.RegisterType<AddressAttributeParser>().As<IAddressAttributeParser>().InstancePerLifetimeScope();
    117             builder.RegisterType<AddressAttributeService>().As<IAddressAttributeService>().InstancePerLifetimeScope();
    118             builder.RegisterType<AddressService>().As<IAddressService>().InstancePerLifetimeScope();
    119             builder.RegisterType<AffiliateService>().As<IAffiliateService>().InstancePerLifetimeScope();
    120             builder.RegisterType<VendorService>().As<IVendorService>().InstancePerLifetimeScope();
    121             builder.RegisterType<SearchTermService>().As<ISearchTermService>().InstancePerLifetimeScope();
    122             builder.RegisterType<GenericAttributeService>().As<IGenericAttributeService>().InstancePerLifetimeScope();
    123             builder.RegisterType<FulltextService>().As<IFulltextService>().InstancePerLifetimeScope();
    124             builder.RegisterType<MaintenanceService>().As<IMaintenanceService>().InstancePerLifetimeScope();
    125 
    126 
    127             builder.RegisterType<CustomerAttributeParser>().As<ICustomerAttributeParser>().InstancePerLifetimeScope();
    128             builder.RegisterType<CustomerAttributeService>().As<ICustomerAttributeService>().InstancePerLifetimeScope();
    129             builder.RegisterType<CustomerService>().As<ICustomerService>().InstancePerLifetimeScope();
    130             builder.RegisterType<CustomerRegistrationService>().As<ICustomerRegistrationService>().InstancePerLifetimeScope();
    131             builder.RegisterType<CustomerReportService>().As<ICustomerReportService>().InstancePerLifetimeScope();
    132 
    133             //use static cache (between HTTP requests)
    134             builder.RegisterType<PermissionService>().As<IPermissionService>()
    135                 .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
    136                 .InstancePerLifetimeScope();
    137             //use static cache (between HTTP requests)
    138             builder.RegisterType<AclService>().As<IAclService>()
    139                 .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
    140                 .InstancePerLifetimeScope();
    141             //use static cache (between HTTP requests)
    142             builder.RegisterType<PriceCalculationService>().As<IPriceCalculationService>()
    143                 .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
    144                 .InstancePerLifetimeScope();
    145 
    146             builder.RegisterType<GeoLookupService>().As<IGeoLookupService>().InstancePerLifetimeScope();
    147             builder.RegisterType<CountryService>().As<ICountryService>().InstancePerLifetimeScope();
    148             builder.RegisterType<CurrencyService>().As<ICurrencyService>().InstancePerLifetimeScope();
    149             builder.RegisterType<MeasureService>().As<IMeasureService>().InstancePerLifetimeScope();
    150             builder.RegisterType<StateProvinceService>().As<IStateProvinceService>().InstancePerLifetimeScope();
    151 
    152             builder.RegisterType<StoreService>().As<IStoreService>().InstancePerLifetimeScope();
    153             //use static cache (between HTTP requests)
    154             builder.RegisterType<StoreMappingService>().As<IStoreMappingService>()
    155                 .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
    156                 .InstancePerLifetimeScope();
    157 
    158             builder.RegisterType<DiscountService>().As<IDiscountService>().InstancePerLifetimeScope();
    159 
    160 
    161             //use static cache (between HTTP requests)
    162             builder.RegisterType<SettingService>().As<ISettingService>()
    163                 .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
    164                 .InstancePerLifetimeScope();
    165             builder.RegisterSource(new SettingsSource());
    166 
    167             //use static cache (between HTTP requests)
    168             builder.RegisterType<LocalizationService>().As<ILocalizationService>()
    169                 .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
    170                 .InstancePerLifetimeScope();
    171 
    172             //use static cache (between HTTP requests)
    173             builder.RegisterType<LocalizedEntityService>().As<ILocalizedEntityService>()
    174                 .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
    175                 .InstancePerLifetimeScope();
    176             builder.RegisterType<LanguageService>().As<ILanguageService>().InstancePerLifetimeScope();
    177 
    178             builder.RegisterType<DownloadService>().As<IDownloadService>().InstancePerLifetimeScope();
    179             //picture service
    180             var useAzureBlobStorage = !String.IsNullOrEmpty(config.AzureBlobStorageConnectionString);
    181             if (useAzureBlobStorage)
    182             {
    183                 //Windows Azure BLOB
    184                 builder.RegisterType<AzurePictureService>().As<IPictureService>().InstancePerLifetimeScope();
    185             }
    186             else
    187             {
    188                 //standard file system
    189                 builder.RegisterType<PictureService>().As<IPictureService>().InstancePerLifetimeScope();
    190             }
    191 
    192             builder.RegisterType<MessageTemplateService>().As<IMessageTemplateService>().InstancePerLifetimeScope();
    193             builder.RegisterType<QueuedEmailService>().As<IQueuedEmailService>().InstancePerLifetimeScope();
    194             builder.RegisterType<NewsLetterSubscriptionService>().As<INewsLetterSubscriptionService>().InstancePerLifetimeScope();
    195             builder.RegisterType<CampaignService>().As<ICampaignService>().InstancePerLifetimeScope();
    196             builder.RegisterType<EmailAccountService>().As<IEmailAccountService>().InstancePerLifetimeScope();
    197             builder.RegisterType<WorkflowMessageService>().As<IWorkflowMessageService>().InstancePerLifetimeScope();
    198             builder.RegisterType<MessageTokenProvider>().As<IMessageTokenProvider>().InstancePerLifetimeScope();
    199             builder.RegisterType<Tokenizer>().As<ITokenizer>().InstancePerLifetimeScope();
    200             builder.RegisterType<EmailSender>().As<IEmailSender>().InstancePerLifetimeScope();
    201 
    202             builder.RegisterType<CheckoutAttributeFormatter>().As<ICheckoutAttributeFormatter>().InstancePerLifetimeScope();
    203             builder.RegisterType<CheckoutAttributeParser>().As<ICheckoutAttributeParser>().InstancePerLifetimeScope();
    204             builder.RegisterType<CheckoutAttributeService>().As<ICheckoutAttributeService>().InstancePerLifetimeScope();
    205             builder.RegisterType<GiftCardService>().As<IGiftCardService>().InstancePerLifetimeScope();
    206             builder.RegisterType<OrderService>().As<IOrderService>().InstancePerLifetimeScope();
    207             builder.RegisterType<OrderReportService>().As<IOrderReportService>().InstancePerLifetimeScope();
    208             builder.RegisterType<OrderProcessingService>().As<IOrderProcessingService>().InstancePerLifetimeScope();
    209             builder.RegisterType<OrderTotalCalculationService>().As<IOrderTotalCalculationService>().InstancePerLifetimeScope();
    210             builder.RegisterType<ReturnRequestService>().As<IReturnRequestService>().InstancePerLifetimeScope();
    211             builder.RegisterType<RewardPointService>().As<IRewardPointService>().InstancePerLifetimeScope();
    212             builder.RegisterType<ShoppingCartService>().As<IShoppingCartService>().InstancePerLifetimeScope();
    213 
    214             builder.RegisterType<PaymentService>().As<IPaymentService>().InstancePerLifetimeScope();
    215 
    216             builder.RegisterType<EncryptionService>().As<IEncryptionService>().InstancePerLifetimeScope();
    217             builder.RegisterType<FormsAuthenticationService>().As<IAuthenticationService>().InstancePerLifetimeScope();
    218 
    219 
    220             //use static cache (between HTTP requests)
    221             builder.RegisterType<UrlRecordService>().As<IUrlRecordService>()
    222                 .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
    223                 .InstancePerLifetimeScope();
    224 
    225             builder.RegisterType<ShipmentService>().As<IShipmentService>().InstancePerLifetimeScope();
    226             builder.RegisterType<ShippingService>().As<IShippingService>().InstancePerLifetimeScope();
    227 
    228             builder.RegisterType<TaxCategoryService>().As<ITaxCategoryService>().InstancePerLifetimeScope();
    229             builder.RegisterType<TaxService>().As<ITaxService>().InstancePerLifetimeScope();
    230 
    231             builder.RegisterType<DefaultLogger>().As<ILogger>().InstancePerLifetimeScope();
    232 
    233             //use static cache (between HTTP requests)
    234             builder.RegisterType<CustomerActivityService>().As<ICustomerActivityService>()
    235                 .WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"))
    236                 .InstancePerLifetimeScope();
    237 
    238             bool databaseInstalled = DataSettingsHelper.DatabaseIsInstalled();
    239             if (!databaseInstalled)
    240             {
    241                 //installation service
    242                 if (config.UseFastInstallationService)
    243                 {
    244                     builder.RegisterType<SqlFileInstallationService>().As<IInstallationService>().InstancePerLifetimeScope();
    245                 }
    246                 else
    247                 {
    248                     builder.RegisterType<CodeFirstInstallationService>().As<IInstallationService>().InstancePerLifetimeScope();
    249                 }
    250             }
    251 
    252             builder.RegisterType<ForumService>().As<IForumService>().InstancePerLifetimeScope();
    253 
    254             builder.RegisterType<PollService>().As<IPollService>().InstancePerLifetimeScope();
    255             builder.RegisterType<BlogService>().As<IBlogService>().InstancePerLifetimeScope();
    256             builder.RegisterType<WidgetService>().As<IWidgetService>().InstancePerLifetimeScope();
    257             builder.RegisterType<TopicService>().As<ITopicService>().InstancePerLifetimeScope();
    258             builder.RegisterType<NewsService>().As<INewsService>().InstancePerLifetimeScope();
    259 
    260             builder.RegisterType<DateTimeHelper>().As<IDateTimeHelper>().InstancePerLifetimeScope();
    261             builder.RegisterType<SitemapGenerator>().As<ISitemapGenerator>().InstancePerLifetimeScope();
    262             builder.RegisterType<PageHeadBuilder>().As<IPageHeadBuilder>().InstancePerLifetimeScope();
    263 
    264             builder.RegisterType<ScheduleTaskService>().As<IScheduleTaskService>().InstancePerLifetimeScope();
    265 
    266             builder.RegisterType<ExportManager>().As<IExportManager>().InstancePerLifetimeScope();
    267             builder.RegisterType<ImportManager>().As<IImportManager>().InstancePerLifetimeScope();
    268             builder.RegisterType<PdfService>().As<IPdfService>().InstancePerLifetimeScope();
    269             builder.RegisterType<ThemeProvider>().As<IThemeProvider>().InstancePerLifetimeScope();
    270             builder.RegisterType<ThemeContext>().As<IThemeContext>().InstancePerLifetimeScope();
    271 
    272 
    273             builder.RegisterType<ExternalAuthorizer>().As<IExternalAuthorizer>().InstancePerLifetimeScope();
    274             builder.RegisterType<OpenAuthenticationService>().As<IOpenAuthenticationService>().InstancePerLifetimeScope();
    275            
    276                 
    277             builder.RegisterType<RoutePublisher>().As<IRoutePublisher>().SingleInstance();
    278 
    279             //Register event consumers
    280             var consumers = typeFinder.FindClassesOfType(typeof(IConsumer<>)).ToList();
    281             foreach (var consumer in consumers)
    282             {
    283                 builder.RegisterType(consumer)
    284                     .As(consumer.FindInterfaces((type, criteria) =>
    285                     {
    286                         var isMatch = type.IsGenericType && ((Type)criteria).IsAssignableFrom(type.GetGenericTypeDefinition());
    287                         return isMatch;
    288                     }, typeof(IConsumer<>)))
    289                     .InstancePerLifetimeScope();
    290             }
    291             builder.RegisterType<EventPublisher>().As<IEventPublisher>().SingleInstance();
    292             builder.RegisterType<SubscriptionService>().As<ISubscriptionService>().SingleInstance();
    293 
    294         }
    View Code
  • 相关阅读:
    CentOS下设置ipmi
    CentOS 使用文件增加Swap空间
    CentOS LVM
    做IT需要掌握的电力基础知识
    CentOS 7搭建本地yum源
    Lsi卡和IB卡在CentOS中升级
    Mellanox 4036配置
    IdentityServer4入门二
    IdentityServer4入门一
    RAFT选举算法-分布式数据库困惑
  • 原文地址:https://www.cnblogs.com/devilsky/p/5363795.html
Copyright © 2020-2023  润新知