一. 高层相关类说明

  当路由系统根据请求Url收集路由信息后,下一步就要将路由信息传给Controller激活系统,Controller激活系统负责实现了IController接口的Controller类实例化。它的相关类族体系如下图所示:

MvcHandler实现了IHttpHandler, IHttpAsyncHandler,IRequiresSessionState三个接口,其中IHttpHandler, IHttpAsyncHandler分别是HttpHandler同步与异步的实现,IRequiresSessionState是个标记接口,表示需要Session支持.

IController, IAsyncController, ControllerBase, Controller 是一个继承体系,IController是控制器接口,只定义了一个方法Execute方法表示执行入口,

IAsyncController是控制器的异步执行版本,ControllerBase是控制器基类,为控制器执行做一些初始化和环境准备工作,实现了Execute方法并在其内调用保护的抽像方法ExecuteCore.这个地方应用了Templete Method模式. Controller实现了一堆接口, 为我们编程提供方便, 定义了大量的属性和方法,具体的后面章节专门讲解.

IControllerFactory 表示的是控制器的创建工厂,其中定义了三个方法, CreateController方法创建IController的实例, GetControllerSessionBehavior方法获取Controller的会话行为,我们可以在自定义的Controller的上应用SessionStateAttribute指定会话行为。ReleaseController负责释放使用完的Controller实例。

public interface IControllerFactory
{
  IController CreateController(RequestContext requestContext, string controllerName);
  SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
  void ReleaseController(IController controller);
}

ControllerBuilder 是负责实例化IController和IControllerFactory的接口,封装具体的创建算法。提供了一个静态只读属性Current表示当前的ControllerBuilder对象。

二. MvcHandler中IController与IControllerFactory的创建与执行

1. 主体过程ProcessRequest方法,代码如下所示, 创建的过程委托给私有方法ProcessRequestInit

protected internal virtual void ProcessRequest(HttpContextBase httpContext)
{
  IController controller;
  IControllerFactory factory;
  ProcessRequestInit(httpContext, out controller, out factory);

  try
  {
    controller.Execute(RequestContext);
  }
  finally
  {
    factory.ReleaseController(controller);
  }
}

2. ProcessRequestInit方法的主要代码如下所示,我们可以看到最终的创建工作是交给了ControllerBuilder对象.

private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
  //其它代码
  // Get the controller type
  string controllerName = RequestContext.RouteData.GetRequiredString("controller");

  // Instantiate the controller and call Execute
  factory = ControllerBuilder.GetControllerFactory();
  controller = factory.CreateController(RequestContext, controllerName);
  if (controller == null)
  {
    throw new InvalidOperationException(...)
  }
}

 三.ControllerBuilder解析

 1.接口定义如下:

  public class ControllerBuilder

  {
    public ControllerBuilder();

    public static ControllerBuilder Current { get; }  //
    public HashSet<string> DefaultNamespaces { get; } //默认命名空间,用于Controller类型解析过程

    public IControllerFactory GetControllerFactory(); 
    public void SetControllerFactory(IControllerFactory controllerFactory); //设置自定义ControllerFactory
    public void SetControllerFactory(Type controllerFactoryType); //设置自定义ControllerFactory的类型, 
  }

  ControllerBuilder主要封装了IControllerFactory的创建过程,也许命名叫ControllerFactoryBuilder更合适,从接口可以看出,我们可以传入自定义

实现的IControllerFactory。

 2. 内部引用的几个主要类说明:

  

   IResolver<T> 只定义了一个泛型属性,表示获取相关类型的一个实例;

   SingleServiceResolver<TService> 顾名思义,表示单一服务类型解析,它实现在了IResolver接口, 在ControllerBuilder内部使用的是SingleServiceResolver<IControllerFactory>;

   DefaultControllerFactory 系统提供的默认的Controller创建工厂实现;

3. 内部IControllerFactory创建过程

    在ControllerBuilder实例化时,调用默认构造函数,而默认构造函数调用如下的内部构造函数,serviceResolver传值为null,

  internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)

  {
    _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
       () => _factoryThunk(),
       new DefaultControllerFactory { ControllerBuilder = this },
        "ControllerBuilder.GetControllerFactory");
  }

  _factoryTunk() 是一个返回IControllerFactory的委托(Func<IControllerFactory>),默认值是() => null; 其主要目的是当传入自定义IControllerFactory时做统一处理,

  public void SetControllerFactory(IControllerFactory controllerFactory)

  {
    _factoryThunk = () => controllerFactory;
  }

  ControllerBuilder返回IControllerFactory的方法内部实现如下:

  public IControllerFactory GetControllerFactory()
  {
    return _serviceResolver.Current;
  }

