GetControllerType和GetcontrollerInstance

GetControllerType

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,调用了GetControllerTypeWithinNamespaces。如果ControllerBuilder的DefaultNamespaces不为空,取出来调用GetControllerTypeWithinNamespaces,如果都没有的话直接调用GetControllerTypeWithinNamespaces,只是namespaces的部分为空。简单说,就是将DataTokens中的namespace和ControllerBuilder的DefaultNamespaces取出来调用GetControllerTypeWithinNamespaces。

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个便报错。

主要看一下EnsureInitialized和GetControllerTypes这两个方法。

EnsureInitialized

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这个方法里面发生了什么。

//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。

//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中的。

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。 最后进行路由匹配。

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的集合。

理一下上面的逻辑。

  • GetControllerType 方法 通过不同的设置namespace的情况分别调用GetControllerTypeWithinNamespaces。

    • GetControllerTypeWithinNamespaces 方法内的 EnsureInitialized

      • 首先读取缓存文件
      • 然后循环所有的引用程序集,通过IsControllerType过滤类型
      • 保存会程序集中
      • 将返回的符合的程序集通过名字分组,赋给_cache属性
    • GetControllerTypeWithinNamespaces方法 GetControllerTypes 方法
      • 如果有命名空间,通过比对_cache中的Type的命名空间比对,天玑道machtingType中
      • 没有的话,比对所有的Type

GetControllerInstance

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为空,那就通过反射进行构造返回就行了。

MVC 源码系列之控制器激活(二)之GetControllerType和GetcontrollerInstance的更多相关文章

  1. MVC 源码系列之控制器激活(一)

    Controller的激活 上篇说到Route的使用,GetRoute的方法里面获得RouteData.然后通过一些判断,将最后的RouteData中的RouteHandler添加到context.R ...

  2. MVC 源码系列之控制器执行(二)

    ## 控制器的执行 上一节说道Controller中的ActionInvoker.InvokeAction public virtual bool InvokeAction(ControllerCon ...

  3. MVC 源码系列之控制器执行(一)

    控制器的执行 之前说了Controller的激活,现在看一个激活Controller之后控制器内部的主要实现. public interface IController { void Execute( ...

  4. 精尽Spring MVC源码分析 - HandlerMapping 组件(二)之 HandlerInterceptor 拦截器

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  5. 精尽Spring MVC源码分析 - HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  6. MVC 源码系列之路由(一)

    路由系统 注释:这部分的源码是通过Refector查看UrlRoutingModule的源码编写,这部分的代码没有写到MVC中,却是MVC的入口. 简单的说一下激活路由之前的一些操作.一开始是由MVC ...

  7. MVC 源码系列之路由(二)

    MVCParseData和Match方法的实现 ### ParseData 那么首先要了解一下ParseData. //namespace Route public string Url { get ...

  8. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  9. 精尽Spring MVC源码分析 - 文章导读

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

随机推荐

  1. linux Apache 日志轮询

    安装日志轮询工具 cronolog [root@Nagios-Server tools]# wgethttp://cronolog.org/download/cronolog-1.6.2.tar.gz ...

  2. numpy中的range()

    1.arange返回一个array对象,arange(5)=([0,1,2,3,4]) 2.如果是两个参数,第一个参数是起点,第二个参数是终点 3.如果是三个参数,那么第三个参数就是步长

  3. NLP 中任务及相关概念

    命名实体识别 命名实体识别(Named Entity Recognition,简称NER),又称作"专名识别",是指识别文本中具有特定意义的实体,主要包括人名.地名.机构名.专有名 ...

  4. java并发学习--第十章 java内存模型的内存语义

    一.锁的内存语义 所为的java内存模型的内存语义指的就是在JVM中的实现原则. 锁的内存语义:锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息. 我们把上面这句话再整理下: ...

  5. java File获取字节流

    /*文件64位编码*/ public static void main(String[] args) { byte[] fileByte = toByteArray(newFile); String ...

  6. python基本数据类型常用方法

    python基本数据类型 1.整型 1.1 int 1.2 bit_lenght # 当前数字的二进制位数,至少用n位表示 r = age.bit_length() >>> a = ...

  7. Django【第25篇】:后端CORS解决跨域问题

    解决跨域问题 一.为什么会有跨域问题? 是因为浏览器的同源策略是对ajax请求进行阻拦了,但是不是所有的请求都给做跨域,像是一般的href属性,a标签什么的都不拦截. 二.解决跨域问题的两种方式 JS ...

  8. Java日期时间以及日期相互转换_java - JAVA

    文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 Java日期时间,以及相互转化,供大家参考,具体内容如下 package com.study.string; impor ...

  9. Linux kswapd0 进程CPU占用过高

    图便宜买了个1核1G虚拟机,启动两个jar后cpu飙升直接卡死,查看cpu及内存占用 发现kswapd0进程cpu占用一直居高不下,于是查询资料,总结如下. swap分区的作用是当物理内存不足时,会将 ...

  10. linux运维、架构之路-tomcat日志切割工具 logrotate

    一.Logrotate简介 1.Logrotate实际就是对日志进行切割的小工具,他通过让用户来配置规则的方式,检测和处理日志文件.配合Cron可让处理定时化:2.Logrotate预制了大量判断条件 ...