NOP源码分析 二
昨天研究到:
builder.RegisterInstance(config).As<NopConfig>().SingleInstance();
builder.RegisterInstance(this).As<IEngine>().SingleInstance();
builder.RegisterInstance(typeFinder).As<ITypeFinder>().SingleInstance();
builder.Update(container);
注册NopConfig、NopEngine/WebAppTypeFinder
-------------------------------------------接着往下-------------------------------------------------
var drTypes = typeFinder.FindClassesOfType<IDependencyRegistrar>(); 这一句根据类名可知道,是根据给定类型(接口),获得类 就是获得IDependencyRegistrar类,其实就是一个依赖注入类。通过多态,最终调用的代码:return FindClassesOfType(assignTypeFrom, GetAssemblies(), onlyConcreteClasses);
其中获得集合代码:
/// <summary>Gets the assemblies related to the current implementation.</summary>
/// <returns>A list of assemblies that should be loaded by the Nop factory.</returns>
public virtual IList<Assembly> GetAssemblies()
{
var addedAssemblyNames = new List<string>();
var assemblies = new List<Assembly>();
if (LoadAppDomainAssemblies)
AddAssembliesInAppDomain(addedAssemblyNames, assemblies);
AddConfiguredAssemblies(addedAssemblyNames, assemblies);
return assemblies;
}
private bool ignoreReflectionErrors = true;
private bool loadAppDomainAssemblies = true;
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";
private string assemblyRestrictToLoadingPattern = ".*";
这是声明时默认设置, 执行
AddAssembliesInAppDomain(addedAssemblyNames, assemblies);代码如下:
/// <summary>
/// Iterates all assemblies in the AppDomain and if it's name matches the configured patterns add it to our list.迭代所有AppDomain的类库,
/// </summary>
/// <param name="addedAssemblyNames"></param>
/// <param name="assemblies"></param>
private void AddAssembliesInAppDomain(List<string> addedAssemblyNames, List<Assembly> assemblies)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (Matches(assembly.FullName))
{
if (!addedAssemblyNames.Contains(assembly.FullName))
{
assemblies.Add(assembly);
addedAssemblyNames.Add(assembly.FullName);
}
}
}
}
其中Matches调用
public virtual bool Matches(string assemblyFullName)
{
return !Matches(assemblyFullName, AssemblySkipLoadingPattern)
&& Matches(assemblyFullName, AssemblyRestrictToLoadingPattern);
}
最终调用如下:
protected virtual bool Matches(string assemblyFullName, string pattern)
{
return Regex.IsMatch(assemblyFullName, pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
}
可以看到不加载AssemblySkipLoadingPattern所有DLL,并且在AssemblyRestrictToLoadingPattern的匹配控制内。
继续往下(!addedAssemblyNames.Contains(assembly.FullName)) 不包含则添加assemblies.Add(assembly); 这是类库。addedAssemblyNames.Add(assembly.FullName); 这是名称。
感觉AddConfiguredAssemblies(addedAssemblyNames, assemblies);有点多余啊 ,记录一下。
调用代码如下: 这个方法最终就是返回assignTypeFrom所属类型的类的类型(非实例)的集合。
public IEnumerable<Type> FindClassesOfType(Type assignTypeFrom, IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true)
{
var result = new List<Type>();
try
{
foreach (var a in assemblies)
{
Type[] types = null;
try
{
types = a.GetTypes();
}
catch
{
//Entity Framework 6 doesn't allow getting types (throws an exception)
if (!ignoreReflectionErrors)
{
throw;
}
}
if (types != null)
{
foreach (var t in types)
{
if (assignTypeFrom.IsAssignableFrom(t) || (assignTypeFrom.IsGenericTypeDefinition && DoesTypeImplementOpenGeneric(t, assignTypeFrom)))
{
if (!t.IsInterface)
{
if (onlyConcreteClasses)
{
if (t.IsClass && !t.IsAbstract)
{
result.Add(t);
}
}
else
{
result.Add(t);
}
}
}
}
}
}
}
catch (ReflectionTypeLoadException ex)
{
var msg = string.Empty;
foreach (var e in ex.LoaderExceptions)
msg += e.Message + Environment.NewLine;
var fail = new Exception(msg, ex);
Debug.WriteLine(fail.Message, fail);
throw fail;
}
return result;
}
回到NopEngine
foreach (var drType in drTypes)
drInstances.Add((IDependencyRegistrar) Activator.CreateInstance(drType));
添加实例。
最后根据dependencyRegistrar.order进行排序,用dependencyRegistrar.Register(builder, typeFinder);方法进行依赖注入注册。
this._containerManager = new ContainerManager(container);
声明容器管理器 传入autofac的容器对象。
最后一句:
//set dependency resolver
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
以前深入研究过DependencyResolver,但现在几乎都忘了。只知道好像是MVC实现依赖的一个扩展点。这里我理解就是用Autofac接管MVC的控制反转。
参考http://www.cnblogs.com/miku/archive/2013/01/16/2862675.html 、http://www.cnblogs.com/hkncd/archive/2012/11/28/2792474.html 、http://book.2cto.com/201212/12051.html等。
回到Init。。。。方法最后一局,如果没有启动任务,则执行RunStartupTasks();代码如下:
/// <summary>
/// Run startup tasks
/// </summary>
protected virtual void RunStartupTasks()
{
var typeFinder = _containerManager.Resolve<ITypeFinder>();
var startUpTaskTypes = typeFinder.FindClassesOfType<IStartupTask>();
var startUpTasks = new List<IStartupTask>();
foreach (var startUpTaskType in startUpTaskTypes)
startUpTasks.Add((IStartupTask)Activator.CreateInstance(startUpTaskType));
//sort
startUpTasks = startUpTasks.AsQueryable().OrderBy(st => st.Order).ToList();
foreach (var startUpTask in startUpTasks)
startUpTask.Execute();
}
第一句Resolve的代码如下:
public T Resolve<T>(string key = "", ILifetimeScope scope = null) where T : class
{
if (scope == null)
{
//no scope specified
scope = Scope();
}
if (string.IsNullOrEmpty(key))
{
return scope.Resolve<T>();
}
return scope.ResolveKeyed<T>(key);
}
Scope(),作用域的代码如下:
public ILifetimeScope Scope()
{
try
{
if (HttpContext.Current != null)
return AutofacDependencyResolver.Current.RequestLifetimeScope;
//when such lifetime scope is returned, you should be sure that it'll be disposed once used (e.g. in schedule tasks)
return Container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
}
catch (Exception)
{
//we can get an exception here if RequestLifetimeScope is already disposed
//for example, requested in or after "Application_EndRequest" handler
//but note that usually it should never happen
//when such lifetime scope is returned, you should be sure that it'll be disposed once used (e.g. in schedule tasks)
return Container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
}
}
这里不做郭拓探讨,因为我也糊涂(
) 其实就是默认返回Autofac当前请求的作用范围,然后返回需要的对象。
var startUpTaskTypes = typeFinder.FindClassesOfType<IStartupTask>();
找到所有启动类的集合。最后排序,调用startUpTask.Execute();方法。
终于完了,回到了Golbal.asax.cs文件的RegisterRoutes方法。
var routePublisher = EngineContext.Current.Resolve<IRoutePublisher>(); 我们仅仅研究了Current,就执行了很多,初始化NOP引擎、初始化依赖注入Autofac、执行所有启动任务。
然后我们看Resolve方法。和上面的一样,返回路由发布者、注册路由。http://blog.csdn.net/francislaw/article/details/7429317 第四个参数是限制参数 比如id必须是数字等(这里没第四参数)
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "Nop.Web.Controllers" }//第五个参数为命名空间,即该路由匹配在那个命名空间下有效
);
Application_Start 方法
protected void Application_Start()
{
//initialize engine context
EngineContext.Initialize(false);
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); 不用说了,就是初始化NopEngine.
第二句 bool databaseInstalled = DataSettingsHelper.DatabaseIsInstalled(); 名字就猜到了是判断数据库是否生成。代码如下:
namespace Nop.Core.Data
{
public partial class DataSettingsHelper
{
private static bool? _databaseIsInstalled;
public static bool DatabaseIsInstalled()
{
if (!_databaseIsInstalled.HasValue)
{
var manager = new DataSettingsManager();
var settings = manager.LoadSettings();
_databaseIsInstalled = settings != null && !String.IsNullOrEmpty(settings.DataConnectionString);
}
return _databaseIsInstalled.Value;
}
public static void ResetCache()
{
_databaseIsInstalled = null;
}
}
}
通过manager加载配置,并通过判断配置连接字符串存在则返回true. 下面是加载代码:
public virtual DataSettings LoadSettings(string filePath = null)
{
if (String.IsNullOrEmpty(filePath))
{
//use webHelper.MapPath instead of HostingEnvironment.MapPath which is not available in unit tests
filePath = Path.Combine(MapPath("~/App_Data/"), filename);
}
if (File.Exists(filePath))
{
string text = File.ReadAllText(filePath);
return ParseSettings(text);
}
return new DataSettings();
}
初始设置:
protected const char separator = ':';
protected const string filename = "Settings.txt";
在App_Data文件夹下找到文件 内容是:
DataProvider: sqlserver
DataConnectionString: Data Source=.;Initial Catalog=NOPCDB;Integrated Security=False;Persist Security Info=False;User ID=sa;Password=123
刚开始肯定返回是 所以不会执行,但以后每次都执行。 所以暂且分析一下:
if (databaseInstalled)
{
//remove all view engines
ViewEngines.Engines.Clear();
//except the themeable razor view engine we use
ViewEngines.Engines.Add(new ThemeableRazorViewEngine());
}
清除所有view engines,添加ThemeableRazorViewEngine。我们分析下ThemeableRazorViewEngine。
这个视图引擎 以前分析过,但差不多也忘了,大体就是设置自己的视图引擎路径,一下是代码:
public class ThemeableRazorViewEngine : ThemeableVirtualPathProviderViewEngine
{
public ThemeableRazorViewEngine()
{
AreaViewLocationFormats = new[]
{
//themes
"~/Areas/{2}/Themes/{3}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Themes/{3}/Views/Shared/{0}.cshtml",
//default
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
};
AreaMasterLocationFormats = new[]
{
//themes
"~/Areas/{2}/Themes/{3}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Themes/{3}/Views/Shared/{0}.cshtml",
//default
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
};
AreaPartialViewLocationFormats = new[]
{
//themes
"~/Areas/{2}/Themes/{3}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Themes/{3}/Views/Shared/{0}.cshtml",
//default
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.cshtml"
};
ViewLocationFormats = new[]
{
//themes
"~/Themes/{2}/Views/{1}/{0}.cshtml",
"~/Themes/{2}/Views/Shared/{0}.cshtml",
//default
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
//Admin
"~/Administration/Views/{1}/{0}.cshtml",
"~/Administration/Views/Shared/{0}.cshtml",
};
MasterLocationFormats = new[]
{
//themes
"~/Themes/{2}/Views/{1}/{0}.cshtml",
"~/Themes/{2}/Views/Shared/{0}.cshtml",
//default
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml"
};
PartialViewLocationFormats = new[]
{
//themes
"~/Themes/{2}/Views/{1}/{0}.cshtml",
"~/Themes/{2}/Views/Shared/{0}.cshtml",
//default
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
//Admin
"~/Administration/Views/{1}/{0}.cshtml",
"~/Administration/Views/Shared/{0}.cshtml",
};
FileExtensions = new[] { "cshtml" };
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
IEnumerable<string> fileExtensions = base.FileExtensions;
return new RazorView(controllerContext, partialPath, null, false, fileExtensions);
//return new RazorView(controllerContext, partialPath, layoutPath, runViewStartPages, fileExtensions, base.ViewPageActivator);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
IEnumerable<string> fileExtensions = base.FileExtensions;
return new RazorView(controllerContext, viewPath, masterPath, true, fileExtensions);
}
}
先放一下视图引擎,单独研究吧,也不是很难。

浙公网安备 33010602011771号