NopCommerce源码架构详解--TypeFinder程序集类型自动查找及操作相关源码分析

今天我们来看看NopCommerce源码一个值得借鉴的设计,TypeFinder类型自动查找。它会自动去程序的bin目录或者AppDomain下查找满足指定类型的的所有类型的集合。比如:

 
  1. var consumers = typeFinder.FindClassesOfType(typeof(IConsumer<>)).ToList();

上面代码就是查找实现接口所有的类。仅可以查找指定类型,还可以获取全部的程序集,如下:

 
  1. typeFinder.GetAssemblies().ToArray()

利用以上两点就可以利用IoC容器对查找的类型进行遍历注册。下面是我从nopCommerce源码中找到关于IoC和TypeFinder使用的代码。

 
  1. builder.RegisterControllers(typeFinder.GetAssemblies().ToArray());
 
  1. var consumers = typeFinder.FindClassesOfType(typeof(IConsumer<>)).ToList();
  2. foreach (var consumer in consumers)
  3. {
  4. builder.RegisterType(consumer)
  5. .As(consumer.FindInterfaces((type, criteria) =>
  6. {
  7. var isMatch = type.IsGenericType && ((Type)criteria).IsAssignableFrom(type.GetGenericTypeDefinition());
  8. return isMatch;
  9. }, typeof(IConsumer<>)))
  10. .InstancePerLifetimeScope();
  11. }

 

从上面我们知道了Nop中TypeFinder的作用,那么它又是怎么实现的呢?下面我就来说说TypeFinder的原理。同样我们来看看这部分相关代码的类图:

TypeFinder相关的类及作用:

1、ITypeFinder:类型查找抽象接口

2、AppDomainTypeFinder:在当前运行中的AppDomain中查找相关类型的集合

3、WebAppTypeFinder:在当前的web App的bin查找相关类型的集合

4、DependencyRegistrar:依赖注册

5、NopEngine:Nop引擎

接下来我们来看看这些类的核心代码及作用。

Nop.Core.Infrastructure.ITypeFinder

 
  1. public interface ITypeFinder
  2. {
  3. IList<Assembly> GetAssemblies();
  4.  
  5. IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, bool onlyConcreteClasses = true);
  6.  
  7. IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true);
  8.  
  9. IEnumerable<Type> FindClassesOfType<T>(bool onlyConcreteClasses = true);
  10.  
  11. IEnumerable<Type> FindClassesOfType<T>(IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true);
  12. }

 

