Controller的激活

概述

  在此系列开篇的时候介绍了MVC的生命周期 , 对于请求的处理,都是将相应的类的方法注册到HttpApplication事件中,通过事件的依次执行从而完成对请求的处理。对于MVC来说,请求是先 经过路由系统,然后由一个MvcHandler来处理的,当请求到来时,执行此MvcHandler的ProcessRequest方法(因为已将 MvcHandler类的此方法注册到HttpApplication的事件中,所以事件的执行就触发了此方法)。详细请看之前介绍MVC生命周期的两篇博客
  下面我们就以MVC声明周期为主线,来分析下MVC源码

  1. public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
  2. {
  3. protected virtual void ProcessRequest(HttpContext httpContext)
  4. {
  5. //使用HttpContextWrapper对HttpContext进行封装,封装的目的是为了解耦以获得可测试性.然后从RequestContext.RouteData中提取Controller名称.
  6. HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
  7. this.ProcessRequest(httpContext2);
  8. }
  9.  
  10. protected internal virtual void ProcessRequest(HttpContextBase httpContext)
  11. {
  12. IController controller;
  13. IControllerFactory controllerFactory;
  14. this.ProcessRequestInit(httpContext, out controller, out controllerFactory);//获取到Controler和ControllerFactory实例,并赋值给局部变量
  15. try
  16. {
  17. //当前Controler对象的Action的创建与执行(执行包括:加载TempData, 创建及执行Action,处理Action返回的ActionResult ,保存TempData数据)
  18. controller.Execute(this.RequestContext);
  19.  
  20. }
  21. finally
  22. {
  23. //释放当前Controler对象
  24. controllerFactory.ReleaseController(controller);
  25. }
  26. }
  27. }

Controller的激活

   从上述代码中可以看出,对Controller激活的相关的操作是通过MvcHandler类的 ProcessRequestInit 方法来执行,而执行完成后,将获取到Controller和ControllerFactory实例!也就是说这 个 this.ProcessRequestInit(httpContext, out controller, out controllerFactory) 方法才是本片博客的旨在,下面就通过这个方法的内部代码来剖析下Controller的激活的机制!

  1. private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
  2. {
  3. //使用指定的 HTTP 上下文来添加版本标头。如:添加一个Http Header: HTTP/1.1 200 OK … X-AspNetMvc-Version: 2.0…
  4. this.AddVersionHeader(httpContext);
  5. this.RemoveOptionalRoutingParameters();
  6. //RequestContext.RouteData中提取Controller名称。如:Home/Index的话,就获取到Home
  7. string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
  8. factory = this.ControllerBuilder.GetControllerFactory();//获取一个用于创建Controller实例的ControllerFactory.默认DefaultControllerFactory
  9. controller = factory.CreateController(this.RequestContext, requiredString);//根据ControllerFactory创建Controller对象。
  10.  
  11. if (controller == null)
  12. {
  13. throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture,MvcResources.ControllerBuilder_FactoryReturnedNull,new object[]
  14. {
  15. factory.GetType(),
  16. requiredString
  17. }));
  18. }
  19. }
  20. 由于使用了out关键字,这个方法中的执行过程中所得到的值,即:赋值给ProcessRequest方法中声明的ControllerControllerFactory

显 然,上述的代码中这两行代码是重点,代码通过执行一个全局的变量ControllerBuilder的GetControllerFactory方法得到 一个ControllerFactory,然后再通过执行此ControllerFactory的CreateController方法得到 Controller实例!

  1. factory = this.ControllerBuilder.GetControllerFactory();//获取一个用于创建Controller实例的ControllerFactory.默认DefaultControllerFactory
  2. controller = factory.CreateController(this.RequestContext, requiredString);//根据ControllerFactory创建Controller对象。

下面就来了解下这两行代码:

1、factory = this.ControllerBuilder.GetControllerFactory();

    this.ControllerBuilder是MvcHandler类的一个属性,属性返回的是MvcHandler类声明的一个 ControllerBuilder类型的字段,属性在返回时会判断当前字段是否为空,如果为空,则调用ControllerBuilder类的静态属性 Current字段,来获取一个ControllerBuilder实例。

  1. namespace System.Web.Mvc
  2. {
  3. public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
  4. {
  5. //省略其他成员变量
  6. private ControllerBuilder _controllerBuilder;
  7. internal ControllerBuilder ControllerBuilder
  8. {
  9. get
  10. {
  11. if (_controllerBuilder == null)
  12. {
  13. _controllerBuilder = ControllerBuilder.Current;
  14. }
  15. return _controllerBuilder;
  16. }
  17. set { _controllerBuilder = value; }
  18. }
  19.  
  20. private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
  21. {
  22. //省略其他代码
  23. factory = ControllerBuilder.GetControllerFactory();
  24. }
  25. }
  26. }
  1. _controllerBuilder = ControllerBuilder.Current;说明ControllerBuilder是由ControllerBuilder类的静态属性Current获取的,接下来再看一下ControllerBuilder
ControllerBuilder

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

ControllerBuilder

   注意,当实例化SingleServiceResolver<IControllerFactory>时,只有第一个参数是变量,默认情况 下传入的是null,那么实例化的该SingleServiceResolver对象的Current属性就返回 DefaultControllerFactory;当调用ControllerBuilder的SetControllerFactroy方法时会对_factoryThunk 赋值一个自定义的ControllerFactory时,那么实例化的该SingleServiceResolver对象的Current属性就返回自定 义的ControllerFactory。所以,此处就是一个扩展点,我们可以自定义一个ControllerFactory,并通过 ControllerBuilder.Current.SetControllerFactory(...)方法来让程序使用自定的 ControllFatory。这里需要说明一下的是_factoryThunk是一个无参数、返回值类型为IControllerFactory的委托,而_factoryThunk()则是一个执行这个委托得到的返回值(null、自定义的ControllerFactory)。

我们说了:
  当参数为null时,该实例对象的Current属性返回的是DefaultControllerFactory
  当参数为自定义ControllerBuilder时,该实例对象的Current属性返回的是自定义的ControllerBuilder
由 于之前我们一直把SingleServiceResolver<IControllerFactory>当作是一个封装 ControllerBuilder的一个类,下面就来看看它到底是不是封装ControllerBuilder的,并且到底是如何根据第一个参数的不同 而决定其Current属性返回值的具体类不同的。

SingleServiceResolver

  此处,我们可以为SingleServiceResolver<T>下个定义,它就是一个筛选器,如果构造函数的第一个参数为null,则返回第二个参数传入的实例,如果不为空,则返回第一个参数传入的实例!

  以 上介绍了factory = this.ControllerBuilder.GetControllerFactory();中的ControllerBuilder如何实例化,并 如何返回一个ControllerFactory实例。有了ControllerFactory之后,那么就该调用ControllerFactory的 CreateController(RequestContext requestContext, string controllerName);方法(根据第二个参数通过反射实例化一个Controller,并返回)。

非重点补充

注:以上所说的ControllerFactory泛指实现了IControllerFactory接口的类;

