一直在想,怎麼寫這個源碼分析,那些大神們是如何一步步分析寫的文章。最開始不可能就瞭解其本質實現的,咱們對任何事物老是有個認知過程的的。那門我就從這個認知過程一步步的解析一下過濾器使用和其本質實現吧。固然前提仍是對Mvc有一些瞭解。(這裏我裝了ReSharp插件和Reflector一塊兒使用的,因此有些源碼格式可能不太同樣)編程
1.初識過濾器,緩存
每每認識事物的開始就是現象,認識瞭解一個方法或者一個類,也通常都是從其實例開始的。ide
需求:作電商的網站,那麼要有用戶的註冊,登錄等。訪問某個頁面也不也說誰都可以訪問的,別如咱們的我的信息,購買信息等等。那確定就須要對用戶進行驗證過濾。那就就有了這個需求,咱們的Action須要用戶過濾。簡而言之,咱們要在Action執行以前,進行過濾(執行自定義的代碼)。源碼分析
那麼,第一個demo就來了。(多是從書本上看來的,多是網上查找的,多是老師教的。無論怎麼樣,你確定已經見過了幾個相似的demo了)。post
/// <summary> /// 自定義用戶登陸特性 /// </summary> public class UserLoginInfoAttribute : ActionFilterAttribute { /// <summary> /// 在Action執行以前 /// </summary> /// <param name="filterContext"></param> public override void OnActionExecuting(ActionExecutingContext filterContext) { //這裏是判斷邏輯 } }
並且你確定應該知道了,這個方法就是在Action執行以前執行的(即使你沒有根據名稱纔出來,也應該msdn查出來吧),ok。這確實可以完成咱們的需求,那麼有這麼個override的方法,根據經驗,看樣子應該還有Action執行以後的方法纔對,根據智能提示看一下吧,哎呦我去,果真有,還有意外收穫。OnActionExecuting,OnActionExecuted,OnResultExecuting,OnResultExecuted,嗯這個不能僅能夠捕獲Action執行前,Action執行後,還能夠在Result返回前,返回後高一些東東,嗯,提供的挺全嘛。學習
有點經驗的同窗是否是就大概能猜到Action執行幹了點什麼了。可能就是相似的這麼段代碼吧。 網站
/// <summary> /// Action的執行 /// </summary> public void ExcuteAction() { //1.OnActionExecuting //2.執行Action //3.OnActionExecuted //4.OnResultExecuting //5.生成結果 //6.OnResultExecuted }
我去,這都猜到了,那要不要證明下呢,嗯,仍是證明下的好,要不總感受欠人1毛錢似的,趕腳怪怪的。好吧,一塊兒看一看吧ui
Action的執行,從哪裏看呢,(若是同窗看過些Mvc的相關知識應該就知道從哪裏入手,不知道暫時也沒什麼),那就從Controller看看吧,別問我爲何,我也不知道,多年當猴(程序猿)進化的結果吧。(想一想大概也能猜的到,Action是在Controller中的,那Action的執行最相關的估麼就應該在Controller中有這麼個執行的東西),轉到定義看下吧。 this
public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IController, IAsyncManagerContainer { // Fields private IActionInvoker _actionInvoker; //此處略去N行代碼。。。() }
我去這麼一大堆,尼瑪徹底亮瞎了個人鈦合金狗眼了,估計也沒有心情徹底看下去,可是瞅兩眼有沒有讓你眼前一亮的東西呢。IActionInvoker。微軟起名仍是很地道的。(若是你沒看見或者一點都沒看,我只能說呵呵),究其實現,實際上是這麼個方法CreateActionInvoker。尼瑪看了看實現,又往下翻了翻定義什麼的,沒什麼頭緒,算了,看這樣子,換個地方吧。在這之中你確定應該看過了這麼個東西插件
/// <summary> /// 定義操做調用程序的協定,該調用程序用於調用一個操做以響應 HTTP 請求。 /// </summary> public interface IActionInvoker { /// <summary> /// 使用指定的控制器上下文來調用指定操做。 /// </summary> /// /// <returns> /// 若是找到了指定操做,則爲 true;不然爲 false。 /// </returns> /// <param name="controllerContext">控制器上下文。</param><param name="actionName">操做的名稱。</param> bool InvokeAction(ControllerContext controllerContext, string actionName); }
這就是控制Action執行的接口。看看Controller的定義。ControllerBase玩意是否是應該引發咱們的關注呢。嗯,應該是Controller的基類吧。看看定義吧
public abstract class ControllerBase : IController { // Fields private DynamicViewDataDictionary _dynamicViewDataDictionary; private readonly SingleEntryGate _executeWasCalledGate; private TempDataDictionary _tempDataDictionary; private bool _validateRequest; private IValueProvider _valueProvider; private ViewDataDictionary _viewDataDictionary; // Methods protected ControllerBase(); protected virtual void Execute(RequestContext requestContext); protected abstract void ExecuteCore(); protected virtual void Initialize(RequestContext requestContext); void IController.Execute(RequestContext requestContext); internal void VerifyExecuteCalledOnce(); // Properties public ControllerContext ControllerContext { get; set; } public TempDataDictionary TempData { get; set; } public bool ValidateRequest { get; set; } public IValueProvider ValueProvider { get; set; } [Dynamic] public object ViewBag { [return: Dynamic] get; } public ViewDataDictionary ViewData { get; set; } }
還能夠,比Controller簡單不少,可是看一眼沒什麼收穫。繼續往下走
/// <summary> /// 定義控制器所需的方法。 /// </summary> public interface IController { /// <summary> /// 執行指定的請求上下文。 /// </summary> /// <param name="requestContext">請求上下文。</param> void Execute(RequestContext requestContext); }
哎,有點戲,Execute,執行請求上下文,應該會有Action的東西,畢竟咱們的Action纔是真正每次處理請求的真正內容麼。
回到Controller看,咱們會看到這麼個方法
protected override void ExecuteCore() { // If code in this method needs to be updated, please also check the BeginExecuteCore() and // EndExecuteCore() methods of AsyncController to see if that code also must be updated. PossiblyLoadTempData(); try { string actionName = RouteData.GetRequiredString("action"); if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) { HandleUnknownAction(actionName); } } finally { PossiblySaveTempData(); } }
我去,終於有點收穫。ActionInvoker.InvokeAction(ControllerContext, actionName)這個是否是就是Action的執行呢。仔細一看,我去依賴接口調用的方法。我去,噴血了。(這個是個人軟肋,這種依賴接口的調用,我就不會找其具體實現了該接口的代碼的位置了,跪求大神指導。)。(固然若是你運氣比較好直接用的reflector看的話,應該會有所發現,由於Controller和ControllerActionInvoker(這是我後面才知道的)的定義是挨着的。)這個是我以前,學習大神Artech的文章看到的。這裏曲折的尋找之路你們就只能各自發揮了,我這裏就直接給出ControllerActionInvoker的InvokeAction執行的。看下代碼片斷
public class ControllerActionInvoker : IActionInvoker { public virtual bool InvokeAction(ControllerContext controllerContext, string actionName) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } if (String.IsNullOrEmpty(actionName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName"); } ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext); ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName); if (actionDescriptor != null) { FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor); try { AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor); if (authContext.Result != null) { // the auth filter signaled that we should let it short-circuit the request InvokeActionResult(controllerContext, authContext.Result); } else { if (controllerContext.Controller.ValidateRequest) { ValidateRequest(controllerContext); } IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor); ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters); InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result); } } catch (ThreadAbortException) { // This type of exception occurs as a result of Response.Redirect(), but we special-case so that // the filters don't see this as an error. throw; } catch (Exception ex) { // something blew up, so execute the exception filters ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex); if (!exceptionContext.ExceptionHandled) { throw; } InvokeActionResult(controllerContext, exceptionContext.Result); } return true; } // notify controller that no method matched return false; } }
恩恩,大概看看,應該可以找到這麼兩個方法InvokeActionMethodWithFilters,InvokeActionResultWithFilters,這尼瑪也太容易了,這方法名不就寫的很清楚了麼。(讓咱們體驗了一把見名知意的重要性)。繼續看一看,最後看到這麼兩個方法InvokeActionMethodFilter,InvokeActionResultFilter
internal static ActionExecutedContext InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func<ActionExecutedContext> continuation) { filter.OnActionExecuting(preContext); if (preContext.Result != null) { return new ActionExecutedContext(preContext, preContext.ActionDescriptor, true /* canceled */, null /* exception */) { Result = preContext.Result }; } bool wasError = false; ActionExecutedContext postContext = null; try { postContext = continuation(); } catch (ThreadAbortException) { // This type of exception occurs as a result of Response.Redirect(), but we special-case so that // the filters don't see this as an error. postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, null /* exception */); filter.OnActionExecuted(postContext); throw; } catch (Exception ex) { wasError = true; postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, ex); filter.OnActionExecuted(postContext); if (!postContext.ExceptionHandled) { throw; } } if (!wasError) { filter.OnActionExecuted(postContext); } return postContext; } internal static ResultExecutedContext InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func<ResultExecutedContext> continuation) { filter.OnResultExecuting(preContext); if (preContext.Cancel) { return new ResultExecutedContext(preContext, preContext.Result, true /* canceled */, null /* exception */); } bool wasError = false; ResultExecutedContext postContext = null; try { postContext = continuation(); } catch (ThreadAbortException) { // This type of exception occurs as a result of Response.Redirect(), but we special-case so that // the filters don't see this as an error. postContext = new ResultExecutedContext(preContext, preContext.Result, false /* canceled */, null /* exception */); filter.OnResultExecuted(postContext); throw; } catch (Exception ex) { wasError = true; postContext = new ResultExecutedContext(preContext, preContext.Result, false /* canceled */, ex); filter.OnResultExecuted(postContext); if (!postContext.ExceptionHandled) { throw; } } if (!wasError) { filter.OnResultExecuted(postContext); } return postContext; }
嗯,看方法第一行和最有一行,好了大概跟咱們猜的差很少,只不過實現要比咱們的想一想複雜的多。咱們猜想的一行註釋基本上就省去了千百行的代碼。從看的過程當中,也能學到不少別的東西,微軟的一些設計思路和方式。之後再看的話,是否是會容易一些呢。
過濾器的實現,是否是就是種面向切面的AOP思想呢,以前我也寫過相似的東東,可是能不能提高到微軟這個層次呢,至少從幾十幾百行代碼一會兒變成小几千行,是否是有種很牛逼的趕腳,呵呵,開個玩笑,代碼仍是要精簡些的。AOP,不是很熟悉,要不要找篇文章再深刻研究下呢,先把這個研究完再說吧。
面向抽象,依賴接口的編程,有木有。(若是不是的話,我就能夠直接轉到定義了,不須要花費精力找Action的執行了)
單例模式有木有。別說你沒看到。(看這個名稱貌似也是個內部Descriptor緩存類的東東)
確實學到很多東西。我去,咱們要幹嗎來着,研究過濾器的本質和實現。我去基本木有進展。
public class ControllerActionInvoker : IActionInvoker { private static readonly ControllerDescriptorCache _staticDescriptorCache = new ControllerDescriptorCache(); }
算了,尼瑪一個問題折騰了半天,不過還好有些收穫。休息休息,等待繼續研究吧。週末得回家,估計不能發blog了,不過研究仍是得繼續,不然無法完整的貫通下來了。
注:1.文章中提到了(這種依賴接口的調用,我就不會找其具體實現了該接口的代碼的位置了,跪求大神指導。)
2.因爲我的水平問題,不免會有一些錯誤,忘大神支出共同進步。