从上面的代码中我们可以看出Factory的创建过程进一步委托给了SingleServiceResolver对象,现在我们看看SingleServiceResolver究竟是怎么创建对象的

.SingleServiceResolver<TService> 类型

 SingleServiceResolver其实ASP.NET MVC许多基础类型创建所遵询的一个模式. 它的构造函数如下:

  public SingleServiceResolver(Func<TService> currentValueThunk, TService defaultValue, string callerMethodName)

  {
    //省略检查代码

    _resolverThunk = () => DependencyResolver.Current; //  DependencyResolver.Current表示系统全局的对象解析器
    _currentValueFromResolver = new Lazy<TService>(GetValueFromResolver);  //从全局的Resolver器中创建对象
    _currentValueThunk = currentValueThunk; //当前传入的创建委托
    _defaultValue = defaultValue;   //默认值
    _callerMethodName = callerMethodName;
  }

  返回实例的代码如下

  public TService Current
  {
    get { return _currentValueFromResolver.Value ?? _currentValueThunk() ?? _defaultValue; }
  }

从中我们可以看出SingleServiceResolver解析对象的过程:

  1. 首先从尝试从全局的对象解析器(DependencyResolver)中创建对象

  2. 否则尝试利用当前的功能委托来创建对象

  3. 最后返回对象的默认值

 在IControllerFactory创建中,默认情况下第1种和第2种情况都不起作用,所以返回的是DefaultControllerFactory, 现在我们终于得到了IControllerFactory实例,现在来看看它是怎么生成Controller实例的。

五.DefaultControllerFactory解析

  DefaultControllerFactory实现了IControllerFactory, 故名思义,它的主要作用就是Controller实例的创建与释放,会话模式的获取。

1. Controller类型的实例化

  Controller实例的创建实现在CreateController方法中,主要的代码如下:

  public virtual IController CreateController(RequestContext requestContext, string controllerName)

  {
    //省略其它代码
    Type controllerType = GetControllerType(requestContext, controllerName);
    IController controller = GetControllerInstance(requestContext, controllerType);
    return controller;
  }

  可以看到分成了两步走,首先查找确定Controller的类型,接着再利用类型创建其实例。下面来具体的看看相关的方法

  1.1 Controller类型的查找

  类型的查找实现在GetControllerType方法中,代码如下:

  protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)

  {

    // 省略其它代码

    //1. 首先检查在定制路由规则时指定的命名空间

    object routeNamespacesObj;

    Type match;

    if (requestContext != null && 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;
        }
      }
      }

  // 检查默认的命名空间
  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;
    }
  }

  //检查所有的命名空间,也就是只要有Controller名唯一匹配的就返回相应的Controller类型
  return GetControllerTypeWithinNamespaces(routeData.Route, controllerName, null /* namespaces */);
}

这个方法是查找Controller类型的骨架,查找是由三个层次的命名空间组成,

 1.首先从制定路由规则时指定的命名空间中查找,但一般我们指定路由规则时没有指定命名空间,这里有还有一个参数UseNamespaceFallback表示是否查找后备命名空间,这个参数默认为true.

2. 从默认的命名空间ControllerBuilder.DefaultNamespaces中查找

3. 从所有的命名空间中查找,查找唯一能匹配的Controller

在以上查找中,都会调用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);
    }
   }

   从以上的代码中我们可以看到查找工作又进一步委托给了ControllerTypeCache这个内部类型,这个类型是特意为实现快速查找Controller类型而设计的一个数据结构,在内部它把所有的Controller通过反射把数据组织成如下形式:

   controllerAName      namespace1,  Type

                 namespace2,   Type

   controllerBName     namespace1,  Type

                 namespace2,   Type

            ...

  当我们调用ControllerTypeCache.GetControllerTypes,传入controllerName, namespaces参数时,首先会通过controllerName得到匹配的namespace和Type列表,

