上文说到Routing Module将控制权交给了MvcHandler,因为MvcHandler实现了IHttpAsyncHandler接口,因此紧接着就会调用 BeginProcessRequest方法,这个方法首先会进行一些Trust Level之类的安全检测,暂且不谈,然后会调用ProcessRequestInit方法(有删节):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) { // Get the controller type string controllerName = RequestContext.RouteData.GetRequiredString( "controller" ); // Instantiate the controller and call Execute factory = ControllerBuilder.GetControllerFactory(); controller = factory.CreateController(RequestContext, controllerName); if (controller == null ) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, factory.GetType(), controllerName)); } } |
首 先会获得controller的名字,然后会实例化controller,这里采用了抽象工厂的模式,首先利用ControllerBuilder获得一 个IControllerFactory的实例,ControllerBuilder采用Dependency Injection来实例化IControllerFactory,关于MVC中DI的实现以后另文介绍,在默认情况 下,ControllerBuilder会返回一个实例。接着调用CreateController方法:
1 2 3 4 5 | public virtual IController CreateController(RequestContext requestContext, string controllerName) { Type controllerType = GetControllerType(requestContext, controllerName); IController controller = DefaultControllerFactoryGetControllerInstance(requestContext, controllerType); return controller; } |
方法分为两步,先获得类型,再获得实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName) { // first search in the current route's namespace collection object routeNamespacesObj; Type match; if (requestContext != null && requestContext.RouteData.DataTokens.TryGetValue( "Namespaces" , out routeNamespacesObj)) { IEnumerable< string > routeNamespaces = routeNamespacesObj as IEnumerable< string >; if (routeNamespaces != null && routeNamespaces.Any()) { HashSet< string > nsHash = new HashSet< string >(routeNamespaces, StringComparer.OrdinalIgnoreCase); match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsHash); // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true" if (match != null || false .Equals(requestContext.RouteData.DataTokens[ "UseNamespaceFallback" ])) { // got a match or the route requested we stop looking return match; } } } // then search in the application's default namespace collection if (ControllerBuilder.DefaultNamespaces.Count > 0) { HashSet< string > nsDefaults = new HashSet< string >(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase); match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsDefaults); if (match != null ) { return match; } } // if all else fails, search every namespace return GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null
); } |
DefaultControllerFactory 在根据路由信息查找对应Controller的类型的时候,首先判断DataToken中有没有Namespace,然后调用 GetControllerTypeWithinNamespaces 方法查找Controller对应的类。先看下这个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet< string > namespaces) { // Once the master list of controllers has been created we can quickly index into it ControllerTypeCache.EnsureInitialized(BuildManager); ICollection<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces); switch (matchingTypes.Count) { case 0: // no matching types return null ; case 1: // single matching type return matchingTypes.First(); default : // multiple matching types throw CreateAmbiguousControllerException(route, controllerName, matchingTypes); } } |
ASP.NET 中大量的用到了反射,因此也需要把这些反射出的类进行缓存以提高性能,首先看下EnsureInitialized这个比较有意思的方法,这个方法的参数 BuildManager经过了层层包装,其实只是System.Web.Compilation.BuildManager的一个实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public void EnsureInitialized(IBuildManager buildManager) { if (_cache == null ) { lock (_lockObj) { if (_cache == null ) { List<Type> controllerTypes = TypeCacheUtil.GetFilteredTypesFromAssemblies(_typeCacheName, IsControllerType, buildManager); var groupedByName = controllerTypes.GroupBy( t => t.Name.Substring(0, t.Name.Length - "Controller" .Length), StringComparer.OrdinalIgnoreCase); _cache = groupedByName.ToDictionary( g => g.Key, g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); } } } } |
首先TypeCacheUtil获得所有是Controller的类型。TypeCacheUtil在前文已经出现过,用来获取所有的AreaRegistration的子类型,这里仔细看下这个方法: