• 探索ASP.NET MVC框架之控制器的查找与激活机制


    引言

      前面一篇博文我们介绍了MVC框架的路由机制,我们知道一个URL请求如何从ASP.NET处理管线到达了IHttpHandler实例(MvcHandler)。今天我们从MvcHandler来进行下一步骤的分析,看看MVC框架是如何找到指定的控制器并且激活(创建)控制器实例的。

    一切从MvcHandler的ProcessRequest方法开始(获取控制器工厂实例)

      我们知道Http请求到达服务端,找到相应的IHttpHandler后,会执行ProcessRequest方法来处理请求,现在我们就来看看MvcHandler是怎么处理请求的。上源码:

     1 IController controller;
     2 IControllerFactory factory;
     3 ProcessRequestInit(httpContext, out controller, out factory);
     4 try
     5 {
     6       controller.Execute(RequestContext);
     7 }
     8 finally
     9 {
    10       factory.ReleaseController(controller);
    11 }

      从这段代码我们看到,在获取控制器实例时,是通过控制器工厂来创建的,下面我们来看一下ProcessRequestInit方法。看源代码:

     1 private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
     2         {
     3             // If request validation has already been enabled, make it lazy. This allows attributes like [HttpPost] (which looks
     4             // at Request.Form) to work correctly without triggering full validation.
     5             // Tolerate null HttpContext for testing.
     6             HttpContext currentContext = HttpContext.Current;
     7             if (currentContext != null)
     8             {
     9                 bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(currentContext);
    10                 if (isRequestValidationEnabled == true)
    11                 {
    12                     ValidationUtility.EnableDynamicValidation(currentContext);
    13                 }
    14             }
    15 
    16             AddVersionHeader(httpContext);
    17             RemoveOptionalRoutingParameters();
    18 
    19             // Get the controller type
    20             string controllerName = RequestContext.RouteData.GetRequiredString("controller");
    21 
    22             // Instantiate the controller and call Execute
    23             factory = ControllerBuilder.GetControllerFactory();
    24             controller = factory.CreateController(RequestContext, controllerName);
    25             if (controller == null)
    26             {
    27                 throw new InvalidOperationException(
    28                     String.Format(
    29                         CultureInfo.CurrentCulture,
    30                         MvcResources.ControllerBuilder_FactoryReturnedNull,
    31                         factory.GetType(),
    32                         controllerName));
    33             }
    34         }

      我们看这个方法从20行开始,该方法先获取控制器的名称,然后获取控制器工厂实例(代码23行)。然后使用控制器工厂实例来创建控制器实例。下面我们就来看一下ControllerBuilder来创建控制器工厂实例的过程。

      下面我们来看一下GetControllerFactory方法的内部实现,源码很简单,就一行代码:

    1 public IControllerFactory GetControllerFactory()
    2 {
    3     return _serviceResolver.Current;
    4 }

      很明显,这是读取了_serviceResolver实例的Current属性值,那么我们需要仔细查看下_serviceResolver实例的创建及其Current属性的赋值,通过看源代码,我们看到了以下代码:

     1         private static ControllerBuilder _instance = new ControllerBuilder();
     2         private Func<IControllerFactory> _factoryThunk = () => null;
     3         private HashSet<string> _namespaces = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
     4         private IResolver<IControllerFactory> _serviceResolver;
     5 
     6         public ControllerBuilder() : this(null) { }
     7 
     8         internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
     9         {
    10             _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
    11                                                       () => _factoryThunk(),
    12                                                       new DefaultControllerFactory { ControllerBuilder = this },
    13                                                       "ControllerBuilder.GetControllerFactory");
    14         }
    15 
    16         [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Calling method multiple times might return different objects.")]
    17         public IControllerFactory GetControllerFactory()
    18         {
    19             return _serviceResolver.Current;
    20         }    

      我们看到ControllerBuilder类中有一个静态字段 _instance,静态字段是在程序执行前加载的,那么由代码可知,就需要执行该类的构造函数,即执行public ControllerBuilder(): this(null),而其后的: this(null)则表示要去执执行带一个参数的构造函数 ControllerBuilder(IResolver<IControllerFactory> serviceResolver),这个构造函数中首先判断传入的参数是否为空,如果为空的话就实例化一个 SingleServiceResolver<IControllerFactory>对象(暂且理解为封装 ControllerFactory的一个类),并赋值给私有变量_serviceResolver,通过调用_serviceResolver的 Current属性来获取当前封装的ControllerFactory实例。

      通过上面的分析,我们知道了大致的流程,下面我们深入SingleServiceResolve内部看看其实例的Current到底是怎么计算的。默认情况下_factoryThunk委托传递的值是null。看源码:

     1 internal class SingleServiceResolver<TService> : IResolver<TService>
     2         where TService : class
     3     {
     4         private Lazy<TService> _currentValueFromResolver;
     5         private Func<TService> _currentValueThunk;
     6         private TService _defaultValue;
     7         private Func<IDependencyResolver> _resolverThunk;
     8         private string _callerMethodName;
     9 
    10         public SingleServiceResolver(Func<TService> currentValueThunk, TService defaultValue, string callerMethodName)
    11         {
    12             if (currentValueThunk == null)
    13             {
    14                 throw new ArgumentNullException("currentValueThunk");
    15             }
    16             if (defaultValue == null)
    17             {
    18                 throw new ArgumentNullException("defaultValue");
    19             }
    20             //默认情况下是DefaultDependencyResolver
    21             _resolverThunk = () => DependencyResolver.Current;
    22             _currentValueFromResolver = new Lazy<TService>(GetValueFromResolver);
    23             _currentValueThunk = currentValueThunk;
    24             _defaultValue = defaultValue;
    25             _callerMethodName = callerMethodName;
    26         }
    27 
    28         internal SingleServiceResolver(Func<TService> staticAccessor, TService defaultValue, IDependencyResolver resolver, string callerMethodName)
    29             : this(staticAccessor, defaultValue, callerMethodName)
    30         {
    31             if (resolver != null)
    32             {
    33                 _resolverThunk = () => resolver;
    34             }
    35         }
    36 
    37         public TService Current
    38         {
    39             get { return _currentValueFromResolver.Value ?? _currentValueThunk() ?? _defaultValue; }
    40         }
    41 
    42         private TService GetValueFromResolver()
    43         {
    44             TService result = _resolverThunk().GetService<TService>();
    45 
    46             if (result != null && _currentValueThunk() != null)
    47             {
    48                 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.SingleServiceResolver_CannotRegisterTwoInstances, typeof(TService).Name.ToString(), _callerMethodName));
    49             }
    50 
    51             return result;
    52         }
    53     }

      我们看到Current属性的求值涉及到3个过程,看代码的红色部分。下面我们来看这3个步骤。

      第一个步骤我们看_currentValueFromResolver.Value的值。我们知道_currentValueFromResolver绑定了GetValueFromResolver方法。该方法内部使用了resolverThunk委托,该委托返回DependencyResolver.Current。我在以上的代码中已经进行了注释。DependencyResolver.Current实际使用的是DefaultDependencyResolver类。使用DefaultDependencyResolver类的GetService方法。我们深入该方法内部发现其逻辑是这样的:

     1 public object GetService(Type serviceType)
     2             {
     3                 // Since attempting to create an instance of an interface or an abstract type results in an exception, immediately return null
     4                 // to improve performance and the debugging experience with first-chance exceptions enabled.
     5                 if (serviceType.IsInterface || serviceType.IsAbstract)
     6                 {
     7                     return null;
     8                 }
     9 
    10                 try
    11                 {
    12                     return Activator.CreateInstance(serviceType);
    13                 }
    14                 catch
    15                 {
    16                     return null;
    17                 }
    18             }

      我们看到,如果传递的Type类型是接口或者抽象类的话就返回null。很明显我们传递的serviceType是一个接口。第一步骤返回时null。接下来我们继续分析第二个步骤。

       第二个步骤我们看到调用的是_currentValueThunk委托。这个委托默认值是返回null的。当然我们在自定义控制器工厂的时候其实就是重新设置这个委托值。这是MVC框架的一个扩展点。我们来看一下设置自定义控制器工厂内部发生的事,看源代码:

    1 public void SetControllerFactory(IControllerFactory controllerFactory)
    2         {
    3             if (controllerFactory == null)
    4             {
    5                 throw new ArgumentNullException("controllerFactory");
    6             }
    7 
    8             _factoryThunk = () => controllerFactory;
    9         }

      看到了把,通过SetControllerFactory方法,我们相当于让_factoryThunk委托返回我们自定义的控制器工厂实例。这样在获取Current属性值的时候,第二个步骤就是调用这个委托返回控制器工厂实例,只是默认值是null而已。如果我们设置了自定义的值,就会返回自定义控制器工厂值。

      第三个步骤我们看到返回的是_defaultValue的值。很明显MVC框架默认值是DefaultControllerFactory。所以MVC框架默认的控制器工厂是DefaultControllerFactory。到此为止我们已经分析出该开始ProcessRequest中获取的控制器工厂实例就是DefaultControllerFactory的实例了。我们继续往下看。

    激活(创建)控制器实例

      通过上面的分析我们知道控制器工厂是DefaultControllerFactory,下面我们深入其内部看看控制器实例到底是怎么创建出来的。通过最开始部分代码的逻辑(即ProcessRequest方法),我们知道创建控制器的方法是CreateController方法,那么我们就从这个方法入手。

     1 public virtual IController CreateController(RequestContext requestContext, string controllerName)
     2         {
     3             if (requestContext == null)
     4             {
     5                 throw new ArgumentNullException("requestContext");
     6             }
     7             if (String.IsNullOrEmpty(controllerName))
     8             {
     9                 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
    10             }
    11             Type controllerType = GetControllerType(requestContext, controllerName);
    12             IController controller = GetControllerInstance(requestContext, controllerType);
    13             return controller;
    14         }

      我们看到整个过程主要分成两个部分。第一步:获取控制器类型。第二步:根据控制器类型获取控制器实例。下面我们就分两步来介绍这个过程。

      第一步获取控制器类型:

     1 protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)
     2         {
     3             if (String.IsNullOrEmpty(controllerName))
     4             {
     5                 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
     6             }
     7 
     8             // first search in the current route's namespace collection
     9             object routeNamespacesObj;
    10             Type match;
    11             if (requestContext != null && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj))
    12             {
    13                 IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;
    14                 if (routeNamespaces != null && routeNamespaces.Any())
    15                 {
    16                     HashSet<string> namespaceHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);
    17                     match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, namespaceHash);
    18 
    19                     // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true"
    20                     if (match != null || false.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"]))
    21                     {
    22                         // got a match or the route requested we stop looking
    23                         return match;
    24                     }
    25                 }
    26             }
    27 
    28             // then search in the application's default namespace collection
    29             if (ControllerBuilder.DefaultNamespaces.Count > 0)
    30             {
    31                 HashSet<string> namespaceDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
    32                 match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, namespaceDefaults);
    33                 if (match != null)
    34                 {
    35                     return match;
    36                 }
    37             }
    38 
    39             // if all else fails, search every namespace
    40             return GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null /* namespaces */);
    41         }

      我们看到这段代码的结构很清晰。第一步先在当前路由设置的命名空间中寻找符合条件的控制器。如果找不到就从工程中设置的命名空间中寻找。如果还是找不到就一个一个命名空间的遍历寻找。

      第二步根据控制器类型创建其实例

      我们接下来看第二个步骤,创建控制器实例,逻辑中也是封装在另一个方法中的(GetControllerInstance)。下面我们来看看其内部实现。

     1 /// <summary>
     2         /// 通过控制器类型来创建控制器实例
     3         /// </summary>
     4         /// <param name="requestContext"></param>
     5         /// <param name="controllerType"></param>
     6         /// <returns></returns>
     7         protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
     8         {
     9             if (controllerType == null)
    10             {
    11                 throw new HttpException(404,
    12                                         String.Format(
    13                                             CultureInfo.CurrentCulture,
    14                                             MvcResources.DefaultControllerFactory_NoControllerFound,
    15                                             requestContext.HttpContext.Request.Path));
    16             }
    17             if (!typeof(IController).IsAssignableFrom(controllerType))
    18             {
    19                 throw new ArgumentException(
    20                     String.Format(
    21                         CultureInfo.CurrentCulture,
    22                         MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,
    23                         controllerType),
    24                     "controllerType");
    25             }
    26             return ControllerActivator.Create(requestContext, controllerType);
    27         }

      我们看到创建控制器本质上是交给控制器激活器来创建的。看来我们需要好好看看这个控制器激活器是如何工作的了。首先我们应该先找到ControllerActivator的值。我们看到了如下代码:

     1 private IControllerActivator ControllerActivator
     2         {
     3             get
     4             {
     5                 if (_controllerActivator != null)
     6                 {
     7                     return _controllerActivator;
     8                 }
     9                 _controllerActivator = _activatorResolver.Current;
    10                 return _controllerActivator;
    11             }
    12         }

      我们接着往下看:

     1  public DefaultControllerFactory()
     2             : this(null, null, null)
     3         {
     4         }
     5 
     6         public DefaultControllerFactory(IControllerActivator controllerActivator)
     7             : this(controllerActivator, null, null)
     8         {
     9         }
    10 
    11         internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver)
    12         {
    13             //如果不存在自定义的控制器激活器,就使用默认的DefaultControllerActivator控制器激活器
    14             if (controllerActivator != null)
    15             {
    16                 _controllerActivator = controllerActivator;
    17             }
    18             else
    19             {
    20                 _activatorResolver = activatorResolver ?? new SingleServiceResolver<IControllerActivator>(
    21                                                               () => null,
    22                                                               new DefaultControllerActivator(dependencyResolver),
    23                                                               "DefaultControllerFactory constructor");
    24             }
    25         }

      我们在这里又看到了熟悉的代码(红色部分),相信大家对于SingleServiceResolver泛型类比较熟悉了。我们看到MVC框架默认的控制器激活器是DefaultControllerActivator。我们接下来来看DefaultControllerActivator类内部逻辑。

     1 /// <summary>
     2         /// MVC框架默认的自定义控制器激活器
     3         /// </summary>
     4         private class DefaultControllerActivator : IControllerActivator
     5         {
     6             private Func<IDependencyResolver> _resolverThunk;
     7 
     8             public DefaultControllerActivator()
     9                 : this(null)
    10             {
    11             }
    12 
    13             public DefaultControllerActivator(IDependencyResolver resolver)
    14             {
    15                 if (resolver == null)
    16                 {
    17                     //DependencyResolver.Current默认是DefaultDependencyResolver
    18                     _resolverThunk = () => DependencyResolver.Current;
    19                 }
    20                 else
    21                 {
    22                     _resolverThunk = () => resolver;
    23                 }
    24             }
    25 
    26             public IController Create(RequestContext requestContext, Type controllerType)
    27             {
    28                 try
    29                 {
    30                     return (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
    31                 }
    32                 catch (Exception ex)
    33                 {
    34                     throw new InvalidOperationException(
    35                         String.Format(
    36                             CultureInfo.CurrentCulture,
    37                             MvcResources.DefaultControllerFactory_ErrorCreatingController,
    38                             controllerType),
    39                         ex);
    40                 }
    41             }
    42         }

      我们来看激活器的Create方法,控制器的实例就是由这个方法来创建。我们看到控制器的实例就是根据控制器类型来创建的。到此为止,我们终于知道了如何激活控制器实例。下面一篇文章我们还会沿着请求的步骤继续往下探索。

  • 相关阅读:
    转载:[Oracle]杀死正在执行的sql语句
    转载:记录一次MySQL两千万数据的大表优化解决过程
    转载:logback日志详解
    转载:MySQL千万级大表优化攻略
    使用dbUnit的 IDataSet 因乱序造成assert失败而采取的措施
    解锁用户scott并授权
    两表连接各种Join图示,SQL及查询结果
    一句Delete..In.. 删除语句的优化
    大数据技术之_08_Hive学习_02_DDL数据定义(创建/查询/修改/删除数据库+创建表+分区表+修改表+删除表)+DML数据操作(数据导入+数据导出+清除表中数据)
    大数据技术之_08_Hive学习_01_Hive入门+Hive安装、配置和使用+Hive数据类型
  • 原文地址:https://www.cnblogs.com/dreamGong/p/5159415.html
Copyright © 2020-2023  润新知