再利用传入的namespaces参数与列表中的每个namespace进行比较,匹配则将相应的类型加入返回列表,如果传入的namespaces为null, 则直接将列表中所有的类型都加入返回列表,在GetControllerTypeWithinNamespaces方法中我们检查返回结果,如果只有一个类型,这是我们想要的结果,则直接返回,有一个以上则抛出Ambiguous异常。至此我们确定了Controller的类型,现在来看看Controller的实例化。

1.2 Controller类型的实例化

   在前面我们已经看到,Controller类型实例化是实现在GetControllerInstance方法中,代码如下:

    protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)

    {

      //省略非关键代码
      return ControllerActivator.Create(requestContext, controllerType);
    }

  直接调用了ControllerActivator来创建实例,ControllerActivator是一个类型为IControllerActivator的属性,IControllerActivator的定义如下:

   public interface IControllerActivator

  {
    IController Create(RequestContext requestContext, Type controllerType);
  }

  具体来看看ControllerActivator属性的定义,

    private IControllerActivator ControllerActivator
   {
      get
      {
        if (_controllerActivator != null)
        {
          return _controllerActivator;
        }
        _controllerActivator = _activatorResolver.Current;
        return _controllerActivator;
      }
  }

  这里_controllerActivator在DefaultControllerFactory构造函数中初始化,代表传入自定义的Controller激活器.具有最高的优先级。

_activatorResolver是IResolver<IControllerActivator>类型, 也是在构造函数中初始化,允许自定义实现IResolver<IControllerActivator>,具有第二高的优先级,

但在默认情况一下,前面两个参数都为null, _activatorResolver被实例化为SingleServiceResolver<IControllerActivator>类型。具体我们来看看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 constructor");
    }
  }

  SingleServiceResolver<T>泛型类前面已分析,这里不再赘述,从上面的代码中我们看到,默认情况下Controller实例化最终落在了DefaultControllerActivator的头上,

再来看看该类型的实现:

  private class DefaultControllerActivator : IControllerActivator

  {
    private 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(...)
    }
   }
  }

   DefaultControllerActivator构造函数允许传一个IDependencyResolver 对象,从上面的代码中我们可以看出,优先使用该对象创建Conroller实例,

如果该对象为null,则尝试使用系统默认的DependencyResolver,如果前面的两者IDependencyResolver创建失败,再使用Activator.CreateInstance实列化.

在默认情况一下,Controller实例是通过DependencyResolver.Current实列化,最终也是调用Activator.CreateInstance实例化的。

2. Controller实例的释放

   释放的实现代码很简单,即检查Controller是否实现了IDisposable 接口,如果实现该接口则调用其Dispose()方法。

  public virtual void ReleaseController(IController controller)

  {
    IDisposable disposable = controller as IDisposable;
    if (disposable != null)
    {
      disposable.Dispose();
    }
  }

3.Controller会话行为的设置

  3.1 会话行为获取

    controller会话行为的获取是通过反射得到应用在Controller上的SessionStateAttribute,具体的实现在代码在GetControllerSessionBehavior中,

    protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
    {
      if (controllerType == null)
      {
        return SessionStateBehavior.Default;
      }

      return _sessionStateCache.GetOrAdd(
        controllerType,
        type =>
        {
          var attr = type.GetCustomAttributes(typeof(SessionStateAttribute), inherit: true)
          .OfType<SessionStateAttribute>()
          .FirstOrDefault();

          return (attr != null) ? attr.Behavior : SessionStateBehavior.Default;
      });
    }

    从上面的代码中可以看出,如果没在Controller上的指定特别的Session行为,会返回SessionStateBehavior.Default,表示由IHttpHandler实现的会话标记接口来确定会话行为,MvcHandler标记了IRequiresSessionState,表示Session可读可写.

  3.2 会话行为设置

    会话行为设置是在MvcRouteHandler中,具本的代码如下:

    protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)

    {

      //设置会话行为
      requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
      return new MvcHandler(requestContext);
    }

    protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)
    {

      //省略非关键代码

      IControllerFactory controllerFactory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
      return controllerFactory.GetControllerSessionBehavior(requestContext, controllerName);
    }

六总结

    在本小节中,我们在源代码级别分析了ControllerFacotry和Controller实例的创建过程,从中可以看出ASP.NET MVC框架定义很多的扩展点,

