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

- public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
- {
- protected virtual void ProcessRequest(HttpContext httpContext)
- {
- //使用HttpContextWrapper对HttpContext进行封装,封装的目的是为了解耦以获得可测试性.然后从RequestContext.RouteData中提取Controller名称.
- HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
- this.ProcessRequest(httpContext2);
- }
- protected internal virtual void ProcessRequest(HttpContextBase httpContext)
- {
- IController controller;
- IControllerFactory controllerFactory;
- this.ProcessRequestInit(httpContext, out controller, out controllerFactory);//获取到Controler和ControllerFactory实例,并赋值给局部变量
- try
- {
- //当前Controler对象的Action的创建与执行(执行包括:加载TempData, 创建及执行Action,处理Action返回的ActionResult ,保存TempData数据)
- controller.Execute(this.RequestContext);
- }
- finally
- {
- //释放当前Controler对象
- controllerFactory.ReleaseController(controller);
- }
- }
- }

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

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

显 然,上述的代码中这两行代码是重点,代码通过执行一个全局的变量ControllerBuilder的GetControllerFactory方法得到 一个ControllerFactory,然后再通过执行此ControllerFactory的CreateController方法得到 Controller实例!
- factory = this.ControllerBuilder.GetControllerFactory();//获取一个用于创建Controller实例的ControllerFactory.默认DefaultControllerFactory
- controller = factory.CreateController(this.RequestContext, requiredString);//根据ControllerFactory创建Controller对象。
下面就来了解下这两行代码:
1、factory = this.ControllerBuilder.GetControllerFactory();
this.ControllerBuilder是MvcHandler类的一个属性,属性返回的是MvcHandler类声明的一个 ControllerBuilder类型的字段,属性在返回时会判断当前字段是否为空,如果为空,则调用ControllerBuilder类的静态属性 Current字段,来获取一个ControllerBuilder实例。
- namespace System.Web.Mvc
- {
- public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
- {
- //省略其他成员变量
- private ControllerBuilder _controllerBuilder;
- internal ControllerBuilder ControllerBuilder
- {
- get
- {
- if (_controllerBuilder == null)
- {
- _controllerBuilder = ControllerBuilder.Current;
- }
- return _controllerBuilder;
- }
- set { _controllerBuilder = value; }
- }
- private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
- {
- //省略其他代码
- factory = ControllerBuilder.GetControllerFactory();
- }
- }
- }
- _controllerBuilder = ControllerBuilder.Current;说明ControllerBuilder是由ControllerBuilder类的静态属性Current获取的,接下来再看一下ControllerBuilder类
ControllerBuilderControllerBuilder类中有一个静态字段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接口开始介绍:
- public interface IControllerFactory
- {
- //获取Controller实例
- IController CreateController(RequestContext requestContext, string controllerName);
- //返回值为SessionStateBehavior枚举,表示请求处理过程中会话状态支持的模式
- SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
- //释放Controller实例
- void ReleaseController(IController controller);
- }
- public enum SessionStateBehavior
{
Default,使用默认 ASP.NET 逻辑来确定请求的会话状态行为。
Required,为请求启用完全的读写会话状态行为。
ReadOnly,为请求启用只读会话状态。
Disabled 禁用会话状态。
}我们可以实现此IControllerFactory接 口,再通过ControllerBuilder.Current.SetControllerBuilder()添加自定义的 ControllerBuilder;然后默认情况下,MVC使用的是DefaultControllerFactory,接下来就看看 DefaultControllerFactory中是如何实现IControllerFactory接口的。
- public class DefaultControllerFactory : IControllerFactory
- {
- //省略其他成员
- private IResolver<IControllerActivator> _activatorResolver;
- private IControllerActivator _controllerActivator;
- private ControllerBuilder _controllerBuilder;
- public DefaultControllerFactory()
- : this(null, null, null)
- {
- }
- //利用自定义的ControllerActivator时,通过在ControllerBuilder中实例化DefaultControllerFactory时出入自定义ControllerActivator为参数
- public DefaultControllerFactory(IControllerActivator controllerActivator)
- : this(controllerActivator, null, null)
- {
- }
- internal DefaultControllerFactory(IControllerActivator controllerActivator,
- IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver)
- {
- //controllerActivator类才是最终通过反射创建Controller实例的
- //重点:这里对判断controllerActivator的判断,只有当为空时才使用默认的DefaultControllerActivator
- //所以,这里又有一个扩展,通过实现IControllerActivator接口创建自定义ControllerActivator
- //在实例化DefaultControllerFactory时传入自定义ControllerActivator作为参数
- if (controllerActivator != null)
- {
- _controllerActivator = controllerActivator;
- }
- else
- {
- //此处第一个参数是个常量且为null,所以此SingleServiceResolver<>的Current属性只能返回DefaultControllerActivator实例
- _activatorResolver = activatorResolver ?? new SingleServiceResolver<IControllerActivator>(
- () => null,
- new DefaultControllerActivator(dependencyResolver),
- "DefaultControllerFactory constructor");
- }
- }
- //在ControllerBuilder中实例化DefaultControllerFactory时,赋值。(参见ControllerBuilder类的构造函数)
- internal ControllerBuilder ControllerBuilder
- {
- get { return _controllerBuilder ?? ControllerBuilder.Current; }
- set { _controllerBuilder = value; }
- }
- private IControllerActivator ControllerActivator
- {
- get
- {
- //当_controllerActivator为空时,才去_activatorResolver中获取DefaultControllerActivator实例作为ControllerActivator
- if (_controllerActivator != null)
- {
- return _controllerActivator;
- }
- _controllerActivator = _activatorResolver.Current;
- return _controllerActivator;
- }
- }
- public virtual IController CreateController(RequestContext requestContext, string controllerName)
- {
- //省略其他代码
- //根据字符串【类型名】和【上下文中的命名空间】创建Controller的类型
- Type controllerType = GetControllerType(requestContext, controllerName);
- IController controller = GetControllerInstance(requestContext, controllerType);//根据类型创建Controller实例
- return controller;
- }
- protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
- {
- //省略其他代码
- //调用ControllerActivator实例的Create通过反射创建实例。默认为DefaultControllerActivator
- return ControllerActivator.Create(requestContext, controllerType);
- }
- }
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
- ControllerBuilder controllerBulder = ControllerBuilder.Current;
- 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方法