2、controller = factory.CreateController(this.RequestContext, requiredString);

   此行代码,利用上一句得到一个ControllerFactory实例,再将【请求上下文】和【要激活的Controller的字符串】作为参数来调用 ControllerFactory类的CreateController方法,以此创建Controller实例,并返回!  即:通过ControllerFactory类来完成对Controller的激活,那么对于此行代码的解析就成了对ControllerFactory 类的分析!

  因为ControllerFactory泛指实现了IControllerFactory的类,所以就从IControllerFactory接口开始介绍:

  1. public interface IControllerFactory
  2. {
  3. //获取Controller实例
  4. IController CreateController(RequestContext requestContext, string controllerName);
  5. //返回值为SessionStateBehavior枚举,表示请求处理过程中会话状态支持的模式
  6. SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
  7. //释放Controller实例
  8. void ReleaseController(IController controller);
  9. }
  1. public enum SessionStateBehavior
    {
    Default,使用默认 ASP.NET 逻辑来确定请求的会话状态行为。
    Required,为请求启用完全的读写会话状态行为。
    ReadOnly,为请求启用只读会话状态。
    Disabled 禁用会话状态。
    }

  我们可以实现此IControllerFactory接 口,再通过ControllerBuilder.Current.SetControllerBuilder()添加自定义的 ControllerBuilder;然后默认情况下,MVC使用的是DefaultControllerFactory,接下来就看看 DefaultControllerFactory中是如何实现IControllerFactory接口的。

  1. public class DefaultControllerFactory : IControllerFactory
  2. {
  3. //省略其他成员
  4. private IResolver<IControllerActivator> _activatorResolver;
  5. private IControllerActivator _controllerActivator;
  6. private ControllerBuilder _controllerBuilder;
  7.  
  8. public DefaultControllerFactory()
  9. : this(null, null, null)
  10. {
  11. }
  12. //利用自定义的ControllerActivator时,通过在ControllerBuilder中实例化DefaultControllerFactory时出入自定义ControllerActivator为参数
  13. public DefaultControllerFactory(IControllerActivator controllerActivator)
  14. : this(controllerActivator, null, null)
  15. {
  16. }
  17.  
  18. internal DefaultControllerFactory(IControllerActivator controllerActivator,
  19. IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver)
  20. {
  21. //controllerActivator类才是最终通过反射创建Controller实例的
  22. //重点:这里对判断controllerActivator的判断,只有当为空时才使用默认的DefaultControllerActivator
  23. //所以,这里又有一个扩展,通过实现IControllerActivator接口创建自定义ControllerActivator
  24. //在实例化DefaultControllerFactory时传入自定义ControllerActivator作为参数
  25. if (controllerActivator != null)
  26. {
  27. _controllerActivator = controllerActivator;
  28. }
  29. else
  30. {
  31. //此处第一个参数是个常量且为null,所以此SingleServiceResolver<>的Current属性只能返回DefaultControllerActivator实例
  32. _activatorResolver = activatorResolver ?? new SingleServiceResolver<IControllerActivator>(
  33. () => null,
  34. new DefaultControllerActivator(dependencyResolver),
  35. "DefaultControllerFactory constructor");
  36. }
  37. }
  38. //在ControllerBuilder中实例化DefaultControllerFactory时,赋值。(参见ControllerBuilder类的构造函数)
  39. internal ControllerBuilder ControllerBuilder
  40. {
  41. get { return _controllerBuilder ?? ControllerBuilder.Current; }
  42. set { _controllerBuilder = value; }
  43. }
  44.  
  45. private IControllerActivator ControllerActivator
  46. {
  47. get
  48. {
  49. //当_controllerActivator为空时,才去_activatorResolver中获取DefaultControllerActivator实例作为ControllerActivator
  50. if (_controllerActivator != null)
  51. {
  52. return _controllerActivator;
  53. }
  54. _controllerActivator = _activatorResolver.Current;
  55. return _controllerActivator;
  56. }
  57. }
  58.  
  59. public virtual IController CreateController(RequestContext requestContext, string controllerName)
  60. {
  61. //省略其他代码
  62.        //根据字符串【类型名】和【上下文中的命名空间】创建Controller的类型
  63. Type controllerType = GetControllerType(requestContext, controllerName);
  64. IController controller = GetControllerInstance(requestContext, controllerType);//根据类型创建Controller实例
  65. return controller;
  66. }
  67.  
  68. protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
  69. {
  70. //省略其他代码
  71.        //调用ControllerActivator实例的Create通过反射创建实例。默认为DefaultControllerActivator
  72. return ControllerActivator.Create(requestContext, controllerType);
  73. }
  74.  
  75. }
DefaultControllerActivator

  上述代码中注释部分包含【Controller激活的步骤】和又一扩展点【创建自定ControllerActivator 】,所以略显混乱。下面我们就来用文字叙述一下这两部分:
一、根据ControllerBuilder中实例化DefaultControllerFactory时,利用的是无参数的构造函数,但是此构造函数又带着三个为null的参数执行有三个参数的构造函数,所以默认情况下私有字段_controllerActivator就是null,那么执行Current来获取ControllerActivator时,就将DefaultControllerActivator实例赋值给_controllerActivator字段,之后再调用该实例的Create方法,在方法内部利用反射创建Controller实例。
二、正如我们所知,最后是通过一个ControllerActivator的Create方法,根据参数【请求上下文】【Controller类型】来实现创建Controller实例的。所以,我们就可以通过实现IControllerActivator接口来创建自定义的ControllerActivator。创建自定义的ControllerActivator没问题,那么
如何使程序去利用自定义的ControllerActivator呢?
  答:注意DefaultControllerFactory含有三个参数的构造函数,首先会判断第一个参数controllerActivator如果不为空,则将参数赋值给私有变量_controllerActivator。当通过Current获取当前ControllerActivator时,判断 私有变量_controllerActivator是否为空,如果不为空,则直接返回。由此,我们就可以在实例化DefaultControllerFactory时为第一个参数传入自定义ControllerActivator实例,即通过实例化含一个参数DefaultControllerFactory构造函数,进而再去执行三个参数的构造函数。
又如何执行DefaultControllerFactory带一个参数的构造函数并传入自定义ControllerActivator实例作为参数呢?
   答:通过在Application_Start方法中通过ControllerBuilder的SetControllerBuilder方法的参数中 传入new DefaultControllerFactory(new MyControllerActivator())为参数。即:MyControllerActivator类是实现了 IControllerActivator接口的自定义ControllerActivator

  1. ControllerBuilder controllerBulder = ControllerBuilder.Current;
  2. controllerBulder.SetControllerFactory(new DefaultControllerFactory(new MyControllerActivator()) { ControllerBuilder = controllerBulder });

   以上就是第二句代码controller = factory.CreateController(this.RequestContext, requiredString);获取Controller实例的全部,其中又对发现了一个扩展点【自定义ControllerActivator】和如 何让程序使用自定义的ControllerActivator

 Controller的释放

  由本篇博客概述中的MvcHandler类中,可以看出在try finally块的fianlly中通过controllerFactory.ReleaseController(controller); 即:当前ControllerFactory的ReleseController方法来实现释放,我们就来看看默认的DefaultControllerFactory中的ReleseController方法

  1. public class DefaultControllerFactory : IControllerFactory
  2. {
  3. //省略其他成员
  4.  
  5. public virtual void ReleaseController(IController controller)
  6. {
  7. IDisposable disposable = controller as IDisposable;
  8. if (disposable != null)
  9. {
  10. disposable.Dispose();
  11. }
  12. }
  13. }

以上本篇博客Controller激活的全部内容!!!

 对于Controller激活的相关知识中遗留的问题有:
1、Controller类型的缓存

2、会话状态行为的控制

3、DependencyResoolver类
如有兴趣的话,对于此三部分的问题可以到Artech的博客MVC系列【How ASP.NET MVC Works】Controller激活部分学习下!

 
 
分类: MVC