Nop.Core.Infrastructure.AppDomainTypeFinder

 
  1. public class AppDomainTypeFinder : ITypeFinder
  2. {
  3. #region Fields
  4.  
  5. private bool ignoreReflectionErrors = true;
  6. private bool loadAppDomainAssemblies = true;
  7. //忽略一些系统程序集及其它特殊程序集
  8. private string assemblySkipLoadingPattern = "^System|^mscorlib|^Microsoft|^AjaxControlToolkit|^Antlr3|^Autofac|^AutoMapper|^Castle|^ComponentArt|^CppCodeProvider|^DotNetOpenAuth|^EntityFramework|^EPPlus|^FluentValidation|^ImageResizer|^itextsharp|^log4net|^MaxMind|^MbUnit|^MiniProfiler|^Mono.Math|^mvcContrib|^Newtonsoft|^NHibernate|^nunit|^Org.Mentalis|^PerlRegex|^QuickGraph|^Recaptcha|^Remotion|^RestSharp|^Rhino|^Telerik|^Iesi|^TestDriven|^TestFu|^UserAgentStringLibrary|^VJSharpCodeProvider|^WebActivator|^WebDev|^WebGrease";
  9. private string assemblyRestrictToLoadingPattern = ".*";
  10. private IList<string> assemblyNames = new List<string>();
  11.  
  12. #endregion
  13.  
  14.  
  15. #region Methods
  16.  
  17. public IEnumerable<Type> FindClassesOfType<T>(bool onlyConcreteClasses = true)
  18. {
  19. return FindClassesOfType(typeof(T), onlyConcreteClasses);
  20. }
  21.  
  22. public IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, bool onlyConcreteClasses = true)
  23. {
  24. return FindClassesOfType(assignTypeFrom, GetAssemblies(), onlyConcreteClasses);
  25. }
  26.  
  27. public IEnumerable<Type> FindClassesOfType<T>(IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true)
  28. {
  29. return FindClassesOfType(typeof (T), assemblies, onlyConcreteClasses);
  30. }
  31.  
  32. public IEnumerable<Type> FindClassesOfType(Type assignTypeFrom,
  33. IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true)
  34. {
  35. var result = new List<Type>();
  36. try
  37. {
  38. foreach (var a in assemblies)
  39. {
  40. Type[] types = null;
  41. try
  42. {
  43. types = a.GetTypes();
  44. }
  45. catch
  46. {
  47. //Entity Framework 6 doesn't allow getting types (throws an exception)
  48. if (!ignoreReflectionErrors)
  49. {
  50. throw;
  51. }
  52. }
  53. if (types != null)
  54. {
  55. foreach (var t in types)
  56. {
  57. if (assignTypeFrom.IsAssignableFrom(t)
  58. || (assignTypeFrom.IsGenericTypeDefinition && DoesTypeImplementOpenGeneric(t, assignTypeFrom)))
  59. {
  60. if (!t.IsInterface)
  61. {
  62. if (onlyConcreteClasses)
  63. {
  64. if (t.IsClass && !t.IsAbstract)
  65. {
  66. result.Add(t);
  67. }
  68. }
  69. else
  70. {
  71. result.Add(t);
  72. }
  73. }
  74. }
  75. }
  76. }
  77. }
  78. }
  79. catch (ReflectionTypeLoadException ex)
  80. {
  81. var msg = string.Empty;
  82. foreach (var e in ex.LoaderExceptions)
  83. msg += e.Message + Environment.NewLine;
  84.  
  85. var fail = new Exception(msg, ex);
  86. Debug.WriteLine(fail.Message, fail);
  87.  
  88. throw fail;
  89. }
  90. return result;
  91. }
  92.  
  93. /// <summary>获取指定类型的所有实现类</summary>
  94. /// <returns>A list of assemblies that should be loaded by the Nop factory.</returns>
  95. public virtual IList<Assembly> GetAssemblies()
  96. {
  97. var addedAssemblyNames = new List<string>();
  98. var assemblies = new List<Assembly>();
  99.  
  100. if (LoadAppDomainAssemblies)
  101. AddAssembliesInAppDomain(addedAssemblyNames, assemblies);
  102. AddConfiguredAssemblies(addedAssemblyNames, assemblies);
  103.  
  104. return assemblies;
  105. }
  106.  
  107. #endregion
  108.  
  109. #region Utilities
  110.  
  111. /// <summary>
  112. /// 遍历AppDomain下的所有程序集并与指定类型进行比较,如果匹配就加入要返回的List中
  113. /// </summary>
  114. /// <param name="addedAssemblyNames"></param>
  115. /// <param name="assemblies"></param>
  116. private void AddAssembliesInAppDomain(List<string> addedAssemblyNames, List<Assembly> assemblies)
  117. {
  118. foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
  119. {
  120. if (Matches(assembly.FullName))
  121. {
  122. if (!addedAssemblyNames.Contains(assembly.FullName))
  123. {
  124. assemblies.Add(assembly);
  125. addedAssemblyNames.Add(assembly.FullName);
  126. }
  127. }
  128. }
  129. }
  130.  
  131. /// <summary>
  132. /// 添加特定程序集到到集合中
  133. /// </summary>
  134. /// <param name="addedAssemblyNames"></param>
  135. /// <param name="assemblies"></param>
  136. protected virtual void AddConfiguredAssemblies(List<string> addedAssemblyNames, List<Assembly> assemblies)
  137. {
  138. foreach (string assemblyName in AssemblyNames)
  139. {
  140. Assembly assembly = Assembly.Load(assemblyName);
  141. if (!addedAssemblyNames.Contains(assembly.FullName))
  142. {
  143. assemblies.Add(assembly);
  144. addedAssemblyNames.Add(assembly.FullName);
  145. }
  146. }
  147. }
  148.  
  149. /// <summary>
  150. /// Check if a dll is one of the shipped dlls that we know don't need to be investigated.
  151. /// </summary>
  152. /// <param name="assemblyFullName">
  153. /// The name of the assembly to check.
  154. /// </param>
  155. /// <returns>
  156. /// True if the assembly should be loaded into Nop.
  157. /// </returns>
  158. public virtual bool Matches(string assemblyFullName)
  159. {
  160. return !Matches(assemblyFullName, AssemblySkipLoadingPattern)
  161. && Matches(assemblyFullName, AssemblyRestrictToLoadingPattern);
  162. }
  163.  
  164. /// <summary>
  165. /// Check if a dll is one of the shipped dlls that we know don't need to be investigated.
  166. /// </summary>
  167. /// <param name="assemblyFullName">
  168. /// The assembly name to match.
  169. /// </param>
  170. /// <param name="pattern">
  171. /// The regular expression pattern to match against the assembly name.
  172. /// </param>
  173. /// <returns>
  174. /// True if the pattern matches the assembly name.
  175. /// </returns>
  176. protected virtual bool Matches(string assemblyFullName, string pattern)
  177. {
  178. return Regex.IsMatch(assemblyFullName, pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
  179. }
  180.  
  181. /// <summary>
  182. /// Makes sure matching assemblies in the supplied folder are loaded in the app domain.
  183. /// </summary>
  184. /// <param name="directoryPath">
  185. /// The physical path to a directory containing dlls to load in the app domain.
  186. /// </param>
  187. protected virtual void LoadMatchingAssemblies(string directoryPath)
  188. {
  189. var loadedAssemblyNames = new List<string>();
  190. foreach (Assembly a in GetAssemblies())
  191. {
  192. loadedAssemblyNames.Add(a.FullName);
  193. }
  194.  
  195. if (!Directory.Exists(directoryPath))
  196. {
  197. return;
  198. }
  199. //获取指定目录下以.dll为结尾的文件
  200. foreach (string dllPath in Directory.GetFiles(directoryPath, "*.dll"))
  201. {
  202. try
  203. {
  204. var an = AssemblyName.GetAssemblyName(dllPath);
  205. if (Matches(an.FullName) && !loadedAssemblyNames.Contains(an.FullName))
  206. {
  207. App.Load(an);//加载程序集到当前AppDomain中
  208. }
  209.  
  210. //old loading stuff
  211. //Assembly a = Assembly.ReflectionOnlyLoadFrom(dllPath);
  212. //if (Matches(a.FullName) && !loadedAssemblyNames.Contains(a.FullName))
  213. //{
  214. // App.Load(a.FullName);
  215. //}
  216. }
  217. catch (BadImageFormatException ex)
  218. {
  219. Trace.TraceError(ex.ToString());
  220. }
  221. }
  222. }
  223.  
  224. /// <summary>
  225. /// Does type implement generic?
  226. /// </summary>
  227. /// <param name="type"></param>
  228. /// <param name="openGeneric"></param>
  229. /// <returns></returns>
  230. protected virtual bool DoesTypeImplementOpenGeneric(Type type, Type openGeneric)
  231. {
  232. try
  233. {
  234. var genericTypeDefinition = openGeneric.GetGenericTypeDefinition();
  235. foreach (var implementedInterface in type.FindInterfaces((objType, objCriteria) => true, null))
  236. {
  237. if (!implementedInterface.IsGenericType)
  238. continue;
  239.  
  240. var isMatch = genericTypeDefinition.IsAssignableFrom(implementedInterface.GetGenericTypeDefinition());
  241. return isMatch;
  242. }
  243. return false;
  244. }catch
  245. {
  246. return false;
  247. }
  248. }
  249.  
  250. #endregion
  251. }

 