下一节我们来看看具体Controller激活相关的扩展应用。

ASP.NET MVC4学习笔记之Controller的激活的更多相关文章

  1. ASP.NET MVC4学习笔记之Controller激活的扩展

    一. 为什么要进行扩展 在前面的分析中,我们知道默认的Controller激活系统只能实例化无参构造函数的Controller类型,但在某些情况一下,我们希望某些服务的实例能够自动注入到Control ...

  2. ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则

    ASP.NET MVC 学习笔记-7.自定义配置信息   ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...

  3. asp.net mvc4 学习笔记一(基本原理)

    做了8年的asp.net webform,用过MVVM但还没用过MVC , 虽然项目不用MVC,但是还是想了解一下,今天第二天学习,以下是学习心得. VS2012默认带有asp.net mvc3和as ...

  4. ASP.NET MVC4学习笔记路由系统概念与应用篇

    一.概念 1.路由是计算机网络中的一个技术概念,表示把数据包从一个网段转发至另一网段.ASP.NET中的路由系统作用类似,其作用是把请求Url映射到相应的"资源"上,资源可以是一段 ...

  5. ASP.NET MVC4学习笔记路由系统实现

    一.路由实现 路由系统实际是一个实现了ASP.NET IHttpModule接口的模块,通过注册HttpApplication的PostResolveRequestCache 事件对Url路由处理.总 ...

  6. ASP.NET MVC4学习笔记之总体概述

    断断续续使用ASP.NET MVC框架也有一年多了,也算积累了一些经验,唉,一直想写一些笔记好好总结一下,人太懒不想动笔,今天终于决定开始.希望自己能坚持下去. 这篇文章大体介绍ASP.NET MVC ...

  7. ASP.NET MVC5学习笔记之Controller同步执行架构分析

    在开始之前,声明一下,由于ASP.NET MVC5正式发布了,后面的分析将基于ASP.NET MVC5最新的源代码.在前面的内容我们分析了怎样根据路由信息来确定Controller的类型,并最终生成C ...

  8. ASP.NET MVC4学习笔记

    一.MVC简介

  9. ASP.NET MVC5学习笔记之Controller执行ControllerDescriptor和ActionDescriptor

    一. ControllerDescriptor说明 ControllerDescriptor是一个抽象类,它定义的接口代码如下: public abstract class ControllerDes ...

随机推荐

  1. C++primer练习14.44

    编写一个简单的桌面计算器使其处理二元运算 // 14_44.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iost ...

  2. BC水题--The Factor(质因分解)

    网址:http://acm.hdu.edu.cn/showproblem.php?pid=5428 roblem Description There is a sequence of n positi ...

  3. 根据硬件配置后mapred-site.xml和yarn-site.xml

    机器总共16G内存,sqoop导入数据时大约需要2G左右 mapred-site.xml <configuration> <property> <name>mapr ...

  4. Java-convert between INT and STRING

    int -> String 三种写法 String s = 43 + ""; String s = String.valueOf(43); String s = Intege ...

  5. Kendo UI - Class 基类定义

    在 kendo 中,使用原型继承机制,Class 是 Kendo 中的基类,定义了函数 extend 用来派生其它类. function Class() {} Class.extend = funct ...

  6. xutils 框架

    1. android快速开发框架xUtils xUtils简介 xUtils 包含了很多实用的android工具. xUtils 支持大文件上传,更全面的http请求协议支持(10种谓词),拥有更加灵 ...

  7. 开机自启动redis

    修改redis.conf,打开后台运行选项: # By default Redis does not run as a daemon. Use 'yes' if you need it. # Note ...

  8. NTP服务器地址及IP

    yum install ntp */20 * * * * /usr/sbin/ntpdate 61.172.254.29 210.72.145.44 (国家授时中心服务器IP地址)133.100.11 ...

  9. 开源安全平台Ossim 4.5系统使用入门(高清视频)

    开源安全平台Ossim 4.5系统使用入门 时长:15分钟 观看地址: http://www.tudou.com/programs/view/Yb0m4vPYlIQ/ 视频截图: 650) this. ...

  10. MFC常见问题解惑

    MFC类的分类 1 Root: CObject : CObject2 Application Architecture Classes: CWinApp/CFrameWnd/... 3 Window, ...