我们在上一篇文章中介绍Controller激活系统中所涉及到的一些类型,比如有关Controller类型的相关定义类型就包括了IController类型,IAsyncController类型,ControllerBase抽象类型和我们最终要使用的抽象类型Controller,这是ASP.NET MVC 框架中和Controller本身定义相关的类型。其他辅助类型,包括管理Controller的类型ControllerFactory,这个工厂负责Controller的生产和销毁。我们还涉及到另一个辅助类型,用于把系统默认定义的或者自定义的ControllerFactory注册到ASP.NET MVC框架中的类型ControllerBuilder。
Controller类型、ControllerFactory类型和ControllerBuilder类型,他们之间的关系可以描述为:ControllerBuilder是面向客户的,或者说是程序员和ASP.NET MVC框架之间的桥梁。我们通过ControllerBuilder类型的SetControllerFactory方法把我们自定义的ControllerFactory类型实例注册到ASP.NET MVC框架中,ControllerFactory类型用于管理Controller类型实例,其实也就是说ControllerFactory类型就是ASP.NET MVC框架中的一个扩展点。
我们今天主要讲Controller是怎么解析出来的,之所以把这一部分分开写,因为合在一起太长了,也说的不详细,如果大家对以上说的不太清楚,可以查看《白话ASP.NET MVC之二:Controller激活系统的概览》, 该文对ASP.NET MVC框架中所提到的Controlelr激活系统所涉及的类型有详细的介绍。
一、“路由系统”和“激活系统”是怎么关联起来的
上一篇文章有过讲述,我们在这里简单说一下。ASP.NET 的路由系统是建立在一个叫做UrlRoutingModule的HttpModule组件上的,针对请求的路由解析是通过注册HttpApplication对象的PostResolveRequestCache事件来实现的,为当前的请求动态映射到一个HttpHandler类型上,最终由该HttpHandler接管请求并处理。我们来看看UrlRoutingModule类型的代码吧。
1 [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")] 2 public class UrlRoutingModule : IHttpModule 3 { 4 private static readonly object _contextKey = new object(); 5 6 private static readonly object _requestDataKey = new object(); 7 8 private RouteCollection _routeCollection; 9 10 public RouteCollection RouteCollection 11 { 12 get 13 { 14 if (this._routeCollection == null) 15 { 16 this._routeCollection = RouteTable.Routes; 17 } 18 return this._routeCollection; 19 } 20 set 21 { 22 this._routeCollection = value; 23 } 24 } 25 26 protected virtual void Dispose() 27 { 28 } 29 30 protected virtual void Init(HttpApplication application) 31 { 32 if (application.Context.Items[UrlRoutingModule._contextKey] != null) 33 { 34 return; 35 } 36 application.Context.Items[UrlRoutingModule._contextKey] = UrlRoutingModule._contextKey; 37 application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache); 38 } 39 40 private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) 41 { 42 HttpContextBase context = new HttpContextWrapper(((HttpApplication)sender).Context); 43 this.PostResolveRequestCache(context); 44 } 45 46 [Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")] 47 public virtual void PostMapRequestHandler(HttpContextBase context) 48 { 49 } 50 51 public virtual void PostResolveRequestCache(HttpContextBase context) 52 { 53 RouteData routeData = this.RouteCollection.GetRouteData(context); 54 if (routeData == null) 55 { 56 return; 57 } 58 IRouteHandler routeHandler = routeData.RouteHandler; 59 if (routeHandler == null) 60 { 61 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0])); 62 } 63 if (routeHandler is StopRoutingHandler) 64 { 65 return; 66 } 67 RequestContext requestContext = new RequestContext(context, routeData); 68 context.Request.RequestContext = requestContext; 69 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); 70 if (httpHandler == null) 71 { 72 throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] 73 { 74 routeHandler.GetType() 75 })); 76 } 77 if (!(httpHandler is UrlAuthFailureHandler)) 78 { 79 context.RemapHandler(httpHandler); 80 return; 81 } 82 if (FormsAuthenticationModule.FormsAuthRequired) 83 { 84 UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this); 85 return; 86 } 87 throw new HttpException(401, SR.GetString("Assess_Denied_Description3")); 88 } 89 90 void IHttpModule.Dispose() 91 { 92 this.Dispose(); 93 } 94 95 void IHttpModule.Init(HttpApplication application) 96 { 97 this.Init(application); 98 } 99 }
具体来说,该组件通过以RouteTable的静态属性Routes表示的路由表针对当前请求实施路由解析,如果有匹配,就会根据路由对象Route来生成RouteData路由数据对象,然后我们借助RouteData对象的RouteHandler属性获取想要的HttpHandler对象。在默认情况下这个RouteHandler属性所代表的对象是MvcRouteHandler。翠花,上代码:
1 /// <summary>Creates an object that implements the IHttpHandler interface and passes the request context to it.</summary> 2 public class MvcRouteHandler : IRouteHandler 3 { 4 private IControllerFactory _controllerFactory; 5 6 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcRouteHandler" /> class.</summary> 7 public MvcRouteHandler() 8 { 9 } 10 11 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcRouteHandler" /> class using the specified factory controller object.</summary> 12 /// <param name="controllerFactory">The controller factory.</param> 13 public MvcRouteHandler(IControllerFactory controllerFactory) 14 { 15 this._controllerFactory = controllerFactory; 16 } 17 18 /// <summary>Returns the HTTP handler by using the specified HTTP context.</summary> 19 /// <returns>The HTTP handler.</returns> 20 /// <param name="requestContext">The request context.</param> 21 protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) 22 { 23 requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext)); 24 return new MvcHandler(requestContext); 25 } 26 27 /// <summary>Returns the session behavior.</summary> 28 /// <returns>The session behavior.</returns> 29 /// <param name="requestContext">The request context.</param> 30 protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext) 31 { 32 string text = (string)requestContext.RouteData.Values["controller"]; 33 if (string.IsNullOrWhiteSpace(text)) 34 { 35 throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController); 36 } 37 IControllerFactory controllerFactory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory(); 38 return controllerFactory.GetControllerSessionBehavior(requestContext, text); 39 } 40 41 /// <summary>Returns the HTTP handler by using the specified request context.</summary> 42 /// <returns>The HTTP handler.</returns> 43 /// <param name="requestContext">The request context.</param> 44 IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) 45 { 46 return this.GetHttpHandler(requestContext); 47 } 48 }
在该类型里面包含了一个IControllerFactory类型成员字段,这个接口类型是所有ControllerFactory都要必须实现的接口,否则就不叫Controller的工厂了。MvcRouteHandler类型有两个构造函数,无参的没的说,另一个需要传递一个IControllerFactory类型的参数,这个参数用于初始化MvcRouteHandler类型内部包含的类型为IControllerFactory的_controllerFactory字段。当我们构造MvcRouteHandler实例的时候,如果我们调用了无参的构造函数,它会在内部使用ControllerBuilder.Current.GetControllerFactory()方法来获取我们通过ControllerBuilder类型注册的IControllerFactory类型的实例,代码很明显:
IControllerFactory controllerFactory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
MvcRouteHandler实现了IRouteHandler接口,目的只有一个,提供后续的HttpHandler,IRouteHandler接口定义如下:
public interface IRouteHandler { IHttpHandler GetHttpHandler(RequestContext requestContext); }
MvcRouteHandler会给我们直接返回MvcHandler对象,这个对象用于处理请求,包括激活Controler对象,代码最有说服力,这份代码,上篇文章也贴过,现在也贴一下把,上代码:
1 public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState 2 { 3 private struct ProcessRequestState 4 { 5 internal IAsyncController AsyncController; 6 7 internal IControllerFactory Factory; 8 9 internal RequestContext RequestContext; 10 11 internal void ReleaseController() 12 { 13 this.Factory.ReleaseController(this.AsyncController); 14 } 15 } 16 17 private static readonly object _processRequestTag = new object(); 18 19 internal static readonly string MvcVersion = MvcHandler.GetMvcVersionString(); 20 21 /// <summary>Contains the header name of the ASP.NET MVC version.</summary> 22 public static readonly string MvcVersionHeaderName = "X-AspNetMvc-Version"; 23 24 private ControllerBuilder _controllerBuilder; 25 26 internal ControllerBuilder ControllerBuilder 27 { 28 get 29 { 30 if (this._controllerBuilder == null) 31 { 32 this._controllerBuilder = ControllerBuilder.Current; 33 } 34 return this._controllerBuilder; 35 } 36 set 37 { 38 this._controllerBuilder = value; 39 } 40 } 41 42 /// <summary>Gets or sets a value that indicates whether the MVC response header is disabled.</summary> 43 /// <returns>true if the MVC response header is disabled; otherwise, false.</returns> 44 public static bool DisableMvcResponseHeader 45 { 46 get; 47 set; 48 } 49 50 /// <summary>Gets a value that indicates whether another request can use the <see cref="T:System.Web.IHttpHandler" /> instance.</summary> 51 /// <returns>true if the <see cref="T:System.Web.IHttpHandler" /> instance is reusable; otherwise, false.</returns> 52 protected virtual bool IsReusable 53 { 54 get 55 { 56 return false; 57 } 58 } 59 60 /// <summary>Gets the request context.</summary> 61 /// <returns>The request context.</returns> 62 public RequestContext RequestContext 63 { 64 get; 65 private set; 66 } 67 68 /// <summary>Gets a value that indicates whether another request can use the <see cref="T:System.Web.IHttpHandler" /> instance.</summary> 69 /// <returns>true if the <see cref="T:System.Web.IHttpHandler" /> instance is reusable; otherwise, false.</returns> 70 bool IHttpHandler.IsReusable 71 { 72 get 73 { 74 return this.IsReusable; 75 } 76 } 77 78 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcHandler" /> class.</summary> 79 /// <param name="requestContext">The request context.</param> 80 /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception> 81 public MvcHandler(RequestContext requestContext) 82 { 83 if (requestContext == null) 84 { 85 throw new ArgumentNullException("requestContext"); 86 } 87 this.RequestContext = requestContext; 88 } 89 90 /// <summary>Adds the version header by using the specified HTTP context.</summary> 91 /// <param name="httpContext">The HTTP context.</param> 92 protected internal virtual void AddVersionHeader(HttpContextBase httpContext) 93 { 94 if (!MvcHandler.DisableMvcResponseHeader) 95 { 96 httpContext.Response.AppendHeader(MvcHandler.MvcVersionHeaderName, MvcHandler.MvcVersion); 97 } 98 } 99 100 /// <summary>Called by ASP.NET to begin asynchronous request processing.</summary> 101 /// <returns>The status of the asynchronous call.</returns> 102 /// <param name="httpContext">The HTTP context.</param> 103 /// <param name="callback">The asynchronous callback method.</param> 104 /// <param name="state">The state of the asynchronous object.</param> 105 protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state) 106 { 107 HttpContextBase httpContext2 = new HttpContextWrapper(httpContext); 108 return this.BeginProcessRequest(httpContext2, callback, state); 109 } 110 111 /// <summary>Called by ASP.NET to begin asynchronous request processing using the base HTTP context.</summary> 112 /// <returns>The status of the asynchronous call.</returns> 113 /// <param name="httpContext">The HTTP context.</param> 114 /// <param name="callback">The asynchronous callback method.</param> 115 /// <param name="state">The state of the asynchronous object.</param> 116 protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state) 117 { 118 IController controller; 119 IControllerFactory factory; 120 this.ProcessRequestInit(httpContext, out controller, out factory); 121 IAsyncController asyncController = controller as IAsyncController; 122 if (asyncController != null) 123 { 124 BeginInvokeDelegate<MvcHandler.ProcessRequestState> beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState) 125 { 126 IAsyncResult result; 127 try 128 { 129 result = innerState.AsyncController.BeginExecute(innerState.RequestContext, asyncCallback, asyncState); 130 } 131 catch 132 { 133 innerState.ReleaseController(); 134 throw; 135 } 136 return result; 137 }; 138 EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> endDelegate = delegate(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState) 139 { 140 try 141 { 142 innerState.AsyncController.EndExecute(asyncResult); 143 } 144 finally 145 { 146 innerState.ReleaseController(); 147 } 148 }; 149 MvcHandler.ProcessRequestState invokeState = new MvcHandler.ProcessRequestState 150 { 151 AsyncController = asyncController, 152 Factory = factory, 153 RequestContext = this.RequestContext 154 }; 155 SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext(); 156 return AsyncResultWrapper.Begin<MvcHandler.ProcessRequestState>(callback, state, beginDelegate, endDelegate, invokeState, MvcHandler._processRequestTag, -1, synchronizationContext); 157 } 158 Action action = delegate 159 { 160 try 161 { 162 controller.Execute(this.RequestContext); 163 } 164 finally 165 { 166 factory.ReleaseController(controller); 167 } 168 }; 169 return AsyncResultWrapper.BeginSynchronous(callback, state, action, MvcHandler._processRequestTag); 170 } 171 172 /// <summary>Called by ASP.NET when asynchronous request processing has ended.</summary> 173 /// <param name="asyncResult">The asynchronous result.</param> 174 protected internal virtual void EndProcessRequest(IAsyncResult asyncResult) 175 { 176 AsyncResultWrapper.End(asyncResult, MvcHandler._processRequestTag); 177 } 178 179 private static string GetMvcVersionString() 180 { 181 return new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2); 182 } 183 184 /// <summary>Processes the request by using the specified HTTP request context.</summary> 185 /// <param name="httpContext">The HTTP context.</param> 186 protected virtual void ProcessRequest(HttpContext httpContext) 187 { 188 HttpContextBase httpContext2 = new HttpContextWrapper(httpContext); 189 this.ProcessRequest(httpContext2); 190 } 191 192 /// <summary>Processes the request by using the specified base HTTP request context.</summary> 193 /// <param name="httpContext">The HTTP context.</param> 194 protected internal virtual void ProcessRequest(HttpContextBase httpContext) 195 { 196 IController controller; 197 IControllerFactory controllerFactory; 198 this.ProcessRequestInit(httpContext, out controller, out controllerFactory); 199 try 200 { 201 controller.Execute(this.RequestContext); 202 } 203 finally 204 { 205 controllerFactory.ReleaseController(controller); 206 } 207 } 208 209 private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) 210 { 211 HttpContext current = HttpContext.Current; 212 if (current != null && ValidationUtility.IsValidationEnabled(current) == true) 213 { 214 ValidationUtility.EnableDynamicValidation(current); 215 } 216 this.AddVersionHeader(httpContext); 217 this.RemoveOptionalRoutingParameters(); 218 string requiredString = this.RequestContext.RouteData.GetRequiredString("controller"); 219 factory = this.ControllerBuilder.GetControllerFactory(); 220 controller = factory.CreateController(this.RequestContext, requiredString); 221 if (controller == null) 222 { 223 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[] 224 { 225 factory.GetType(), 226 requiredString 227 })); 228 } 229 } 230 231 private void RemoveOptionalRoutingParameters() 232 { 233 RouteValueDictionary values = this.RequestContext.RouteData.Values; 234 values.RemoveFromDictionary((KeyValuePair<string, object> entry) => entry.Value == UrlParameter.Optional); 235 } 236 237 /// <summary>Enables processing of HTTP Web requests by a custom HTTP handler that implements the <see cref="T:System.Web.IHttpHandler" /> interface.</summary> 238 /// <param name="httpContext">An <see cref="T:System.Web.HttpContext" /> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) that are used to service HTTP requests.</param> 239 void IHttpHandler.ProcessRequest(HttpContext httpContext) 240 { 241 this.ProcessRequest(httpContext); 242 } 243 244 /// <summary>Called by ASP.NET to begin asynchronous request processing using the base HTTP context.</summary> 245 /// <returns>The status of the asynchronous call.</returns> 246 /// <param name="context">The HTTP context.</param> 247 /// <param name="cb">The asynchronous callback method.</param> 248 /// <param name="extraData">The data.</param> 249 IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) 250 { 251 return this.BeginProcessRequest(context, cb, extraData); 252 } 253 254 /// <summary>Called by ASP.NET when asynchronous request processing has ended.</summary> 255 /// <param name="result">The asynchronous result.</param> 256 void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) 257 { 258 this.EndProcessRequest(result); 259 } 260 }
MvcHandler类型的BeginProcessRequest方法用来处理请求,包括激活Controller实例等。由于MvcHandler类型同时实现了IHttpHandler接口和IHttpAsyncHandler接口,所以他总是异步方式去执行(调用BeginProcessRequest/EndProcessRequest方法)。BeginProcessRequest方法在执行的时候通过RequestContext对象的RouteData属性获得Controller的名字,然后在通过ControllerBuilder获得ControllerFactory对象,然后用Controller的名字和ControllerFactory对象来激活目标Controller实例。如果Controller类型实现了IAsyncController接口,那就以异步方式执行Controller,否则以同步方式执行。Controller对象成功执行后,MvcHandler对象会调用ControllerFactory对象ReleaseController方法来销毁Controller实例对象。
我们小结一下,ASP.NET MVC的路由系统和Controller的激活系统是通过这些对象关联起来的:请求Url ------->Route------->RouteData------->RouteHandler(MvcRouteHandler)-------->MvcRouteHandler------>MvcHandler,通过这些对象就能串起来了。
二、Controller的详细解析过程
我先来简述一下Controller解析的原理吧。Controller实例对象的解析是通过实现了IControllerFactory接口的ControllerFactory对象实现的,ControllerFactory是怎么来的呢?是通过调用ControllerBuilder的SetControllerFactory方法实现对ControllerFactory类型或者实例对象的注册。如果我们没有调用ControllerBuilder的SetControllerFactory方法对象ControllerFactory类型或者实例显示注册,系统会使用默认的ControllerFactory来完成对Controller对象的解析,这个对象就是DefaultControllerFactory类型,该类型的实现正好反映了ASP.NET MVC框架对Controller实例的激活采取的默认策略。今天我们就看看DefaultControllerFactory类型是如何把Controller对象激活的,这也是Controller激活系统的默认实现,我们可以扩展ControllerFactory类型,实现自定义的Controller激活策略。
我把代码流程写一下,在MvcHandler类型里面有两个方法,一个方法是:BeginProcessRequest(HttpContextBase httpContext,AsyncCallback callback,object state),该方法用于对请求进行处理;第二个方法是:ProcessRequestInit(HttpContextbase httpContext,out IController controller,out IControllerFactory controllerFactory),该方法就是定义了激活Controller算法的骨架,上代码吧,代码最无二意,我们先看BeginProcessRequest的代码:
1 /// <summary>Called by ASP.NET to begin asynchronous request processing using the base HTTP context.</summary> 2 /// <returns>The status of the asynchronous call.</returns> 3 /// <param name="httpContext">The HTTP context.</param> 4 /// <param name="callback">The asynchronous callback method.</param> 5 /// <param name="state">The state of the asynchronous object.</param> 6 protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state) 7 { 8 IController controller; 9 IControllerFactory factory; 10 this.ProcessRequestInit(httpContext, out controller, out factory); 11 IAsyncController asyncController = controller as IAsyncController; 12 if (asyncController != null) 13 { 14 BeginInvokeDelegate<MvcHandler.ProcessRequestState> beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState) 15 { 16 IAsyncResult result; 17 try 18 { 19 result = innerState.AsyncController.BeginExecute(innerState.RequestContext, asyncCallback, asyncState); 20 } 21 catch 22 { 23 innerState.ReleaseController(); 24 throw; 25 } 26 return result; 27 }; 28 EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> endDelegate = delegate(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState) 29 { 30 try 31 { 32 innerState.AsyncController.EndExecute(asyncResult); 33 } 34 finally 35 { 36 innerState.ReleaseController(); 37 } 38 }; 39 MvcHandler.ProcessRequestState invokeState = new MvcHandler.ProcessRequestState 40 { 41 AsyncController = asyncController, 42 Factory = factory, 43 RequestContext = this.RequestContext 44 }; 45 SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext(); 46 return AsyncResultWrapper.Begin<MvcHandler.ProcessRequestState>(callback, state, beginDelegate, endDelegate, invokeState, MvcHandler._processRequestTag, -1, synchronizationContext); 47 } 48 Action action = delegate 49 { 50 try 51 { 52 controller.Execute(this.RequestContext); 53 } 54 finally 55 { 56 factory.ReleaseController(controller); 57 } 58 }; 59 return AsyncResultWrapper.BeginSynchronous(callback, state, action, MvcHandler._processRequestTag); 60 }
在这个方法里面根据解析出来的Controller的类型来执行,如果是异步的Controller那就异步执行,否则就同步执行。在该方法里面,第三行,标红色的方法就是定义解析和执行Controller的算法骨架,就是我们要贴代码的第二个方法ProcessRequestInit,源码如下:
1 private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) 2 { 3 HttpContext current = HttpContext.Current; 4 if (current != null && ValidationUtility.IsValidationEnabled(current) == true) 5 { 6 ValidationUtility.EnableDynamicValidation(current); 7 } 8 this.AddVersionHeader(httpContext); 9 this.RemoveOptionalRoutingParameters(); 10 string requiredString = this.RequestContext.RouteData.GetRequiredString("controller"); 11 factory = this.ControllerBuilder.GetControllerFactory(); 12 controller = factory.CreateController(this.RequestContext, requiredString); 13 if (controller == null) 14 { 15 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[] 16 { 17 factory.GetType(), 18 requiredString 19 })); 20 } 21 }
这个方法主要是获取ControllerFactory实例,根据获得的ControllerFactory对象激活Controller对象,红色标注的代码就是核心关键点。说明一点,这个方法只是定义了激活Controller算法的骨架,具体的实现在DefaultControllerFactory类型中。代码很简单,我相信大家看的清楚。
DefaultControllerFactory会先通过“MVC-ControllerTypeCache.xml”文件来加载Controller的类型,如果没有找到有效的数据才会通过调用BuildManager的静态方法GetReferencedAssemblies获取到系统所使用到的所有程序集,然后针对每个程序集通过反射的方式获得所有实现了IController接口的类型,并XML文件的形式缓存起来,缓存的文件名就是“MVC-ControllerTypeCache.xml”。然后我们把Controller的名称和命名空间作为匹配条件去查找对应的Controller类型。当我们获得了符合标准的真是的Controller类型后,DefaultControllerFactory对象通过反射的方式创建Controller类型的实例对象。解析逻辑不复杂,但是代码不少。首先我说明一点,把代码全部贴出来不是为了占篇幅,而是为了大家能看的更明白,我把完整的源码贴出来,大家可以仔细体会一下。
1 /// <summary>Represents the controller factory that is registered by default.</summary> 2 public class DefaultControllerFactory : IControllerFactory 3 { 4 private class DefaultControllerActivator : IControllerActivator 5 { 6 private Func<IDependencyResolver> _resolverThunk; 7 8 public DefaultControllerActivator() : this(null) 9 { 10 } 11 12 public DefaultControllerActivator(IDependencyResolver resolver) 13 { 14 if (resolver == null) 15 { 16 this._resolverThunk = (() => DependencyResolver.Current); 17 return; 18 } 19 this._resolverThunk = (() => resolver); 20 } 21 22 public IController Create(RequestContext requestContext, Type controllerType) 23 { 24 IController result; 25 try 26 { 27 result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType)); 28 } 29 catch (Exception innerException) 30 { 31 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, new object[] 32 { 33 controllerType 34 }), innerException); 35 } 36 return result; 37 } 38 } 39 40 private static readonly ConcurrentDictionary<Type, SessionStateBehavior> _sessionStateCache = new ConcurrentDictionary<Type, SessionStateBehavior>(); 41 42 private static ControllerTypeCache _staticControllerTypeCache = new ControllerTypeCache(); 43 44 private IBuildManager _buildManager; 45 46 private IResolver<IControllerActivator> _activatorResolver; 47 48 private IControllerActivator _controllerActivator; 49 50 private ControllerBuilder _controllerBuilder; 51 52 private ControllerTypeCache _instanceControllerTypeCache; 53 54 private IControllerActivator ControllerActivator 55 { 56 get 57 { 58 if (this._controllerActivator != null) 59 { 60 return this._controllerActivator; 61 } 62 this._controllerActivator = this._activatorResolver.Current; 63 return this._controllerActivator; 64 } 65 } 66 67 internal IBuildManager BuildManager 68 { 69 get 70 { 71 if (this._buildManager == null) 72 { 73 this._buildManager = new BuildManagerWrapper(); 74 } 75 return this._buildManager; 76 } 77 set 78 { 79 this._buildManager = value; 80 } 81 } 82 83 internal ControllerBuilder ControllerBuilder 84 { 85 get 86 { 87 return this._controllerBuilder ?? ControllerBuilder.Current; 88 } 89 set 90 { 91 this._controllerBuilder = value; 92 } 93 } 94 95 internal ControllerTypeCache ControllerTypeCache 96 { 97 get 98 { 99 return this._instanceControllerTypeCache ?? DefaultControllerFactory._staticControllerTypeCache; 100 } 101 set 102 { 103 this._instanceControllerTypeCache = value; 104 } 105 } 106 107 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.DefaultControllerFactory" /> class.</summary> 108 public DefaultControllerFactory() : this(null, null, null) 109 { 110 } 111 112 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.DefaultControllerFactory" /> class using a controller activator.</summary> 113 /// <param name="controllerActivator">An object that implements the controller activator interface.</param> 114 public DefaultControllerFactory(IControllerActivator controllerActivator) : this(controllerActivator, null, null) 115 { 116 } 117 118 internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver) 119 { 120 if (controllerActivator != null) 121 { 122 this._controllerActivator = controllerActivator; 123 return; 124 } 125 IResolver<IControllerActivator> arg_44_1 = activatorResolver; 126 if (activatorResolver == null) 127 { 128 arg_44_1 = new SingleServiceResolver<IControllerActivator>(() => null, new DefaultControllerFactory.DefaultControllerActivator(dependencyResolver), "DefaultControllerFactory constructor"); 129 } 130 this._activatorResolver = arg_44_1; 131 } 132 133 internal static InvalidOperationException CreateAmbiguousControllerException(RouteBase route, string controllerName, ICollection<Type> matchingTypes) 134 { 135 StringBuilder stringBuilder = new StringBuilder(); 136 foreach (Type current in matchingTypes) 137 { 138 stringBuilder.AppendLine(); 139 stringBuilder.Append(current.FullName); 140 } 141 Route route2 = route as Route; 142 string message; 143 if (route2 != null) 144 { 145 message = string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithRouteUrl, new object[] 146 { 147 controllerName, 148 route2.Url, 149 stringBuilder, 150 Environment.NewLine 151 }); 152 } 153 else 154 { 155 message = string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithoutRouteUrl, new object[] 156 { 157 controllerName, 158 stringBuilder, 159 Environment.NewLine 160 }); 161 } 162 return new InvalidOperationException(message); 163 } 164 165 private static InvalidOperationException CreateDirectRouteAmbiguousControllerException(ICollection<Type> matchingTypes) 166 { 167 StringBuilder stringBuilder = new StringBuilder(); 168 foreach (Type current in matchingTypes) 169 { 170 stringBuilder.AppendLine(); 171 stringBuilder.Append(current.FullName); 172 } 173 string message = string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_DirectRouteAmbiguous, new object[] 174 { 175 stringBuilder, 176 Environment.NewLine 177 }); 178 return new InvalidOperationException(message); 179 } 180 181 /// <summary>Creates the specified controller by using the specified request context.</summary> 182 /// <returns>The controller.</returns> 183 /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param> 184 /// <param name="controllerName">The name of the controller.</param> 185 /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception> 186 /// <exception cref="T:System.ArgumentException">The <paramref name="controllerName" /> parameter is null or empty.</exception> 187 public virtual IController CreateController(RequestContext requestContext, string controllerName) 188 { 189 if (requestContext == null) 190 { 191 throw new ArgumentNullException("requestContext"); 192 } 193 if (string.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch()) 194 { 195 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName"); 196 } 197 Type controllerType = this.GetControllerType(requestContext, controllerName); 198 return this.GetControllerInstance(requestContext, controllerType); 199 } 200 201 /// <summary>Retrieves the controller instance for the specified request context and controller type.</summary> 202 /// <returns>The controller instance.</returns> 203 /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param> 204 /// <param name="controllerType">The type of the controller.</param> 205 /// <exception cref="T:System.Web.HttpException"> 206 /// <paramref name="controllerType" /> is null.</exception> 207 /// <exception cref="T:System.ArgumentException"> 208 /// <paramref name="controllerType" /> cannot be assigned.</exception> 209 /// <exception cref="T:System.InvalidOperationException">An instance of <paramref name="controllerType" /> cannot be created.</exception> 210 protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType) 211 { 212 if (controllerType == null) 213 { 214 throw new HttpException(404, string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_NoControllerFound, new object[] 215 { 216 requestContext.HttpContext.Request.Path 217 })); 218 } 219 if (!typeof(IController).IsAssignableFrom(controllerType)) 220 { 221 throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase, new object[] 222 { 223 controllerType 224 }), "controllerType"); 225 } 226 return this.ControllerActivator.Create(requestContext, controllerType); 227 } 228 229 /// <summary>Returns the controller's session behavior.</summary> 230 /// <returns>The controller's session behavior.</returns> 231 /// <param name="requestContext">The request context.</param> 232 /// <param name="controllerType">The type of the controller.</param> 233 protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType) 234 { 235 if (controllerType == null) 236 { 237 return SessionStateBehavior.Default; 238 } 239 return DefaultControllerFactory._sessionStateCache.GetOrAdd(controllerType, delegate(Type type) 240 { 241 SessionStateAttribute sessionStateAttribute = type.GetCustomAttributes(typeof(SessionStateAttribute), true).OfType<SessionStateAttribute>().FirstOrDefault<SessionStateAttribute>(); 242 if (sessionStateAttribute == null) 243 { 244 return SessionStateBehavior.Default; 245 } 246 return sessionStateAttribute.Behavior; 247 }); 248 } 249 250 /// <summary>Retrieves the controller type for the specified name and request context.</summary> 251 /// <returns>The controller type.</returns> 252 /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param> 253 /// <param name="controllerName">The name of the controller.</param> 254 protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName) 255 { 256 if (requestContext == null) 257 { 258 throw new ArgumentNullException("requestContext"); 259 } 260 if (string.IsNullOrEmpty(controllerName) && (requestContext.RouteData == null || !requestContext.RouteData.HasDirectRouteMatch())) 261 { 262 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName"); 263 } 264 RouteData routeData = requestContext.RouteData; 265 if (routeData != null && routeData.HasDirectRouteMatch()) 266 { 267 return DefaultControllerFactory.GetControllerTypeFromDirectRoute(routeData); 268 } 269 object obj; 270 if (routeData.DataTokens.TryGetValue("Namespaces", out obj)) 271 { 272 IEnumerable<string> enumerable = obj as IEnumerable<string>; 273 if (enumerable != null && enumerable.Any<string>()) 274 { 275 HashSet<string> namespaces = new HashSet<string>(enumerable, StringComparer.OrdinalIgnoreCase); 276 Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces); 277 if (controllerTypeWithinNamespaces != null || false.Equals(routeData.DataTokens["UseNamespaceFallback"])) 278 { 279 return controllerTypeWithinNamespaces; 280 } 281 } 282 } 283 if (this.ControllerBuilder.DefaultNamespaces.Count > 0) 284 { 285 HashSet<string> namespaces2 = new HashSet<string>(this.ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase); 286 Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces2); 287 if (controllerTypeWithinNamespaces != null) 288 { 289 return controllerTypeWithinNamespaces; 290 } 291 } 292 return this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, null); 293 } 294 295 private static Type GetControllerTypeFromDirectRoute(RouteData routeData) 296 { 297 IEnumerable<RouteData> directRouteMatches = routeData.GetDirectRouteMatches(); 298 List<Type> list = new List<Type>(); 299 foreach (RouteData current in directRouteMatches) 300 { 301 if (current != null) 302 { 303 Type targetControllerType = current.GetTargetControllerType(); 304 if (targetControllerType == null) 305 { 306 throw new InvalidOperationException(MvcResources.DirectRoute_MissingControllerType); 307 } 308 if (!list.Contains(targetControllerType)) 309 { 310 list.Add(targetControllerType); 311 } 312 } 313 } 314 if (list.Count == 0) 315 { 316 return null; 317 } 318 if (list.Count == 1) 319 { 320 return list[0]; 321 } 322 throw DefaultControllerFactory.CreateDirectRouteAmbiguousControllerException(list); 323 } 324 325 private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces) 326 { 327 this.ControllerTypeCache.EnsureInitialized(this.BuildManager); 328 ICollection<Type> controllerTypes = this.ControllerTypeCache.GetControllerTypes(controllerName, namespaces); 329 switch (controllerTypes.Count) 330 { 331 case 0: 332 return null; 333 case 1: 334 return controllerTypes.First<Type>(); 335 default: 336 throw DefaultControllerFactory.CreateAmbiguousControllerException(route, controllerName, controllerTypes); 337 } 338 } 339 340 /// <summary>Releases the specified controller.</summary> 341 /// <param name="controller">The controller to release.</param> 342 public virtual void ReleaseController(IController controller) 343 { 344 IDisposable disposable = controller as IDisposable; 345 if (disposable != null) 346 { 347 disposable.Dispose(); 348 } 349 } 350 351 internal IReadOnlyList<Type> GetControllerTypes() 352 { 353 this.ControllerTypeCache.EnsureInitialized(this.BuildManager); 354 return this.ControllerTypeCache.GetControllerTypes(); 355 } 356 357 /// <summary>This API supports the ASP.NET MVC infrastructure and is not intended to be used directly from your code. This method calls the <see cref="M:System.Web.Mvc.DefaultControllerFactory.GetControllerSessionBehavior(System.Web.Routing.RequestContext,System.Type)" /> method.</summary> 358 /// <returns>The controller's session behavior.</returns> 359 /// <param name="requestContext">The request context.</param> 360 /// <param name="controllerName">The controller name.</param> 361 SessionStateBehavior IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, string controllerName) 362 { 363 if (requestContext == null) 364 { 365 throw new ArgumentNullException("requestContext"); 366 } 367 if (string.IsNullOrEmpty(controllerName)) 368 { 369 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName"); 370 } 371 Type controllerType = this.GetControllerType(requestContext, controllerName); 372 return this.GetControllerSessionBehavior(requestContext, controllerType); 373 } 374 }
既然是ControllerFactory,DefaultControllerFactory肯定也实现了IControllerFactory接口,别的我们就不看了,我们看看是如何创建Controller对象的,方法代码如下:
1 /// <summary>Creates the specified controller by using the specified request context.</summary> 2 /// <returns>The controller.</returns> 3 /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param> 4 /// <param name="controllerName">The name of the controller.</param> 5 /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception> 6 /// <exception cref="T:System.ArgumentException">The <paramref name="controllerName" /> parameter is null or empty.</exception> 7 public virtual IController CreateController(RequestContext requestContext, string controllerName) 8 { 9 if (requestContext == null) 10 { 11 throw new ArgumentNullException("requestContext"); 12 } 13 if (string.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch()) 14 { 15 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName"); 16 } 17 Type controllerType = this.GetControllerType(requestContext, controllerName); 18 return this.GetControllerInstance(requestContext, controllerType); 19 }
代码很简单,该方法获取Controller的Type类型对象,然后根据Type对象创建实例。这个方法里面有两个辅助方法,一个是GetControllerType方法,另一个是GetControllerInstance方法,根据名称我们就能知道是做什么的。我们先看看GetControllerType方法的源码吧,这里是关键,没有Type对象的获取,以后都是空言:
1 /// <summary>Retrieves the controller type for the specified name and request context.</summary> 2 /// <returns>The controller type.</returns> 3 /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param> 4 /// <param name="controllerName">The name of the controller.</param> 5 protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName) 6 { 7 if (requestContext == null) 8 { 9 throw new ArgumentNullException("requestContext"); 10 } 11 if (string.IsNullOrEmpty(controllerName) && (requestContext.RouteData == null || !requestContext.RouteData.HasDirectRouteMatch())) 12 { 13 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName"); 14 } 15 RouteData routeData = requestContext.RouteData; 16 if (routeData != null && routeData.HasDirectRouteMatch()) 17 { 18 return DefaultControllerFactory.GetControllerTypeFromDirectRoute(routeData); 19 } 20 object obj; 21 if (routeData.DataTokens.TryGetValue("Namespaces", out obj)) 22 { 23 IEnumerable<string> enumerable = obj as IEnumerable<string>; 24 if (enumerable != null && enumerable.Any<string>()) 25 { 26 HashSet<string> namespaces = new HashSet<string>(enumerable, StringComparer.OrdinalIgnoreCase); 27 Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces); 28 if (controllerTypeWithinNamespaces != null || false.Equals(routeData.DataTokens["UseNamespaceFallback"])) 29 { 30 return controllerTypeWithinNamespaces; 31 } 32 } 33 } 34 if (this.ControllerBuilder.DefaultNamespaces.Count > 0) 35 { 36 HashSet<string> namespaces2 = new HashSet<string>(this.ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase); 37 Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces2); 38 if (controllerTypeWithinNamespaces != null) 39 { 40 return controllerTypeWithinNamespaces; 41 } 42 } 43 return this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, null); 44 }
我们先根据RouteData路由数据来获取Controller的类型对象,如果RouteData不为空,并且在RouteData的Values属性中包含Key为“MS_DirectRouteMatches”的值,那我们就据此获取Controller的类型对象,如果没找到就返回Null值,如果有一个值,就会作为Controller的Type类型值返回,如果多于一个就会抛出异常。如果RouteData不包含Key为“MS_DirectRouteMatches”的值,我们就根据RouteData对象中DataTokens属性Key为“Namespaces”来获取Controller的Type对象,同理,如果没找到就返回null,找到一个就直接返回,如果多余一个的话就抛出异常。
如果我们还是没找到怎么办呢?我们就要看看能不能使用后备命名空间,如果可以,就根据此命名空间来查找,我们从RouteData路由数据的DataTokens属性中查找是否包含有Key为“UseNamespaceFallback”的值,如果有,并且是False,就直接返回,结束查找,如果不包含Key为“UseNamepsaceFallback”的值或者该值为True,我们就可以根据ControllerBuilder的DefaultNamespaces属性表示后备命名空间查找Controller的类型,同理,没有找到就返回null,找到一个就作为结果值直接返回,如果多于一个那就要抛出异常了。
好了,大概的逻辑写完了,如果还有不明白的,就仔细的看看源码把,如果把源码完整的看一遍,然后在整理逻辑,就容易多了。看代码别太注意代码细节,关注主要的关键点或者叫关键类就可以,把哥哥类之间的关系和责任整理清楚,理解起来并不复杂。
三、扩展点
到了现在,Controller激活系统就写的差不多了,唯一还差一点的就是扩展点还没提。ASP.NET MVC号称几乎任何地方都可以扩展,Controller激活系统中肯定也包含着扩展点,下来我们一一详述。
扩展点一:扩展IControllerFactory接口
第一个很容易想到的扩展点就是实现IControllerFactory接口,定义一个全新的ControllerFactory类型。如果是这样,里面所有的功能的就需要全部重新实现,包括Controller类型的解析、Controller实例的创建和销毁、也包括会话状态行为模式的管理。这样的好处就是我们就有更好的控制权了,可以按着我们的思想实现其功能。但是如果觉得重新实现有点麻烦,而且有时候也没有必要,拿我们就可以继承DefaultControllerFactory类型,针对特定的方法去扩展,实现我们自己的功能要求。比如我们想增加Ioc,有了IOC可以解耦Controller和Model的关系。一般来说,Controller实例的创建才需要IOC容器的控制,我们直接从DefaultControllerFactory类型继承,重写Controller实例创建的逻辑即可,这样也避免了全部重新实现,做起事来就更事半功倍了。
DefaultControllerFactory类型在创建Controller实例的时候,有两个方法是可以扩展的,一个是受保护的虚方法GetControllerType,我们可以定义自己的Controller类型的解析方法的实现,方法签名如下:
protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)
其实这也是一个扩展点,但是我们今天不扩展这里,因为IOC框架和这里的关系不大,我们要扩展的是第二受保护的虚方法,方法签名如下:
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
这个方法是用于创建Controller实例的,这里正好可以使用IOC容器来管理Controller实例,当然在DefaultControllerFactory类型中还有其他的扩展点,大家可以自己去看,全部代码我以前也贴出来过。
1 public class UnityControllerFactory:DefaultControllerFactory 2 { 3 /// <summary> 4 /// IOC的容器对象 5 /// </summary> 6 public IUnityContainer UnityContainer { get; private set; } 7 8 /// <summary> 9 /// 通过构造函数初始化IOC的容器对象 10 /// </summary> 11 /// <param name="unityContainer">传入的IOC容器对象</param> 12 public UnityControllerFactory(IUnityContainer unityContainer) 13 { 14 if (unityContainer != null) 15 { 16 this.UnityContainer = unityContainer; 17 } 18 else 19 { 20 throw new ArgumentNullException("容器对象不能为空!"); 21 } 22 } 23 24 /// <summary> 25 /// 26 /// </summary> 27 /// <param name="requestContext"></param> 28 /// <param name="controllerType"></param> 29 /// <returns></returns> 30 protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) 31 { 32 if (controllerType == null) 33 { 34 return null; 35 } 36 return (IController)this.UnityContainer.Resolve(controllerType); 37 } 38 }
代码很简单,我们将解析出来的Controller类型作为参数调用UnityContainer对象的Resolve方法,进而得到最终激活的Controller对象。这是样例代码,具体项目中,还要引入Unity的DLL,还需要注册,这些我就不细说了。
扩展点二:扩展ControllerActivator
DefaultControllerFactory对象并不直接激活目标Controller对象,真正激活Controller对象的是一个叫做ControllerActiviator类型的对象,换句话说,DefaultControllerFactory对象委托ControllerActivator对象根据解析出来类型创建对应Controller对象。所有的ControllerActivator对象均实现IControllerActivator接口,该接口定义如下:
/// <summary>Provides fine-grained control over how controllers are instantiated using dependency injection.</summary> public interface IControllerActivator { /// <summary>When implemented in a class, creates a controller.</summary> /// <returns>The created controller.</returns> /// <param name="requestContext">The request context.</param> /// <param name="controllerType">The controller type.</param> IController Create(RequestContext requestContext, Type controllerType); }
该接口中定义了Create方法实现针对目标Controller对象的创建,两个参数分别表示请求上下文和Controller的类型。我们在看看DefaultControllerFactory和ControllerActivator之间的关系吧,DefaultControllerFactory类型有三个构造函数,代码如下:
1 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.DefaultControllerFactory" /> class.</summary> 2 public DefaultControllerFactory() : this(null, null, null) 3 { 4 } 5 6 /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.DefaultControllerFactory" /> class using a controller activator.</summary> 7 /// <param name="controllerActivator">An object that implements the controller activator interface.</param> 8 public DefaultControllerFactory(IControllerActivator controllerActivator) : this(controllerActivator, null, null) 9 { 10 } 11 12 internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver) 13 { 14 if (controllerActivator != null) 15 { 16 this._controllerActivator = controllerActivator; 17 return; 18 } 19 IResolver<IControllerActivator> arg_44_1 = activatorResolver; 20 if (activatorResolver == null) 21 { 22 arg_44_1 = new SingleServiceResolver<IControllerActivator>(() => null, new DefaultControllerFactory.DefaultControllerActivator(dependencyResolver), "DefaultControllerFactory constructor"); 23 } 24 this._activatorResolver = arg_44_1; 25 }
第二个和第三个构造函数都有一个IControllerActivator类型的参数,第三个是程序集内部的,暂时不说,就说第二吧,DefaultControllerFactory正式通过该参数来创建目标Controller对象的,我们基于ControllerActivator对象来创建一个DefaultControllerFactory对象,他会最终被用于创建目标的Controller对象,我们的扩展点也就找到了。
我们扩展的源码如下:
1 public class NinjectControllerActivator : IControllerActivator 2 { 3 public IKernel Kernel { get; private set; } 4 5 public NinjectControllerActivator() 6 { 7 this.Kernel = new StandardKernel(); 8 } 9 public IController Create(RequestContext requestContext, Type controllerType) 10 { 11 return (IController)this.Kernel.TryGet(controllerType); 12 } 13 14 public void Register<TFrom, TTo>() where TTo : TFrom 15 { 16 this.Kernel.Bind<TFrom>().To<TTo>(); 17 } 18 }
扩展写好了,还需要在Global.asax文件里面进行注册,否则不能使用。IOC相关的内容就不介绍了,大家可以自行研究。
扩展点三:扩展DependencyResolver
如果我们在构建DefaultControllerFactory对象的时候没有显示指定采用的ControllerActiviator参数,则它会使用默认的一个ControllerActivator对象DefaultControllerActivator来完成,该类型是一个私有类型,外界无法访问。源码如下:
1 private class DefaultControllerActivator : IControllerActivator 2 { 3 private Func<IDependencyResolver> _resolverThunk; 4 5 public DefaultControllerActivator() : this(null) 6 { 7 } 8 9 public DefaultControllerActivator(IDependencyResolver resolver) 10 { 11 if (resolver == null) 12 { 13 this._resolverThunk = (() => DependencyResolver.Current); 14 return; 15 } 16 this._resolverThunk = (() => resolver); 17 } 18 19 public IController Create(RequestContext requestContext, Type controllerType) 20 { 21 IController result; 22 try 23 { 24 result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType)); 25 } 26 catch (Exception innerException) 27 { 28 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, new object[] 29 { 30 controllerType 31 }), innerException); 32 } 33 return result; 34 } 35 }
其实DefaultControllerActivator对象要像创建目标Controller对象,还依赖另外一个叫DependencyResolver的对象。所有的DenpendencyResolver类型均实现了IDenpendencyResolver接口,接口定义如下:
1 /// <summary>Defines the methods that simplify service location and dependency resolution.</summary> 2 public interface IDependencyResolver 3 { 4 /// <summary>Resolves singly registered services that support arbitrary object creation.</summary> 5 /// <returns>The requested service or object.</returns> 6 /// <param name="serviceType">The type of the requested service or object.</param> 7 object GetService(Type serviceType); 8 9 /// <summary>Resolves multiply registered services.</summary> 10 /// <returns>The requested services.</returns> 11 /// <param name="serviceType">The type of the requested services.</param> 12 IEnumerable<object> GetServices(Type serviceType); 13 }
系统中使用的DependencyResolver是通过DependencyResolver类型注册的。DependencyResolver类型有一个叫做Current的静态属性,该属性表示的是当前的、IDependencyResolver类型的对象。我们可以通过静态方法SetResolver来注册IDependencyResolver对象。需要说明一点,还有静态方法的DependencyResolver类型并没有实现IDependencyResolver接口,所以说该类型并不是真正的DependencyResolver类型,我们可以把它理解为一个Facade,它汇集了所要使用的对象和方法而已,使用更方便。
如果我们没有对DependencyResolver进行显示的注册,则系统会使用一个默认的叫做DefaultDependencyResolver的对象,本对象也是一个私有类型,外界无法使用。
当我们通过无参的构造函数或者通过将ControllerActivator参数指定为null的构造函数来创建DefaultControllerFactory实例的时候,那么用于激活Controller实例的将是通过DependencyResolver类型的静态属性Current表示的DependencyResolver对象来激活的,我们的扩展终于也找到了。
最后实现的代码如下:
1 public class NinjectDependencyResolver : IDependencyResolver 2 { 3 public IKernel Kernel { get; private set; } 4 5 public NinjectDependencyResolver() 6 { 7 this.Kernel = new StandardKernel(); 8 } 9 10 public void Register<TFrom, TTo>() where TTo : TFrom 11 { 12 this.Kernel.Bind<TFrom>().To<TTo>(); 13 } 14 15 public object GetService(Type serviceType) 16 { 17 return this.Kernel.TryGet(serviceType); 18 } 19 20 public IEnumerable<object> GetServices(Type serviceType) 21 { 22 return this.Kernel.GetAll(serviceType); 23 } 24 }
好了,扩展点写完 ,大家有时间可以看看源码,可以有很多启发,获得的也更多。
四、小结
我们可以小结了,到此,Controler激活系统这个小节我就写完了。其实不是很复杂,大家在看的时候,刚开始别太关注代码的细节,先把握整个数据流向,或者叫请求脉络,把里面所涉及到的对象和关系都整理清楚了,然后再有针对性的去看涉及到的每个对象,不是画画图,不是画画,是画各个对象之间的关系图,图能画出来,说明你的心中就有了整体把握了。如果太关注细节,太关注某个类型的代码,也会很容易顾此失彼,因为我开始就是那样,总是看了后面忘记前面,请求的线路也不是很清楚。所以我们学习的时候也要有方法,方法对,学的就快,成就感就能很快产生。好了,说了这么多了,继续努力吧,希望我写的东西对大家有所帮助。