Nop.Core.Infrastructure.WebAppTypeFinder

WebAppTypeFinder是继承于类AppDomainTypeFinder

 
  1. public class WebAppTypeFinder : AppDomainTypeFinder
  2. {
  3. #region Fields
  4.  
  5. private bool _ensureBinFolderAssembliesLoaded = true;
  6. private bool _binFolderAssembliesLoaded = false;
  7.  
  8. #endregion
  9.  
  10. #region Ctor
  11.  
  12. public WebAppTypeFinder(NopConfig config)
  13. {
  14. this._ensureBinFolderAssembliesLoaded = config.DynamicDiscovery;
  15. }
  16.  
  17. #endregion
  18.  
  19. #region Properties
  20.  
  21. /// <summary>
  22. /// Gets or sets wether assemblies in the bin folder of the web application should be specificly checked for beeing loaded on application load. This is need in situations where plugins need to be loaded in the AppDomain after the application been reloaded.
  23. /// </summary>
  24. public bool EnsureBinFolderAssembliesLoaded
  25. {
  26. get { return _ensureBinFolderAssembliesLoaded; }
  27. set { _ensureBinFolderAssembliesLoaded = value; }
  28. }
  29.  
  30. #endregion
  31.  
  32. #region Methods
  33.  
  34. /// <summary>
  35. /// Gets a physical disk path of \Bin directory
  36. /// </summary>
  37. /// <returns>The physical path. E.g. "c:\inetpub\wwwroot\bin"</returns>
  38. public virtual string GetBinDirectory()
  39. {
  40. if (HostingEnvironment.IsHosted)
  41. {
  42. //hosted
  43. return HttpRuntime.BinDirectory;
  44. }
  45. else
  46. {
  47. //not hosted. For example, run either in unit tests
  48. return AppDomain.CurrentDomain.BaseDirectory;
  49. }
  50. }
  51.  
  52. public override IList<Assembly> GetAssemblies()
  53. {
  54. if (this.EnsureBinFolderAssembliesLoaded && !_binFolderAssembliesLoaded)
  55. {
  56. _binFolderAssembliesLoaded = true;
  57. string binPath = GetBinDirectory();
  58. //binPath = _webHelper.MapPath("~/bin");
  59. LoadMatchingAssemblies(binPath);
  60. }
  61.  
  62. return base.GetAssemblies();
  63. }
  64.  
  65. #endregion
  66. }

 

