最近抽空看了一下ASP.NET MVC的部分源码,顺带写篇文章作个笔记以便往后查看。缓存
在UrlRoutingModule模块中,将请求处理程序映射到了MvcHandler中,所以,提及Controller的激活,首先要从MvcHandler入手,MvcHandler实现了三个接口:IHttpAsyncHandler, IHttpHandler, IRequiresSessionState。 其处理逻辑主要实如今同步和异步的ProcessRequest方法中,总的来讲,该方法在执行的时候,大体经历如下几个步骤:闭包
其中第一步在ProcessRequestInit方法中进行处理,本文主要是分析第两步中的controller是如何建立出来的。less
Controller的建立是经过ControllerFactory实现的,而ControllerFactory的建立又是在ControllerBuilder中完成的,所以咱们先了解一下ControllerBuilder的工做原理。异步
从源码中能够看出,在ControllerBuilder类中,并无直接实现对controller工厂的建立,ControllerFactory的建立其实是委托给一个继承自IResolver接口的SingleServiceResolver类的实例来实现的,这一点从GetControllerFactory方法中能够看出,它是经过调用SingleServiceResolver对象的Current属性来完成controller工厂的建立的。ide
public IControllerFactory GetControllerFactory() { return _serviceResolver.Current; //依赖IResolver接口建立工厂 }
而且在源码中还发现,SingleServiceResolver类是internal级别的,这意味着外部没法直接访问,那么ControllerBuilder是如何借助SingleServiceResolver来实现工厂的注册呢?继续看代码,ControllerBuilder类和SingleServiceResolver类都有一个Func<IControllerFactory>
类型的委托字段,咱们姑且称为工厂委托,函数
//ControllerBuilder.cs private Func<IControllerFactory> _factoryThunk = () => null; //工厂委托 //SingleServiceResolver.cs private Func<TService> _currentValueThunk; //工厂委托
该委托实现了工厂的建立,而经过SetControllerFactory方法仅仅是更改了ControllerBuilder类的工厂委托字段,并无更改SingleServiceResolver类的工厂委托字段,post
public void SetControllerFactory(IControllerFactory controllerFactory) { if (controllerFactory == null) { throw new ArgumentNullException("controllerFactory"); } _factoryThunk = () => controllerFactory; //更改ControllerBuilder的工厂委托字段 }
所以必须将相应的更改应用到SingleServiceResolver类中才能实现真正的注册,咱们知道,若是是单纯的引用赋值,那么更改一个引用并不会对另一个引用形成改变,好比:测试
Func<object> f1 = ()=>null; Func<object> f2 = f1; //f1与f2指向同一个对象 object o = new object(); f1 = ()=>o; //更改f1后,f2仍然指向以前的对象 bool b1 = f1() == o; //true bool b2 = f2() == null; //true, f1()!=f2()
因此,ControllerBuilder在实例化SingleServiceResolver对象的时候,并无将自身的工厂委托字段直接赋值给SingleServiceResolver对象的对应字段(由于这样的话SetControllerFactory方法注册的委托没法应用到SingleServiceResolver对象中),而是经过委托来进行了包装,这样就会造成一个闭包,在闭包中进行引用,以下所示:ui
Func<object> f1 = ()=>null; Func<object> f2 = ()=>f1(); //经过委托包装f1,造成闭包 object o = new object(); f1 = ()=>o; //更改f1后,f2与f1保持同步 bool b1 = f1() == o; //true bool b2 = f2() == o; //true, f1()==f2() //ControllerBuilder.cs internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver) { _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>( () => _factoryThunk(), //封装委托,闭包引用 new DefaultControllerFactory { ControllerBuilder = this }, "ControllerBuilder.GetControllerFactory"); }
这样SingleServiceResolver对象中的工厂委托就会与ControllerBuilder对象中的对应字段保持同步了,SetControllerFactory方法也就达到了替换默认工厂的目的。this
闭包引用测试代码:
using System; class Program { public static void Main(string[] args) { Func<object> f1 = ()=>null; Func<object> f2 = f1; //f1与f2指向同一个对象 object o = new object(); f1 = ()=>o; //更改f1后,f2仍然指向以前的对象 bool b1 = f1() == o; //true bool b2 = f2() == null; //true, f1()!=f2() Print("直接赋值:"); Print(f1(),"f1() == {0}"); Print(f2(),"f2() == {0}"); Print(f1() == f2(),"f1() == f2() ? {0}"); Func<object> ff1 = ()=>null; Func<object> ff2 = ()=>ff1(); //经过委托包装f1,造成闭包 object oo = new object(); ff1 = ()=>oo; //更改f1后,f2与f1保持同步 bool bb1 = ff1() == oo; //true bool bb2 = ff2() == oo; //true, f1()==f2() Print("委托赋值:"); Print(ff1(),"ff1() == {0}"); Print(ff2(),"ff2() == {0}"); Print(ff1() == ff2(),"ff1() == ff2() ? {0}"); Console.ReadLine(); } static void Print(object mess,string format = "{0}") { string message = mess == null ? "null" : mess.ToString(); Console.WriteLine(string.Format(format,message)); } }
下面看一下SingleServiceResolver类是如何实现对象的建立的,该类是个泛型类,这意味着能够构造任何类型的对象,不只限于ControllerFactory,实际上在MVC中,该类在不少地方都获得了应用,例如:ControllerBuilder、DefaultControllerFactory、BuildManagerViewEngine等,实现了对多种对象的建立。
该类实现了IResolver接口,主要用来提供指定类型的实例,在SingleServiceResolver类中有三种方式来建立对象:
一、private Lazy<TService> _currentValueFromResolver; //内部调用_resolverThunk 二、private Func<TService> _currentValueThunk; //委托方式 三、private TService _defaultValue; //默认值方式 private Func<IDependencyResolver> _resolverThunk; //IDependencyResolver方式
从Current方法中能够看出他们的优先级:
public TService Current { get { return _currentValueFromResolver.Value ?? _currentValueThunk() ?? _defaultValue; } }
_currentValueFromResolver
其实是对_resolverThunk
的封装,内部仍是调用_resolverThunk
来实现对象的构造,因此优先级是:_resolverThunk > _currentValueThunk > _defaultValue
,即:IDependencyResolver方式 > 委托方式 > 默认值方式。
SingleServiceResolver在构造函数中默认实现了一个DefaultDependencyResolver对象封装到委托字段_resolverThunk
中,该默认的Resolver是以Activator.CreateInstance(type)的方式建立对象的,可是有个前提,指定的type不能是接口或者抽象类,不然直接返回null。
在ControllerBuilder类中实例化SingleServiceResolver对象的时候指定的是IControllerFactory接口类型,因此其内部的SingleServiceResolver对象没法经过IDependencyResolver方式建立对象,那么建立ControllerFactory对象的职责就落到了_currentValueThunk
(委托方式)和_defaultValue
(默认值方式)这两个方式上,前面说过,SingleServiceResolver类中的委托字段其实是经过闭包引用ControllerBuilder类中的相应委托来建立对象的,而在ControllerBuilder类中,这个对应的委托默认是返回null,
private Func<IControllerFactory> _factoryThunk = () => null;
所以,默认状况下SingleServiceResolver类的第二种方式也失效了,那么此时也只能依靠默认值方式来提供对象了,在ControllerBuilder类中这个默认值是DefaultControllerFactory:
internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver) { _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>( () => _factoryThunk(), new DefaultControllerFactory { ControllerBuilder = this }, //默认值 "ControllerBuilder.GetControllerFactory"); }
因此,在默认状况下是使用DefaultControllerFactory类来构造Controller的。
在建立SingleServiceResolver对象的时候,能够从三个地方判断出真正建立对象的方法是哪一种:
new SingleServiceResolver<IControllerFactory>( //一、看泛型接口,若是为接口或抽象类,则IDependencyResolver方式失效 () => _factoryThunk(), //二、看_factoryThunk()是否返回null,若是是则委托方式失效 new DefaultControllerFactory { ControllerBuilder = this }, //三、以上两种都失效,则使用该默认值 "ControllerBuilder.GetControllerFactory");
经过以上建立对象的过程能够得知,有两种方式能够替换默认的对象提供器:
替换默认的DependencyResolver,能够经过DependencyResolver类的静态方法SetResolver方法来实现:
CustomDependencyResolver customResolver = new CustomDependencyResolver(); DependencyResolver.SetResolver(customResolver);
将以上语句放在程序启动的地方,例如:Application_Start
经过前面介绍的ControllerBuilder类的SetControllerFactory方法
注:第一种方式的优先级更高。
经过ControllerBuilder建立出ControllerFactory对象后,下面就要利用该对象完成具体Controller的建立,ControllerFactory都实现了IControllerFactory接口,经过实现CreateController
方法完成对Controller的实例化,CreateController的内部逻辑很是简单,就两步:获取Controller类型,而后建立Controller对象。
根据控制器名称获取控制器Type的过程,有必要深刻了解一下,以便于咱们在往后遇到相关问题的时候可以更好的进行错误定位。获取类型的逻辑都封装在GetControllerType方法中,该过程根据路由数据中是否含有命名空间信息,分为三个阶段进行类型搜索:
UseNamespaceFallback==true
,此时会获取ControllerBuilder中设置的命名空间信息,利用该信息和控制器名称在缓存中进行类型搜索,若是找到惟一一个类型,则返回该类型,找到多个直接抛异常所以,命名空间的优先级是:RouteData > ControllerBuilder
在缓存中搜索类型的时候,若是是第一次查找,会调用ControllerTypeCache.EnsureInitialized方法将保存在硬盘中的Xml缓存文件加载到一个字典类型的内存缓存中。若是该缓存文件不存在,则会遍历当前应用引用的全部程序集,找出全部public权限的Controller类型(判断条件:实现IController接口、非抽象类、类名以Controller结尾),而后将这些类型信息进行xml序列化,生成缓存文件保存在硬盘中,以便于下次直接从缓存文件中加载,同时将类型信息分组以字典的形式缓存在内存中,提升搜索效率,字典的key为ControllerName(不带命名空间)。
Controller类型搜索流程以下图所示:
获取Controller类型之后,接下来就要进行Controller对象的建立。在DefaultControllerFactory类的源码中能够看到,同ControllerBuilder相似,该类的构造函数中也实例化了一个SingleServiceResolver对象,按照以前介绍的方法,咱们一眼就能够看出,该对象是利用默认值的方式提供了一个DefaultControllerActivator对象。
_activatorResolver = activatorResolver ?? new SingleServiceResolver<IControllerActivator>( //一、泛型为接口,IDependencyResolver方式失效 () => null, //二、返回了null,委托方式失效 new DefaultControllerActivator(dependencyResolver), //三、以上两种方式均失效,则使用该提供方式 "DefaultControllerFactory constructor");
实际上DefaultControllerFactory类仅实现了类型的搜索,对象的真正建立过程须要由DefaultControllerActivator类来完成,默认状况下,DefaultControllerActivator建立Controller的过程是很简单的,由于它实际上使用的是一个叫作DefaultDependencyResolver的类来进行Controller建立的,在该类内部直接调用Activator.CreateInstance(serviceType)
方法完成对象的实例化。
从DefaultControllerFactory和DefaultControllerActivator这两个类的建立过程能够发现,MVC提供了多种方式(IDependencyResolver方式、委托方式 、默认值方式)来提供对象,所以在对MVC相关模块进行扩展的时候,也有多种方式能够采用。
Controller中涉及到几个给view传值的数据容器:TempData、ViewData和ViewBag。前二者的不一样之处在于TempData仅存储临时数据,里面的数据在第一次读取以后会被移除,即:只能被读取一次;ViewData和ViewBag保存的是同一份数据,只不过ViewBag是动态对象,对ViewData进行了封装。
public dynamic ViewBag { get { if (_dynamicViewDataDictionary == null) { _dynamicViewDataDictionary = new DynamicViewDataDictionary(() => ViewData); //封装ViewData } return _dynamicViewDataDictionary; } }
下面简单说一下TempData的实现原理。
首先看下MSDN上是如何解释的:
你能够按使用 ViewDataDictionary 对象的相同方式使用 TempDataDictionary 对象传递数据。 可是,TempDataDictionary 对象中的数据仅从一个请求保持到下一个请求,除非你使用 Keep 方法将一个或多个键标记为需保留。 若是键已标记为需保留,则会为下一个请求保留该键。
TempDataDictionary 对象的典型用法是,在数据重定向到一个操做方法时从另外一个操做方法传递数据。 例如,操做方法可能会在调用 RedirectToAction 方法以前,将有关错误的信息存储在控制器的 TempData 属性(该属性返回 TempDataDictionary 对象)中。 而后,下一个操做方法能够处理错误并呈现显示错误消息的视图。
TempData的特性就是能够在两个Action之间传递数据,它会保存一份数据到下一个Action,并随着再下一个Action的到来而失效。因此它被用在两个Action之间来保存数据,好比,这样一个场景,你的一个Action接受一些post的数据,而后交给另外一个Action来处理,并显示到页面,这时就可使用TempData来传递这份数据。
TempData实现了IDictionary<string, object>接口,同时内部含有一个IDictionary<string, object>类型的私有字段,并添加了相关方法对字典字段的操做进行了控制,这明显是代理模式的一个应用。由于TempData须要在Action之间传递数据,所以要求其可以对自身的数据进行保存,TempData依赖ITempDataProvider接口实现了数据的加载与保存,默认状况下是使用SessionStateTempDataProvider对象将TempData中的数据存放在Session中。
下面看一下TempData是如何控制数据操做的,TempDataDictionary源码中有这样一段定义:
internal const string TempDataSerializationKey = "__tempData"; private Dictionary<string, object> _data; private HashSet<string> _initialKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private HashSet<string> _retainedKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
私有字典字段_data是真正存放数据的地方,哈希集合_initialKeys和_retainedKeys用来标记数据,_initialKeys中存放还没有被读取的数据key,_retainedKeys存放能够被屡次访问的key。
TempDataDictionary对数据操做的控制行为主要体如今在读取数据的时候并不会当即从_data中删除对应的数据,而是经过_initialKeys和_retainedKeys这两个hashset标记每条数据的状态,最后在经过ITempDataProvider进行保存的时候再根据以前标记的状态对数据进行过滤,这时才去除已访问过的数据。
相关的控制方法有:TryGetValue、Add、Keep、Peek、Remove、Clear
一、TryGetValue
public bool TryGetValue(string key, out object value) { _initialKeys.Remove(key); return _data.TryGetValue(key, out value); }
该方法在读取数据的时候,会从_initialKeys集合中移除对应的key,前面说过,由于_initialKeys是用来标记数据未访问状态的,从该集合中删除了key,以后在经过ITempDataProvider保存的时候就会将数据从_data字典中删除,下一次请求就没法再从TempData访问该key对应的数据了,即:数据只能在一次请求中使用。
二、Add
public void Add(string key, object value) { _data.Add(key, value); _initialKeys.Add(key); }
添加数据的时候在_initialKeys中打上标记,代表该key对应的数据能够被访问。
三、Keep
public void Keep(string key) { _retainedKeys.Add(key); }
调用Keep方法的时候,会将key添加到_retainedKeys中,代表该条记录能够被屡次访问,为何能够被屡次访问呢,能够从Save方法中找到缘由:
public void Save(ControllerContext controllerContext, ITempDataProvider tempDataProvider) { // Frequently called so ensure delegate is stateless _data.RemoveFromDictionary((KeyValuePair<string, object> entry, TempDataDictionary tempData) => { string key = entry.Key; return !tempData._initialKeys.Contains(key) && !tempData._retainedKeys.Contains(key); }, this); tempDataProvider.SaveTempData(controllerContext, _data); }
能够看出,在保存的时候,会从_data中取出每一条数据,判断该数据的key是否存在于_initialKeys和_retainedKeys中,若是都不存在才会从_data中移除,因此keep方法将key添加到_retainedKeys后,该数据就不会被删除了,即:能够在多个请求中被访问了。
四、Peek
public object Peek(string key) { object value; _data.TryGetValue(key, out value); return value; }
从代码中能够看出,该方法在读取数据的时候,仅仅是从_data中进行了获取,并无移除_initialKeys集合中对应的key,所以经过该方法读取数据不影响数据的状态,该条数据依然能够在下一次请求中被使用。
五、Remove 与 Clear
public bool Remove(string key) { _retainedKeys.Remove(key); _initialKeys.Remove(key); return _data.Remove(key); } public void Clear() { _data.Clear(); _retainedKeys.Clear(); _initialKeys.Clear(); }
这两个方法没什么多说的,只是在删除数据的时候同时删除其对应的状态。