zoukankan      html  css  js  c++  java
  • asp.net mvc源码分析Controllerl篇 如何创建Controller实例

    在上一篇文章asp.net mvc源码分析-路由篇 如何找到 IHttpHandler中最后提到了MvcHandler,来上我们看看它的定义

     public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState

    它有几个比较重要的属性:

    internal ControllerBuilder ControllerBuilder  ,ControllerBuilder 类主要负责创建IControllerFactory

    public RequestContext RequestContext,RequestContext是对这次请求的HttpContext(httpContext类)和RouteData的包装

    而它的BeginProcessRequest方法中有几句比较重要的代码:

    IControllerFactory factory;
    ProcessRequestInit(httpContext, out controller, out factory);
    IAsyncController asyncController = controller as IAsyncController;

    if (asyncController != null) {
    }
    else
    {
      Action action = delegate {
                            try {
                                controller.Execute(RequestContext);
                            }
                            finally {
                                factory.ReleaseController(controller);
                            }
     return AsyncResultWrapper.BeginSynchronous(callback, state, action, _processRequestTag);
    }

    在这篇文章中我们主要关心ProcessRequestInit(httpContext, out controller, out factory);    如何创建controller

    ProcessRequestInit方法大致如下:

      private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) {
                string controllerName = RequestContext.RouteData.GetRequiredString("controller");
                factory = ControllerBuilder.GetControllerFactory();
                controller = factory.CreateController(RequestContext, controllerName);

      }

             string controllerName = RequestContext.RouteData.GetRequiredString("controller");这句很简单获取Controller的名称,RouteData是当前路由数据,里面的key包括我们注册是的url格式  "{controller}/{action}/{id}",  和默认的defaults=new { controller = "Home", action = "Index", id = UrlParameter.Optional } 

    现在大家知道为什么了我上一篇文章提到{controller}/{action}是必须得了吧。

    现在让我们来看看ControllerBuilder的主要代码:

     public ControllerBuilder()
                : this(null) {
            }
    
            internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver) {
                _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
                    () => _factoryThunk(),
                     new DefaultControllerFactory { ControllerBuilder = this },
                    "ControllerBuilder.GetControllerFactory"
                );
            }
    
            public static ControllerBuilder Current {
                get {
                    return _instance;
                }
            }  
    public IControllerFactory GetControllerFactory() {
                return _serviceResolver.Current;
            }
    
            public void SetControllerFactory(IControllerFactory controllerFactory) {
                if (controllerFactory == null) {
                    throw new ArgumentNullException("controllerFactory");
                }
    
                _factoryThunk = () => controllerFactory;
            }
    
            public void SetControllerFactory(Type controllerFactoryType) {
                if (controllerFactoryType == null) {
                    throw new ArgumentNullException("controllerFactoryType");
                }
                if (!typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType)) {
                    throw new ArgumentException(
                        String.Format(
                            CultureInfo.CurrentCulture,
                            MvcResources.ControllerBuilder_MissingIControllerFactory,
                            controllerFactoryType),
                        "controllerFactoryType");
                }
    
                _factoryThunk = delegate() {
                    try {
                        return (IControllerFactory)Activator.CreateInstance(controllerFactoryType);
                    }
                    catch (Exception ex) {
                        throw new InvalidOperationException(
                            String.Format(
                                CultureInfo.CurrentCulture,
                                MvcResources.ControllerBuilder_ErrorCreatingControllerFactory,
                                controllerFactoryType),
                            ex);
                    }
                };
            }
    

      由以上代码我们可以知道如果没有调用ControllerBuilder的SetControllerFactory方法那我们就用默认的DefaultControllerFactory,如果设置了我们就可以用我们自己ControllerFactory。如果项目有需要实现自己的ControllerFactory,那么我们可以Global.asax.cs的 protected void Application_Start()方法调用 ControllerBuilder.Current.SetControllerFactory(xxx); 

    让我们来看看DefaultControllerFactory是怎么创建的:

      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 contstructor"

                    );
                }
            }

    这里我们涉及到一个IControllerActivator接口,默认情况下用的是DefaultControllerActivator类,

    现在我们已经得到了ControllerFactory,再看看它是怎么创建Controller的,说白了就是DefaultControllerFactory的CreateController方法

     public virtual IController CreateController(RequestContext requestContext, string controllerName) {
        。。
                Type controllerType = GetControllerType(requestContext, controllerName);
                IController controller = GetControllerInstance(requestContext, controllerType);

                return controller;
            }

    默认我们是没有namespaces,而GetControllerType(requestContext, controllerName)主要是调用 GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null /* namespaces */)

      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);
                }
            }
    

    这里面主要涉及到一个ControllerTypeCache,它负责缓存当前程序中所有的Controller。

    让我们看看ControllerTypeCache的主要方法吧

    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);
                        }
                    }
                }
            }
      internal static bool IsControllerType(Type t) {
                return
                    t != null &&
                    t.IsPublic &&
                    t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
                    !t.IsAbstract &&
                    typeof(IController).IsAssignableFrom(t);
            }
     public ICollection<Type> GetControllerTypes(string controllerName, HashSet<string> namespaces) {
                HashSet<Type> matchingTypes = new HashSet<Type>();
                ILookup<string, Type> nsLookup;
                if (_cache.TryGetValue(controllerName, out nsLookup)) {
                    // this friendly name was located in the cache, now cycle through namespaces
                    if (namespaces != null) {
                        foreach (string requestedNamespace in namespaces) {
                            foreach (var targetNamespaceGrouping in nsLookup) {
                                if (IsNamespaceMatch(requestedNamespace, targetNamespaceGrouping.Key)) {
                                    matchingTypes.UnionWith(targetNamespaceGrouping);
                                }
                            }
                        }
                    }
                    else {
                        // if the namespaces parameter is null, search *every* namespace
                        foreach (var nsGroup in nsLookup) {
                            matchingTypes.UnionWith(nsGroup);
                        }
                    }
                }
                return matchingTypes;
            }
    

     List<Type> controllerTypes = TypeCacheUtil.GetFilteredTypesFromAssemblies(_typeCacheName, IsControllerType, buildManager);这句代码就是用来查询当前程序的类,这个类必须通过IsControllerType方法的验证(类是共有非抽象继承于IController且类名Controller结尾),而这个_cache是个Dictionary<string, ILookup<string, Type>>类型的数据。
    现在我们已经得到了ControllerType,来看看 IController controller = GetControllerInstance(requestContext, controllerType);吧里面主要就一句
     ControllerActivator.Create(requestContext, controllerType);而ControllerActivator就是先前的DefaultControllerActivator,DefaultControllerActivator的代码如下:

    private class DefaultControllerActivator : IControllerActivator {
                Func<IDependencyResolver> _resolverThunk;
                public DefaultControllerActivator()
                    : this(null) {
                }
                public DefaultControllerActivator(IDependencyResolver resolver) {
                    if (resolver == null) {
                        _resolverThunk = () => DependencyResolver.Current;
                    }
                    else {
                        _resolverThunk = () => resolver;
                    }
                }
                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);
                    }
                }
            }
    

     这里的_resolverThunk()来源于DependencyResolver.Current,
    private static DependencyResolver _instance = new DependencyResolver();
            public static IDependencyResolver Current {
                get {
                    return _instance.InnerCurrent;
                }
            }
     private IDependencyResolver _current = new DefaultDependencyResolver();
            public IDependencyResolver InnerCurrent {
                get {
                    return _current;
                }
            }
    所以真正创建controller是在DefaultDependencyResolver的GetService方法,核心代码   return Activator.CreateInstance(serviceType);
    这篇文件扯得比较远,可见创建Controller是多么的麻烦啊。阅读源代码主要是让我们理解其逻辑,同时也让我们学习其开发的思想。
    本文讲的这么多,在项目中最常用的就是ControllerBuilder.Current.SetControllerFactory(xxx);  只要大家理解了它就可以了。 

      

     

  • 相关阅读:
    详细讲解mysql 主从复制原理
    Golang语言快速上手到综合实战笔记(Go语言、Beego框架、高并发聊天室、爬虫)
    每个人都应该知道的25个Git命令
    docker 记录
    MySQL主从复制数据同步,常见问题总结
    详解mysql 主从复制原理
    算法系列15天速成——第十天 栈
    算法系列15天速成——第二天 七大经典排序【中】
    算法系列15天速成——第一天 七大经典排序【上】
    算法系列15天速成——第四天 五大经典查找【上】
  • 原文地址:https://www.cnblogs.com/majiang/p/2760505.html
Copyright © 2011-2022 走看看