protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (String.IsNullOrEmpty(controllerName) && (requestContext.RouteData == null || !requestContext.RouteData.HasDirectRouteMatch())) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName"); } RouteData routeData = requestContext.RouteData; if (routeData != null && routeData.HasDirectRouteMatch()) { return GetControllerTypeFromDirectRoute(routeData); } // first search in the current route's namespace collection object routeNamespacesObj; Type match; if (routeData.DataTokens.TryGetValue(RouteDataTokenKeys.Namespaces, out routeNamespacesObj)) { IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>; if (routeNamespaces != null && routeNamespaces.Any()) { HashSet<string> namespaceHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase); match = GetControllerTypeWithinNamespaces。 (routeData.Route, controllerName, namespaceHash); // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true" if (match != null || false.Equals(routeData.DataTokens[RouteDataTokenKeys.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> namespaceDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase); match = GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaceDefaults); if (match != null) { return match; } } // if all else fails, search every namespace return GetControllerTypeWithinNamespaces(routeData.Route, controllerName, null /* namespaces */); }
首先進行了入口的檢查,HasDirectRouteMatch這個方法是用來判斷這個RouteData是否是特性路由?應該是這個做用。如今還沒知道,之後說WebApi的時候可能會說到。全部默認狀況是不會去執行GetControllerTypeFromDirectRoute的方法的。在Route中獲取命名空間,若是設置了NameSpace的話便在這邊取出,而後轉化爲List
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); } }
GetControllerTypeWithinNamespaces裏面就就兩句,經過BuildManager初始化,ControllerTypeCache看這個名字也知道是控制器類型緩存,而後經過名字和namesapces得到Type,若是0個返回null,1個正確,2個便報錯。mvc
主要看一下EnsureInitialized和GetControllerTypes這兩個方法。app
public void EnsureInitialized(IBuildManager buildManager) { if (_cache == null) { lock (_lockObj) { if (_cache == null) { //TypeCaheName='MVC-ControllerTypeCache.xml' 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的GetFilteredTypesFromAssemblies的方法。獲得以後,對控制器的名字進行截取,取控制前的名字進行分組(GroupBy)。而後封裝成一個字典_cache。而後咱們看看GetFilteredTypesFromAssemblies這個方法裏面發生了什麼。ui
//predicate == IsControllerType public static List<Type> GetFilteredTypesFromAssemblies(string cacheName, Predicate<Type> predicate, IBuildManager buildManager) { TypeCacheSerializer serializer = new TypeCacheSerializer(); // 首先從磁盤文件中讀取數據 cacheName='MVC-ControllerTypeCache.xml' List<Type> matchingTypes = ReadTypesFromCache(cacheName, predicate, buildManager, serializer); if (matchingTypes != null) { return matchingTypes; } // 若是讀到數據,將每一個數據進行對比 matchingTypes = FilterTypesInAssemblies(buildManager, predicate).ToList(); // 最後保存會磁盤中 SaveTypesToCache(cacheName, matchingTypes, buildManager, serializer); return matchingTypes; }
在c盤應該能夠找到和這個文件,這就是用來進行緩存mvc和Action的類型。裏面的細節的邏輯就不看。主要看這個方法FilterTypesInAssemblies,方法的意思應該就是在程序集中過濾Type。spa
//predicate == IsControllerType private static IEnumerable<Type> FilterTypesInAssemblies(IBuildManager buildManager, Predicate<Type> predicate) { // 瀏覽應用的全部程序集,並進行謂語匹配 IEnumerable<Type> typesSoFar = Type.EmptyTypes; ICollection assemblies = buildManager.GetReferencedAssemblies(); foreach (Assembly assembly in assemblies) { Type[] typesInAsm; try { typesInAsm = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { typesInAsm = ex.Types; } typesSoFar = typesSoFar.Concat(typesInAsm); } return typesSoFar.Where(type => TypeIsPublicClass(type) && predicate(type)); }
定義了一個空的類型,經過buildManager.GetReferencedAssemblies()得到引用的程序集。循環,獲取每一個程序集的type,在最後的時候進行條件過濾,在最前面標註了predicate的值是IsControllerType。這個值是定義在ControllerTypeCache中的。code
internal static bool IsControllerType(Type t) { return t != null && t.IsPublic && t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) && !t.IsAbstract && typeof(IController).IsAssignableFrom(t); }
就是說這個Type是否是public,是否是已Controller結尾,要不是抽象類,是否是繼承IController。經過的Type放到typesSoFar。而後返回matchingTypes,而後賦給controllerTypes。而後賦值給_cache。 最後進行路由匹配。orm
public ICollection<Type> GetControllerTypes(string controllerName, HashSet<string> namespaces) { HashSet<Type> matchingTypes = new HashSet<Type>(); ILookup<string, Type> namespaceLookup; if (_cache.TryGetValue(controllerName, out namespaceLookup)) { // 若是命名空間不爲空的話,循環Type比較Type的命名空間是否和設置的一致。 if (namespaces != null) { foreach (string requestedNamespace in namespaces) { foreach (var targetNamespaceGrouping in namespaceLookup) { if (IsNamespaceMatch(requestedNamespace, targetNamespaceGrouping.Key)) { matchingTypes.UnionWith(targetNamespaceGrouping); } } } } else { // 若是命名空間爲空的話,搜索全部的namespace foreach (var namespaceGroup in namespaceLookup) { matchingTypes.UnionWith(namespaceGroup); } } } return matchingTypes; }
調用了GetController的方法。這裏纔是真正的比對控制器的。首先創建了一個HashSet 泛型 的對象。經過controllerName的值在_cache中獲取,有值,判斷命名空間又沒有,有的話,循環每一個命名空間。比對分完組的Controller集合。調用UnionWith的方法。這個是HashSet裏面的方法。在集合中進行比較。若是沒有重複就放進去。因此到最後返回的是一個Type的集合。
理一下上面的邏輯。xml
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (controllerType == null) { throw new HttpException(404, String.Format( CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_NoControllerFound, requestContext.HttpContext.Request.Path)); } if (!typeof(IController).IsAssignableFrom(controllerType)) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase, controllerType), "controllerType"); } return ControllerActivator.Create(requestContext, controllerType); }
再次判斷一下Type。其實最後起做用的只有最後一句。首先要看一下ControllerActivator這個屬性是什麼樣的。對象
internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver) { if (controllerActivator != null) { _controllerActivator = controllerActivator; } else { _activatorResolver = activatorResolver ?? new SingleServiceResolver<IControllerActivator>( () => null, new DefaultControllerActivator(dependencyResolver), "DefaultControllerFactory constructor"); } } private IControllerActivator ControllerActivator { get { if (_controllerActivator != null) { return _controllerActivator; } _controllerActivator = _activatorResolver.Current; return _controllerActivator; } }
能夠看出是_activatorResolver.Current. _activatorResolver這個類型是何時初始化的呢?是在DefaultControllerFactory建立的時候,能夠看到又是使用了SingleServiceResolver,這個是ControllerFactory的同樣的套路,若是DependencyResolver中註冊了對應的Resolver能夠返回IControllerActivator就會首先返回這個。若是沒有設置的話也就是默認狀態的話會使用DefaultControllerActivator。繼承
public IController Create(RequestContext requestContext, Type controllerType) { try { return (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType)); } catch (Exception ex) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, controllerType), ex); } }
若是構造DefaultControllerActivator的參數dependencyResolver爲空,那就經過反射進行構造返回就好了。