在Nop.Core.Infrastructure.NopEngine类中有以下方法:

 
  1. protected virtual void RegisterDependencies(NopConfig config)
  2. {
  3. var builder = new ContainerBuilder();
  4. var container = builder.Build();
  5.  
  6. //we create new instance of ContainerBuilder
  7. //because Build() or Update() method can only be called once on a ContainerBuilder.
  8.  
  9.  
  10. //dependencies
  11. var typeFinder = new WebAppTypeFinder(config);
  12. builder = new ContainerBuilder();
  13. builder.RegisterInstance(config).As<NopConfig>().SingleInstance();
  14. builder.RegisterInstance(this).As<IEngine>().SingleInstance();
  15. //ITypeFinder接口用WebAppTypeFinder的单例对象注册绑定
  16.   builder.RegisterInstance(typeFinder).As<ITypeFinder>().SingleInstance();
  17. builder.Update(container);
  18.  
  19. //register dependencies provided by other assemblies
  20. builder = new ContainerBuilder();
  21. var drTypes = typeFinder.FindClassesOfType<IDependencyRegistrar>();
  22. var drInstances = new List<IDependencyRegistrar>();
  23. foreach (var drType in drTypes)
  24. drInstances.Add((IDependencyRegistrar) Activator.CreateInstance(drType));
  25. //sort
  26. drInstances = drInstances.AsQueryable().OrderBy(t => t.Order).ToList();
  27. foreach (var dependencyRegistrar in drInstances)
  28. dependencyRegistrar.Register(builder, typeFinder);
  29. builder.Update(container);
  30.  
  31.  
  32. this._containerManager = new ContainerManager(container);
  33.  
  34. //set dependency resolver
  35. DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
  36. }

 

可以看到上面 typeFinder.FindClassesOfType<IDependencyRegistrar>(),查找依赖注入抽象接口的IDependencyRegistrar的全部实现类型的集合,并依次调用其方法Register。

Nop依赖注入相关原理分析看看我以前的文章NopCommerce源码架构详解--Autofac依赖注入分析

同样我们在Nop.Web.Framework.DependencyRegistrar看到下面使用ITypeFinder注册依赖对象,如下:

 
  1. public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder)
  2. {
  3. //...省略其它代码
  4.  
  5. //controllers
  6. builder.RegisterControllers(typeFinder.GetAssemblies().ToArray());
  7.  
  8. //...省略其它代码
  9.  
  10. //Register event consumers
  11. var consumers = typeFinder.FindClassesOfType(typeof(IConsumer<>)).ToList();
  12. foreach (var consumer in consumers)
  13. {
  14. builder.RegisterType(consumer)
  15. .As(consumer.FindInterfaces((type, criteria) =>
  16. {
  17. var isMatch = type.IsGenericType && ((Type)criteria).IsAssignableFrom(type.GetGenericTypeDefinition());
  18. return isMatch;
  19. }, typeof(IConsumer<>)))
  20. .InstancePerLifetimeScope();
  21. }
  22. }

 

posted on 2024-04-12 00:15  大西瓜3721  阅读(18)  评论(0编辑  收藏  举报

导航