Controller的激活的更多相关文章

  1. ASP.NET MVC Controller的激活

    最近抽空看了一下ASP.NET MVC的部分源码,顺带写篇文章做个笔记以便日后查看. 在UrlRoutingModule模块中,将请求处理程序映射到了MvcHandler中,因此,说起Controll ...

  2. 白话学习MVC(五)Controller的激活

    一.概述 在此系列开篇的时候介绍了MVC的生命周期 , 对于请求的处理,都是将相应的类的方法注册到HttpApplication事件中,通过事件的依次执行从而完成对请求的处理.对于MVC来说,请求是先 ...

  3. ASP.NET MVC——Controller的激活

    Controller的激活是根据在路由过程得到的Controller名称来创建对应的Controller对象.相关类如图: Controller激活的过程可通过如下序列图表示: 代码示例如下: str ...

  4. Asp.net mvc 中的 Controller 的激活

    Controller 激活是指根据路由系统解析出来的 Controller 的名称创建 控制器(Controller)的过程,这里的控制器泛指实现了 IController 接口的类型 激活过程中的核 ...

  5. Artech的MVC4框架学习——第三章controller的激活

    第一当目标controller的名称通过URL路由被解析出来后,asp.net mvc利用 ControllerBuilder 注册 ControllerFactory ,根据名称实现对目标contr ...

  6. MVC Controller的激活

    各Controller的继承关系 Controller最重要的是对Execute方法的调用,当目标Controller对象被激活后,对请求的后续处理和最终响应均是通过执行这个Execute方法来完成. ...

  7. 总体介绍ASP.NET Web API下Controller的激活与释放流程

    通过<ASP.NET Web API的Controller是如何被创建的?>我们已经对HttpController激活系统的核心对象有了深刻的了解,这些对象包括用于解析程序集和有效Http ...

  8. ASP.NET WebAPI 09 Controller的激活

    在Controller之前我们先回顾一下HttpMessageHandler通道. 在这个图中我留一个HttpContollerDispatcher没有说明.因为这个类也是继承自HttpMessage ...

  9. ASP.NET MVC4学习笔记之Controller的激活

    一. 高层相关类说明 当路由系统根据请求Url收集路由信息后,下一步就要将路由信息传给Controller激活系统,Controller激活系统负责实现了IController接口的Controlle ...

随机推荐

  1. oracle_job 清空冗余数据 ,每一分钟执行一次

    参照这个例子:http://cherryqq.iteye.com/blog/855022 思路: data表中有4条数据 ,relation有3条数据,通过data_id 对应,需要定时删除 data ...

  2. oracle_根据表名拼装语句

    1.-----批量删除用户下所有表数据------保留表结构 eg: 批量删除用户下的所有表数据 SELECT 'TRUNCATE TALBE '||TABLE_NAME||';' FROM USER ...

  3. spring data jpa使用懒操作

    如果model对象的某属性使用lazy load,调用这个属性时会报错, failed to lazily initialize a collection of role could not init ...

  4. UVa 740 - Baudot Data Communication Code

    称号:目前编码,他们shift键被按下,并提出,对应的两个编码,其中,2相应的编码shift操作. 给你适当的编码值.寻求相应的字符串. 分析:模拟.字符串. 简单题,标记shift的升降分类处理就可 ...

  5. 编写一个程序,将a.txt文件中的单词与b.txt文件中的单词交替合并到c.txt文件中,a.txt文件中的单词用回车符分隔,b.txt文件中用回车或空格进行分隔

    package sundemo2; import java.io.File; import java.io.FileReader; import java.io.FileWriter; public ...

  6. Winform: use the WebBrowser to display XML with xslt, xml, xslt 转 html 字符串

    原文:Winform: use the WebBrowser to display XML with xslt, xml, xslt 转 html 字符串 声明xml字符串: string xml = ...

  7. ASP.NET 5:依赖注入

    ASP.NET 5:依赖注入 1.背景 如果某个具体的(或类)对象被客户程序所依赖,通常把它们抽象成抽象类或接口.简单说,客户程序摆脱所依赖的具体类型,称之为面向接口编程. 那么问题来了?如何选择客户 ...

  8. 《自己动手写CPU》写书评获赠书活动结果

    <自己动手写CPU>写书评获赠图书的读者有: 京东:8***2.16号哨兵.magicyu.kk6803.jddickyd.杰出的胡兵 亚马逊:徐贺.马先童.jaychen.farmfar ...

  9. centos下mysql 最新版最终成功安装!备份一下几个关键地方

    我本来仅仅是为了搭建简单的LAMP环境,亲自己主动手,却发现有这么多的问题会发生.(by default7#zbphp.com) 非常多地方给的安装Mysql的提示是通过yum一键安装.shell命令 ...

  10. APlayer组件自制播放器

    .NET中使用APlayer组件自制播放器 2015-02-02 09:46 by xiaozhi_5638, 402 阅读, 9 评论, 收藏, 编辑 目录 说明 APlayer介绍 APlayer ...