博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Orchard详解--第三篇 依赖注入之基础设施
阅读量:5336 次
发布时间:2019-06-15

本文共 17841 字,大约阅读时间需要 59 分钟。

  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
registrations) {2             var container = CreateHostContainer(registrations);3             return container.Resolve
();4         }

 

以下是容器创建的全量代码:
1         public static IContainer CreateHostContainer(Action
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
().As
().SingleInstance(); 15             builder.RegisterType
().As
().SingleInstance(); 16             builder.RegisterType
().As
().SingleInstance(); 17             builder.RegisterType
().As
().SingleInstance(); 18             builder.RegisterType
().As
().SingleInstance(); 19             builder.RegisterType
().As
().SingleInstance(); 20             builder.RegisterType
().As
().Named
(typeof(IShellSettingsManagerEventHandler).Name).SingleInstance(); 21             builder.RegisterType
().As
().SingleInstance(); 22             builder.RegisterType
().As
().SingleInstance(); 23             builder.RegisterType
().As
().SingleInstance(); 24             builder.RegisterType
().As
().SingleInstance(); 25             builder.RegisterType
().As
().SingleInstance(); 26             builder.RegisterType
().As
().SingleInstance(); 27             builder.RegisterType
().As
().SingleInstance(); 28             builder.RegisterType
().As
().SingleInstance(); 29             builder.RegisterType
().As
().SingleInstance(); 30             builder.RegisterType
().As
().SingleInstance(); 31             builder.RegisterType
().As
().InstancePerDependency(); 32             builder.RegisterType
().As
().SingleInstance(); 33             builder.RegisterType
().As
().SingleInstance(); 34             builder.RegisterType
().As
().SingleInstance(); 35             //builder.RegisterType
().As
().SingleInstance(); 36   37             RegisterVolatileProvider
(builder); 38             RegisterVolatileProvider
(builder); 39             RegisterVolatileProvider
(builder); 40             RegisterVolatileProvider
(builder); 41             RegisterVolatileProvider
(builder); 42             RegisterVolatileProvider
(builder); 43             RegisterVolatileProvider
(builder); 44             RegisterVolatileProvider
(builder); 45             RegisterVolatileProvider
(builder); 46             47             builder.RegisterType
().As
().As
() 48                 .Named
(typeof(IShellSettingsManagerEventHandler).Name) 49                 .Named
(typeof(IShellDescriptorManagerEventHandler).Name) 50                 .SingleInstance(); 51             { 52                 builder.RegisterType
().As
().SingleInstance(); 53   54                 builder.RegisterType
().As
().SingleInstance(); 55                 { 56                     builder.RegisterType
().As
().SingleInstance(); 57   58                     builder.RegisterType
().As
().SingleInstance(); 59                     { 60                         builder.RegisterType
().As
().SingleInstance(); 61                         builder.RegisterType
().As
().SingleInstance(); 62                         builder.RegisterType
().As
().SingleInstance(); 63                         builder.RegisterType
().As
().SingleInstance(); 64                         { 65                             builder.RegisterType
().As
().SingleInstance(); 66                             builder.RegisterType
().As
().SingleInstance() 67                                 .WithParameter(new NamedParameter("paths", extensionLocations.ModuleLocations)); 68                             builder.RegisterType
().As
().SingleInstance() 69                                 .WithParameter(new NamedParameter("paths", extensionLocations.CoreLocations)); 70                             builder.RegisterType
().As
().SingleInstance() 71                                 .WithParameter(new NamedParameter("paths", extensionLocations.ThemeLocations)); 72   73                             builder.RegisterType
().As
().SingleInstance(); 74                             builder.RegisterType
().As
().SingleInstance(); 75                             builder.RegisterType
().As
().SingleInstance(); 76                             builder.RegisterType
().As
().SingleInstance(); 77                             builder.RegisterType
().As
().SingleInstance(); 78                         } 79                     } 80   81                     builder.RegisterType
().As
().SingleInstance(); 82                 } 83   84                 builder.RegisterType
().As
().SingleInstance(); 85             } 86   87             builder.RegisterType
().As
().SingleInstance(); 88             builder.RegisterType
().As
().InstancePerMatchingLifetimeScope("shell"); 89             builder.RegisterType
().As
().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 Providers109             //110             if (HostingEnvironment.IsHosted) {111                 foreach (var vpp in container.Resolve
>()) {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 annotations135             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 instance12             registration.Activated += (s, e) => {13                 foreach (var injector in injectors)14                     injector(e.Context, e.Instance);15             };16         }17  18         private IEnumerable
> BuildLoggerInjectors(Type componentType) {19             // Look for settable properties of type "ILogger"20             var loggerProperties = componentType21                 .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 logger29                 .Where(x => x.IndexParameters.Count() == 0) // must not be an indexer30                 .Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)); //must have get/set, or only set31  32             // Return an array of actions that resolve a logger and assign the property33             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
(new TypedParameter(typeof(Type), componentType)));42                     propertyInfo.SetValue(instance, logger, null);43                 };44             }45         }
View Code

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

 

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

  在.Net中提到事件就会想到委托,但是Orchard的事件比较特殊,它将事件定义为一个接口(实现IEventHandler的接口),对于生产者(事件发布者)它仅需要通过Orchard的容器注入一个相应接口类型即可,而观察者(事件的真实处理器)则仅需要实现相应接口即可(其余框架会自动处理)。
对.Net的委托不了解的可参考张子阳老师的博客:
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 : IBoilHandler37         {38             public void OnTemperatureChanged(int temperature)39             {40                 Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:", temperature);41             }42         }43  44         // 显示器45         public class Display : IBoilHandler46         {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
RegistrationsFor(Service service, Func
> 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 = RegistrationBuilder28                 .ForDelegate((ctx, parameters) => {29                     var interceptors = new IInterceptor[] { new EventsInterceptor(ctx.Resolve
()) };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
_enumerableOfTypeTDictionary = new ConcurrentDictionary
();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
IEnumerable.OfType
(this IEnumerable source)69             // where T is from returnType's IEnumerable
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
(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
(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的依赖注入进一步分析。
 
参考:

转载于:https://www.cnblogs.com/selimsong/p/5999181.html

你可能感兴趣的文章
字典常用方法
查看>>
Spring Cloud Stream消费失败后的处理策略(三):使用DLQ队列(RabbitMQ)
查看>>
bzoj1048 [HAOI2007]分割矩阵
查看>>
Java中的编码
查看>>
PKUWC2018 5/6
查看>>
As-If-Serial 理解
查看>>
洛谷P1005 矩阵取数游戏
查看>>
在Silverlight中使用HierarchicalDataTemplate为TreeView实现递归树状结构
查看>>
无线通信基础(一):无线网络演进
查看>>
如何在工作中快速成长?阿里资深架构师给工程师的10个简单技巧
查看>>
WebSocket 时时双向数据,前后端(聊天室)
查看>>
关于python中带下划线的变量和函数 的意义
查看>>
linux清空日志文件内容 (转)
查看>>
安卓第十三天笔记-服务(Service)
查看>>
Servlet接收JSP参数乱码问题解决办法
查看>>
【bzoj5016】[Snoi2017]一个简单的询问 莫队算法
查看>>
Ajax : load()
查看>>
MySQL-EXPLAIN执行计划Extra解释
查看>>
Zookeeper概述
查看>>
Zookeeper一致性级别
查看>>