• MVC 源码系列之控制器激活(二)之GetControllerType和GetcontrollerInstance


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

  • 相关阅读:
    模拟+bfs——cf1344D
    【留坑】模拟+极角排序+预处理——ICPC PNWRC 2019 H
    【好题】思维+几何+离散化——ICPC PNWRC 2019 G
    【难】组合数学+dp——ICPC PNWRC 2019
    【好题】导数+统计贡献+扫描—— icpc PNWRC 2019
    【模板】二分图匹配+构造+最大独立集——icpc PNWRC 2019
    [Scoi2014]方伯伯的OJ(动态开点splay)
    [ZJOI2006]书架(二分+树状数组)
    [TJOI2017]不勤劳的图书管理员(分块+树状数组)
    [APIO2016]Gap(交互)
  • 原文地址:https://www.cnblogs.com/shaoqi/p/7384440.html
Copyright © 2020-2023  润新知