- public class DefaultControllerFactory : IControllerFactory
- {
- //省略其他成员
- public virtual void ReleaseController(IController controller)
- {
- IDisposable disposable = controller as IDisposable;
- if (disposable != null)
- {
- disposable.Dispose();
- }
- }
- }

以上本篇博客Controller激活的全部内容!!!
对于Controller激活的相关知识中遗留的问题有:
1、Controller类型的缓存
2、会话状态行为的控制
3、DependencyResoolver类
如有兴趣的话,对于此三部分的问题可以到Artech的博客MVC系列【How ASP.NET MVC Works】Controller激活部分学习下!
Controller的激活的更多相关文章
- ASP.NET MVC Controller的激活
最近抽空看了一下ASP.NET MVC的部分源码,顺带写篇文章做个笔记以便日后查看. 在UrlRoutingModule模块中,将请求处理程序映射到了MvcHandler中,因此,说起Controll ...
- 白话学习MVC(五)Controller的激活
一.概述 在此系列开篇的时候介绍了MVC的生命周期 , 对于请求的处理,都是将相应的类的方法注册到HttpApplication事件中,通过事件的依次执行从而完成对请求的处理.对于MVC来说,请求是先 ...
- ASP.NET MVC——Controller的激活
Controller的激活是根据在路由过程得到的Controller名称来创建对应的Controller对象.相关类如图: Controller激活的过程可通过如下序列图表示: 代码示例如下: str ...
- Asp.net mvc 中的 Controller 的激活
Controller 激活是指根据路由系统解析出来的 Controller 的名称创建 控制器(Controller)的过程,这里的控制器泛指实现了 IController 接口的类型 激活过程中的核 ...
- Artech的MVC4框架学习——第三章controller的激活
第一当目标controller的名称通过URL路由被解析出来后,asp.net mvc利用 ControllerBuilder 注册 ControllerFactory ,根据名称实现对目标contr ...
- MVC Controller的激活
各Controller的继承关系 Controller最重要的是对Execute方法的调用,当目标Controller对象被激活后,对请求的后续处理和最终响应均是通过执行这个Execute方法来完成. ...
- 总体介绍ASP.NET Web API下Controller的激活与释放流程
通过<ASP.NET Web API的Controller是如何被创建的?>我们已经对HttpController激活系统的核心对象有了深刻的了解,这些对象包括用于解析程序集和有效Http ...
- ASP.NET WebAPI 09 Controller的激活
在Controller之前我们先回顾一下HttpMessageHandler通道. 在这个图中我留一个HttpContollerDispatcher没有说明.因为这个类也是继承自HttpMessage ...
- ASP.NET MVC4学习笔记之Controller的激活
一. 高层相关类说明 当路由系统根据请求Url收集路由信息后,下一步就要将路由信息传给Controller激活系统,Controller激活系统负责实现了IController接口的Controlle ...
随机推荐
- oracle_job 清空冗余数据 ,每一分钟执行一次
参照这个例子:http://cherryqq.iteye.com/blog/855022 思路: data表中有4条数据 ,relation有3条数据,通过data_id 对应,需要定时删除 data ...
- oracle_根据表名拼装语句
1.-----批量删除用户下所有表数据------保留表结构 eg: 批量删除用户下的所有表数据 SELECT 'TRUNCATE TALBE '||TABLE_NAME||';' FROM USER ...
- spring data jpa使用懒操作
如果model对象的某属性使用lazy load,调用这个属性时会报错, failed to lazily initialize a collection of role could not init ...
- UVa 740 - Baudot Data Communication Code
称号:目前编码,他们shift键被按下,并提出,对应的两个编码,其中,2相应的编码shift操作. 给你适当的编码值.寻求相应的字符串. 分析:模拟.字符串. 简单题,标记shift的升降分类处理就可 ...
- 编写一个程序,将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 ...
- 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 = ...
- ASP.NET 5:依赖注入
ASP.NET 5:依赖注入 1.背景 如果某个具体的(或类)对象被客户程序所依赖,通常把它们抽象成抽象类或接口.简单说,客户程序摆脱所依赖的具体类型,称之为面向接口编程. 那么问题来了?如何选择客户 ...
- 《自己动手写CPU》写书评获赠书活动结果
<自己动手写CPU>写书评获赠图书的读者有: 京东:8***2.16号哨兵.magicyu.kk6803.jddickyd.杰出的胡兵 亚马逊:徐贺.马先童.jaychen.farmfar ...
- centos下mysql 最新版最终成功安装!备份一下几个关键地方
我本来仅仅是为了搭建简单的LAMP环境,亲自己主动手,却发现有这么多的问题会发生.(by default7#zbphp.com) 非常多地方给的安装Mysql的提示是通过yum一键安装.shell命令 ...
- APlayer组件自制播放器
.NET中使用APlayer组件自制播放器 2015-02-02 09:46 by xiaozhi_5638, 402 阅读, 9 评论, 收藏, 编辑 目录 说明 APlayer介绍 APlayer ...