• Orchard详解--第三篇 依赖注入之基础设施


      Orchard提供了依赖注入机制,并且框架的实现也离不开依赖注入如模块管理、日志、事件等。在前一篇中提到在Global.asax中定义的HostInitialization创建了Autofac的IoC容器。
     1       private static IOrchardHost HostInitialization(HttpApplication application) {
     2             var host = OrchardStarter.CreateHost(MvcSingletons);
     3  
     4             host.Initialize();
     5  
     6             // initialize shells to speed up the first dynamic query
     7             host.BeginRequest();
     8             host.EndRequest();
     9  
    10             return host;
    11         }    

    其中OrchardStarter是位于Orchard.Framework项目中Environment目录下的类型

    1     public static IOrchardHost CreateHost(Action<ContainerBuilder> registrations) {
    2             var container = CreateHostContainer(registrations);
    3             return container.Resolve<IOrchardHost>();
    4         }
    以下是容器创建的全量代码:
      1         public static IContainer CreateHostContainer(Action<ContainerBuilder> registrations) {
      2             ExtensionLocations extensionLocations = new ExtensionLocations();
      3  
      4             var builder = new ContainerBuilder();
      5             // Application paths and parameters
      6             builder.RegisterInstance(extensionLocations);
      7  
      8             builder.RegisterModule(new CollectionOrderModule());
      9             builder.RegisterModule(new LoggingModule());
     10             builder.RegisterModule(new EventsModule());
     11             builder.RegisterModule(new CacheModule());
     12  
     13             // a single default host implementation is needed for bootstrapping a web app domain
     14             builder.RegisterType<DefaultOrchardEventBus>().As<IEventBus>().SingleInstance();
     15             builder.RegisterType<DefaultCacheHolder>().As<ICacheHolder>().SingleInstance();
     16             builder.RegisterType<DefaultCacheContextAccessor>().As<ICacheContextAccessor>().SingleInstance();
     17             builder.RegisterType<DefaultParallelCacheContext>().As<IParallelCacheContext>().SingleInstance();
     18             builder.RegisterType<DefaultAsyncTokenProvider>().As<IAsyncTokenProvider>().SingleInstance();
     19             builder.RegisterType<DefaultHostEnvironment>().As<IHostEnvironment>().SingleInstance();
     20             builder.RegisterType<DefaultHostLocalRestart>().As<IHostLocalRestart>().Named<IEventHandler>(typeof(IShellSettingsManagerEventHandler).Name).SingleInstance();
     21             builder.RegisterType<DefaultBuildManager>().As<IBuildManager>().SingleInstance();
     22             builder.RegisterType<DynamicModuleVirtualPathProvider>().As<ICustomVirtualPathProvider>().SingleInstance();
     23             builder.RegisterType<AppDataFolderRoot>().As<IAppDataFolderRoot>().SingleInstance();
     24             builder.RegisterType<DefaultExtensionCompiler>().As<IExtensionCompiler>().SingleInstance();
     25             builder.RegisterType<DefaultRazorCompilationEvents>().As<IRazorCompilationEvents>().SingleInstance();
     26             builder.RegisterType<DefaultProjectFileParser>().As<IProjectFileParser>().SingleInstance();
     27             builder.RegisterType<DefaultAssemblyLoader>().As<IAssemblyLoader>().SingleInstance();
     28             builder.RegisterType<AppDomainAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
     29             builder.RegisterType<GacAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
     30             builder.RegisterType<OrchardFrameworkAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
     31             builder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().InstancePerDependency();
     32             builder.RegisterType<ViewsBackgroundCompilation>().As<IViewsBackgroundCompilation>().SingleInstance();
     33             builder.RegisterType<DefaultExceptionPolicy>().As<IExceptionPolicy>().SingleInstance();
     34             builder.RegisterType<DefaultCriticalErrorProvider>().As<ICriticalErrorProvider>().SingleInstance();
     35             //builder.RegisterType<RazorTemplateCache>().As<IRazorTemplateProvider>().SingleInstance();
     36  
     37             RegisterVolatileProvider<WebSiteFolder, IWebSiteFolder>(builder);
     38             RegisterVolatileProvider<AppDataFolder, IAppDataFolder>(builder);
     39             RegisterVolatileProvider<DefaultLockFileManager, ILockFileManager>(builder);
     40             RegisterVolatileProvider<Clock, IClock>(builder);
     41             RegisterVolatileProvider<DefaultDependenciesFolder, IDependenciesFolder>(builder);
     42             RegisterVolatileProvider<DefaultExtensionDependenciesManager, IExtensionDependenciesManager>(builder);
     43             RegisterVolatileProvider<DefaultAssemblyProbingFolder, IAssemblyProbingFolder>(builder);
     44             RegisterVolatileProvider<DefaultVirtualPathMonitor, IVirtualPathMonitor>(builder);
     45             RegisterVolatileProvider<DefaultVirtualPathProvider, IVirtualPathProvider>(builder);
     46            
     47             builder.RegisterType<DefaultOrchardHost>().As<IOrchardHost>().As<IEventHandler>()
     48                 .Named<IEventHandler>(typeof(IShellSettingsManagerEventHandler).Name)
     49                 .Named<IEventHandler>(typeof(IShellDescriptorManagerEventHandler).Name)
     50                 .SingleInstance();
     51             {
     52                 builder.RegisterType<ShellSettingsManager>().As<IShellSettingsManager>().SingleInstance();
     53  
     54                 builder.RegisterType<ShellContextFactory>().As<IShellContextFactory>().SingleInstance();
     55                 {
     56                     builder.RegisterType<ShellDescriptorCache>().As<IShellDescriptorCache>().SingleInstance();
     57  
     58                     builder.RegisterType<CompositionStrategy>().As<ICompositionStrategy>().SingleInstance();
     59                     {
     60                         builder.RegisterType<ShellContainerRegistrations>().As<IShellContainerRegistrations>().SingleInstance();
     61                         builder.RegisterType<ExtensionLoaderCoordinator>().As<IExtensionLoaderCoordinator>().SingleInstance();
     62                         builder.RegisterType<ExtensionMonitoringCoordinator>().As<IExtensionMonitoringCoordinator>().SingleInstance();
     63                         builder.RegisterType<ExtensionManager>().As<IExtensionManager>().SingleInstance();
     64                         {
     65                             builder.RegisterType<ExtensionHarvester>().As<IExtensionHarvester>().SingleInstance();
     66                             builder.RegisterType<ModuleFolders>().As<IExtensionFolders>().SingleInstance()
     67                                 .WithParameter(new NamedParameter("paths", extensionLocations.ModuleLocations));
     68                             builder.RegisterType<CoreModuleFolders>().As<IExtensionFolders>().SingleInstance()
     69                                 .WithParameter(new NamedParameter("paths", extensionLocations.CoreLocations));
     70                             builder.RegisterType<ThemeFolders>().As<IExtensionFolders>().SingleInstance()
     71                                 .WithParameter(new NamedParameter("paths", extensionLocations.ThemeLocations));
     72  
     73                             builder.RegisterType<CoreExtensionLoader>().As<IExtensionLoader>().SingleInstance();
     74                             builder.RegisterType<ReferencedExtensionLoader>().As<IExtensionLoader>().SingleInstance();
     75                             builder.RegisterType<PrecompiledExtensionLoader>().As<IExtensionLoader>().SingleInstance();
     76                             builder.RegisterType<DynamicExtensionLoader>().As<IExtensionLoader>().SingleInstance();
     77                             builder.RegisterType<RawThemeExtensionLoader>().As<IExtensionLoader>().SingleInstance();
     78                         }
     79                     }
     80  
     81                     builder.RegisterType<ShellContainerFactory>().As<IShellContainerFactory>().SingleInstance();
     82                 }
     83  
     84                 builder.RegisterType<DefaultProcessingEngine>().As<IProcessingEngine>().SingleInstance();
     85             }
     86  
     87             builder.RegisterType<RunningShellTable>().As<IRunningShellTable>().SingleInstance();
     88             builder.RegisterType<DefaultOrchardShell>().As<IOrchardShell>().InstancePerMatchingLifetimeScope("shell");
     89             builder.RegisterType<SessionConfigurationCache>().As<ISessionConfigurationCache>().InstancePerMatchingLifetimeScope("shell");
     90  
     91             registrations(builder);
     92  
     93             var autofacSection = ConfigurationManager.GetSection(ConfigurationSettingsReaderConstants.DefaultSectionName);
     94             if (autofacSection != null)
     95                 builder.RegisterModule(new ConfigurationSettingsReader());
     96  
     97             var optionalHostConfig = HostingEnvironment.MapPath("~/Config/Host.config");
     98             if (File.Exists(optionalHostConfig))
     99                 builder.RegisterModule(new ConfigurationSettingsReader(ConfigurationSettingsReaderConstants.DefaultSectionName, optionalHostConfig));
    100  
    101             var optionalComponentsConfig = HostingEnvironment.MapPath("~/Config/HostComponents.config");
    102             if (File.Exists(optionalComponentsConfig))
    103                 builder.RegisterModule(new HostComponentsConfigModule(optionalComponentsConfig));
    104  
    105             var container = builder.Build();
    106  
    107             //
    108             // Register Virtual Path Providers
    109             //
    110             if (HostingEnvironment.IsHosted) {
    111                 foreach (var vpp in container.Resolve<IEnumerable<ICustomVirtualPathProvider>>()) {
    112                     HostingEnvironment.RegisterVirtualPathProvider(vpp.Instance);
    113                 }
    114             }
    115  
    116             ControllerBuilder.Current.SetControllerFactory(new OrchardControllerFactory());
    117             FilterProviders.Providers.Add(new OrchardFilterProvider());
    118  
    119             GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), new DefaultOrchardWebApiHttpControllerSelector(GlobalConfiguration.Configuration));
    120             GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new DefaultOrchardWebApiHttpControllerActivator(GlobalConfiguration.Configuration));
    121             GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
    122  
    123             GlobalConfiguration.Configuration.Filters.Add(new OrchardApiActionFilterDispatcher());
    124             GlobalConfiguration.Configuration.Filters.Add(new OrchardApiExceptionFilterDispatcher());
    125             GlobalConfiguration.Configuration.Filters.Add(new OrchardApiAuthorizationFilterDispatcher());
    126  
    127             ViewEngines.Engines.Clear();
    128             ViewEngines.Engines.Add(new ThemeAwareViewEngineShim());
    129  
    130             var hostContainer = new DefaultOrchardHostContainer(container);
    131             //MvcServiceLocator.SetCurrent(hostContainer);
    132             OrchardHostContainerRegistry.RegisterHostContainer(hostContainer);
    133  
    134             // Register localized data annotations
    135             ModelValidatorProviders.Providers.Clear();
    136             ModelValidatorProviders.Providers.Add(new LocalizedModelValidatorProvider());
    137  
    138             return container;
    139         }        
    View Code
    代码很长,但是可以分为以下几个部分:
    • 基础组件:日志、事件、缓存
    • 环境相关:路径、程序集、编译器,Http上下文
    • 异常处理:异常策略
    • 拓展相关:拓展目录、拓展依赖、拓展管理。
    • 多租户相关:Shell表、Shell启动。
    • 拓展的依赖注入:拓展模块中的自定义注入、Host及HostComponents配置文件的注入。
    • MVC相关:ControllerFactory替换,过滤器加载
    • WebAPI相关:Selector、Activator替换,DependencyResolver替换、过滤器加载
    • ViewEngine:使用ThemeAwareViewEngineShim。
    • Model相关:使用LocalizedModelValidatorProvider作为Model验证器。
     
    相对特殊的地方有:
    1. 日志的自动属性注入:
    Orchard使用Autofac实现了日志的属性自动注入,具体代码如下:
     1         protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) {
     2             var implementationType = registration.Activator.LimitType;
     3  
     4             // build an array of actions on this type to assign loggers to member properties
     5             var injectors = BuildLoggerInjectors(implementationType).ToArray();
     6  
     7             // if there are no logger properties, there's no reason to hook the activated event
     8             if (!injectors.Any())
     9                 return;
    10  
    11             // otherwise, whan an instance of this component is activated, inject the loggers on the instance
    12             registration.Activated += (s, e) => {
    13                 foreach (var injector in injectors)
    14                     injector(e.Context, e.Instance);
    15             };
    16         }
    17  
    18         private IEnumerable<Action<IComponentContext, object>> BuildLoggerInjectors(Type componentType) {
    19             // Look for settable properties of type "ILogger"
    20             var loggerProperties = componentType
    21                 .GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance)
    22                 .Select(p => new {
    23                     PropertyInfo = p,
    24                     p.PropertyType,
    25                     IndexParameters = p.GetIndexParameters(),
    26                     Accessors = p.GetAccessors(false)
    27                 })
    28                 .Where(x => x.PropertyType == typeof(ILogger)) // must be a logger
    29                 .Where(x => x.IndexParameters.Count() == 0) // must not be an indexer
    30                 .Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)); //must have get/set, or only set
    31  
    32             // Return an array of actions that resolve a logger and assign the property
    33             foreach (var entry in loggerProperties) {
    34                 var propertyInfo = entry.PropertyInfo;
    35  
    36                 yield return (ctx, instance) => {
    37                     string component = componentType.ToString();
    38                     if (component != instance.GetType().ToString()) {
    39                         return;
    40                     }
    41                     var logger = _loggerCache.GetOrAdd(component, key => ctx.Resolve<ILogger>(new TypedParameter(typeof(Type), componentType)));
    42                     propertyInfo.SetValue(instance, logger, null);
    43                 };
    44             }
    45         }    
    View Code

    其实它的核心就是注册时通过对象类型查找是否有一个类型为Ilogger的可读写属性,拿出来赋值。

     

      2. 通过动态代理实现的事件机制:

      在.Net中提到事件就会想到委托,但是Orchard的事件比较特殊,它将事件定义为一个接口(实现IEventHandler的接口),对于生产者(事件发布者)它仅需要通过Orchard的容器注入一个相应接口类型即可,而观察者(事件的真实处理器)则仅需要实现相应接口即可(其余框架会自动处理)。
    对.Net的委托不了解的可参考张子阳老师的博客:http://www.cnblogs.com/JimmyZhang/archive/2007/09/23/903360.html
    Orchard事件的使用如下(使用张子阳老师例子改了一下):
     1         public interface IBoilHandler : IEventHandler
     2         {
     3             void OnTemperatureChanged(int temperature);
     4         }
     5  
     6         // 热水器
     7         public class Heater
     8         {
     9             private int temperature;
    10             private IBoilHandler _boilhandler;
    11  
    12             public Heater(IBoilHandler boilhandler)
    13             {
    14                 _boilhandler = boilhandler;
    15             }
    16  
    17             // 烧水
    18             public void BoilWater()
    19             {
    20                 for (int i = 0; i <= 100; i++)
    21                 {
    22                     temperature = i;
    23  
    24                     if (temperature > 95)
    25                     {
    26                         if (_boilhandler != null)
    27                         { //如果有对象注册
    28                             _boilhandler.OnTemperatureChanged(temperature);  //调用所有注册对象的方法
    29                         }
    30                     }
    31                 }
    32             }
    33         }
    34  
    35         // 警报器
    36         public class Alarm : IBoilHandler
    37         {
    38             public void OnTemperatureChanged(int temperature)
    39             {
    40                 Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:", temperature);
    41             }
    42         }
    43  
    44         // 显示器
    45         public class Display : IBoilHandler
    46         {
    47             public void OnTemperatureChanged(int temperature)
    48             {
    49                 Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", temperature);
    50             }
    51         } 
     
    那么这是如何实现的呢?
    两个要点:
      1. 在注册服务时将所有通过继承IEventhandler的子接口(如上面代码的IBoilHandler)通过动态代理,将相应接口的方法执行全部拦截并转到EventBus上,调用Notify方法,其参数为当前接口名称+方法名称和相应的参数。--生产者
     1   public class EventsRegistrationSource : IRegistrationSource {
     2         private readonly DefaultProxyBuilder _proxyBuilder;
     3  
     4         public EventsRegistrationSource() {
     5             _proxyBuilder = new DefaultProxyBuilder();
     6         }
     7  
     8         public bool IsAdapterForIndividualComponents {
     9             get { return false; }
    10         }
    11  
    12         public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor) {
    13             var serviceWithType = service as IServiceWithType;
    14             if (serviceWithType == null)
    15                 yield break;
    16  
    17             var serviceType = serviceWithType.ServiceType;
    18             if (!serviceType.IsInterface || !typeof(IEventHandler).IsAssignableFrom(serviceType) || serviceType == typeof(IEventHandler))
    19                 yield break;
    20  
    21             var interfaceProxyType = _proxyBuilder.CreateInterfaceProxyTypeWithoutTarget(
    22                 serviceType,
    23                 new Type[0],
    24                 ProxyGenerationOptions.Default);
    25  
    26  
    27             var rb = RegistrationBuilder
    28                 .ForDelegate((ctx, parameters) => {
    29                     var interceptors = new IInterceptor[] { new EventsInterceptor(ctx.Resolve<IEventBus>()) };
    30                     var args = new object[] { interceptors, null };
    31                     return Activator.CreateInstance(interfaceProxyType, args);
    32                 })
    33                 .As(service);
    34  
    35             yield return rb.CreateRegistration();
    36         }
    37     }
    38  
    39     public class EventsInterceptor : IInterceptor {
    40         private readonly IEventBus _eventBus;
    41         private static readonly ConcurrentDictionary<Type,MethodInfo> _enumerableOfTypeTDictionary = new ConcurrentDictionary<Type, MethodInfo>();
    42  
    43         public EventsInterceptor(IEventBus eventBus) {
    44             _eventBus = eventBus;
    45         }
    46  
    47         public void Intercept(IInvocation invocation) {
    48             var interfaceName = invocation.Method.DeclaringType.Name;
    49             var methodName = invocation.Method.Name;
    50  
    51             var data = invocation.Method.GetParameters()
    52                 .Select((parameter, index) => new { parameter.Name, Value = invocation.Arguments[index] })
    53                 .ToDictionary(kv => kv.Name, kv => kv.Value);
    54  
    55             var results = _eventBus.Notify(interfaceName + "." + methodName, data);
    56  
    57             invocation.ReturnValue = Adjust(results, invocation.Method.ReturnType);
    58         }
    59  
    60         public static object Adjust(IEnumerable results, Type returnType) {
    61             if (returnType == typeof(void) ||
    62                 results == null ||
    63                 results.GetType() == returnType) {
    64                 return results;
    65             }
    66  
    67             // acquire method:
    68             // static IEnumerable<T> IEnumerable.OfType<T>(this IEnumerable source)
    69             // where T is from returnType's IEnumerable<T>
    70             var enumerableOfTypeT = _enumerableOfTypeTDictionary.GetOrAdd( returnType, type => typeof(Enumerable).GetGenericMethod("OfType", type.GetGenericArguments(), new[] { typeof(IEnumerable) }, typeof(IEnumerable<>)));
    71             return enumerableOfTypeT.Invoke(null, new[] { results });
    72         }
    73     }
    View Code

      上面代码有个重点就是:

    1  if (!serviceType.IsInterface || !typeof(IEventHandler).IsAssignableFrom(serviceType) || serviceType == typeof(IEventHandler))
    2                 yield break;

      如果不是接口、如果类型不是IEventHandler的子类型(子接口)且当前类型就是IEventHandler,满足这三个接口就跳出。正过来说就是:

    •  类型一定是接口
    •  一定是继承IEventHandler的接口
    •  一定不是IEventHandler本身
      2. 把所有实现了IEventHandler的实现全部解析到EventBus的_eventHandlers字段中。 --消费者
    换句话说就是,当应用程序初始化的时候EventBus里面的处理器都已经被加载了,但真正触发事件时,仅仅是通过拦截器将相应接口的方法转到了Notify方法上,进而去通知相应的Handlers执行。
    下面代码位于ShellContainerFactory.cs文件中
     1               if (typeof(IEventHandler).IsAssignableFrom(item.Type)) {
     2                             var interfaces = item.Type.GetInterfaces();
     3                             foreach (var interfaceType in interfaces) {
     4  
     5                                 // register named instance for each interface, for efficient filtering inside event bus
     6                                 // IEventHandler derived classes only
     7                                 if (interfaceType.GetInterface(typeof (IEventHandler).Name) != null) {
     8                                     registration = registration.Named<IEventHandler>(interfaceType.Name);
     9                                 }
    10                             }
    11                         }
     
      3. ICacheManager的注入:
    ICacheManager的特殊之处就在于依赖它的对象在通过构造注入ICacheManager会将当前对象作为参数来实例化ICacheManager。
     1         protected override void AttachToComponentRegistration(Autofac.Core.IComponentRegistry componentRegistry, Autofac.Core.IComponentRegistration registration) {
     2             var needsCacheManager = registration.Activator.LimitType
     3                 .GetConstructors()
     4                 .Any(x => x.GetParameters()
     5                     .Any(xx => xx.ParameterType == typeof(ICacheManager)));
     6  
     7             if (needsCacheManager) {
     8                 registration.Preparing += (sender, e) => {
     9                     var parameter = new TypedParameter(
    10                         typeof(ICacheManager),
    11                         e.Context.Resolve<ICacheManager>(new TypedParameter(typeof(Type), registration.Activator.LimitType)));
    12                     e.Parameters = e.Parameters.Concat(new[] { parameter });
    13                 };
    14             }
    15         }
     
    小结:
    本章主要是分析了Orchard中IoC容器的创建以及基础设施的注册,其中对Log、Event和CacheManager进行了特殊处理,并且处理方式均是使用Autofac提供的一系列特性,动态的对注册的组件进行额外操作,另外在Orchard中它还针对Shell级别创建了一个子容器,所以后续将对Orchard的依赖注入进一步分析。
     
    参考:
  • 相关阅读:
    Android4.0 以后不允许在主线程进行网络连接
    关于升级linux下apache
    事物复制中大项目(Large Article)出问题如何快速修复
    国企银行面试总结
    Git命令详解(一)-个人使用
    函数体的规模要小,尽量控制在 50 行代码之内
    函数的功能要单一,不要设计多用途的函数
    在函数体的“出口处”,对 return 语句的正确性和效率进行检查
    在函数体的“入口处”,对参数的有效性进行检查
    有些场合用“引用传递”替换“值传 递”可以提高效率
  • 原文地址:https://www.cnblogs.com/selimsong/p/5999181.html
Copyright © 2020-2023  润新知