IController控制器的创建过程
.NET/ASP.NET MVC Controller 控制器(二:IController控制器的创建过程)
阅读目录:
- 1.开篇介绍
- 2.ASP.NETMVC IControllerFactory 控制器工厂接口
- 3.ASP.NETMVC DefaultControllerFactory 默认控制器工厂
- 4.ASP.NETMVC ControllerBuilder 控制器创建入口设置
- 5.ASP.NETMVC 自定义IControllerFactory
1】开篇介绍
上一篇文章“.NET/ASP.NET MVC Controller 控制器(一:深入解析控制器运行原理)”详细的讲解了MvcHandler对象内部的基本流程逻辑,这基本的流程逻辑为我们后面的学习起到铺垫作用,当我们能正确的搞懂它的内部执行流程后,我们就可以顺藤摸瓜的去挖掘每个逻辑环节中的详细逻辑;
通过前面两篇文章的介绍,我们基本上能搞清楚一个Url请求是如何借助于UrlRoutingModule模块顺利穿过ASP.NET基础框架到达应用框架的过程,当UrlRoutingModule处理过后将RouteData对象封装在RequestContext请求上下文中传入到MvcHandler对象,然后MvcHandler对象通过IControllerFactory接口根据从RouteData中获取到controllername控制器名称字符串创建具体的IController对象实例;
这基本的流程我们是清晰了,但是我们并不太清楚IControllerFactory背后所发生的一切,到底谁作为IControllerFactory默认实现的,它又有着怎样的扩展入口让我们来扩展创建过程,这值得一探究竟;
那么这篇文章让我们来分析一下IControllerFactory的背后所发生的事情,我们是否能从中学到什么设计思想;
2】ASP.NETMVC IControllerFactory 控制器工厂接口
既然能将ControllerFactory提取出接口来,那么对于IController的创建将是一个非常宽松的过程;简单的设想一下,如果不将Factory提出接口来,那么对于IController的创建将是一个很直观的过程,但是ASP.NETMVC将IController创建不是简单的使用一个ControllerFactory来解决,而是将这个创建过程设计的很松散,目的是为了扩展性方便,换句话说我们完全可以自定义一个Factroy来替代这个创建过程,也可以基于系统内部的Factroy来扩展一下;
MvcHandler使用IControllerFactroy创建出相应IController对象,那么首先我们需要搞清楚MvcHandler通过什么方式获取到实现IControllerFactory接口的;
其实在MvcHandler中并不是直接使用IControllerFactroy的相关实现,而是使用了ControllerBuilder对象,这个对象是一个单例模式的实现;MvcHanlder通过ControllerBuilder对象获取到一个实例,然后通过ControllerBuilder创建出IControllerFactory实现;
1 internal ControllerBuilder ControllerBuilder {
2 get {
3 if (_controllerBuilder == null) {
4 _controllerBuilder = ControllerBuilder.Current;
5 }
6 return _controllerBuilder;
7 }
8 set {
9 _controllerBuilder = value;
10 }
11 }
12
13 factory = ControllerBuilder.GetControllerFactory();
可以简单的理解为,ControllerBuilder管理着IControllerFactory的创建过程,MvcHanlder通过获取ControllerBuilder的全局实例,然后调用其方法GetControllerFactory,得到可以使用的IControllerFactory实现;
图1:
ControllerBuilder的设计很巧妙,它将IControllerFactory的实现为我们敞开了大门,我们可以通过这个入口做很多事情;
我们看一下IControllerFactroy接口的定义:
1 public interface IControllerFactory {
2 IController CreateController(RequestContext requestContext, string controllerName);
3 SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
4 void ReleaseController(IController controller);
5 }
接口中定义了三个方法,第一个方法CreateController很好理解,根据方法的第二个参数controllerName创建Controller实例;第二个方法GetControllerSessionBehavior方法是用来获取controllerName所代表的Controller的Session行为的,该行为是通过SessionStateAttribute特性表示;第三个方法ReleaseController方法是用在最后释放Controller的:
1 public virtual void ReleaseController(IController controller) {
2 IDisposable disposable = controller as IDisposable;
3 if (disposable != null) {
4 disposable.Dispose();
5 }
6 }
由于Controller继承自IDisposable接口,所以在方法内部是直接调用Dispose方法来释放资源;这里需要注意的是,Controller对IDisposable接口的实现是virtual修饰符:
1 protected virtual void Dispose(bool disposing) {
2 }
这就很方便我们通过重写此方法的方式来释放一些其他资源;
3】ASP.NETMVC DefaultControllerFactory 默认控制器工厂
在ASP.NETMVC内部有一个默认的Factroy(DefaultControllerFactroy),DefaultControllerFactroy实现了核心的创建IController代码,这为我们的扩展提供了很好的接口;
通过调用IControllerFactory接口的CreateController(RequestContext requestContext, string controllerName) 方法,将进入到DefaultControllerFactory实现中,首要任务就是要根据controllerName名称找到对应的ContorllerType,然后才能创建具体的实例;
1 object routeNamespacesObj;
2 Type match;
3 if (requestContext != null && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)) {
4 IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;
5 if (routeNamespaces != null && routeNamespaces.Any()) {
6 HashSet<string> nsHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);
7 match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsHash);
8
9 // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true"
10 if (match != null || false.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"])) {
11 // got a match or the route requested we stop looking
12 return match;
13 }
14 }
15 }
首先根据请求的路由数据RouteData,查找设置的命名空间集合,然后使用命名空间和控制器名称获取Type,如果Type!=null并且没有开启后被命名空间则直接返回Type;
3.1】Controller中的AreaRegistration命名空间
在DefaultControllerFactroy内部使用到了两组命名空间来作为查找Controller的NameSpace,第一个是我们在配置Route数据的时候设置的:
1 context.MapRoute(name: "api.order.default", url: "api/order/{controller}/{action}/{orderid}",
2 defaults: new { controller = "OrderController", action = "GetOrderOperationDatetime", orderid = "1001" },
3 namespaces: new string[] { "Api.Order" });
而第二个我们一般都不会用它的,它是作为AreaRegistration后备命名空间而存在的,是在ControllerBuilder中设置的:
1 ControllerBuilder.Current.DefaultNamespaces.Add("MvcApplication4.ApiOrder");
对后备命名空间的赋值是在AreaRegistrationContext中的MapRoute(string name, string url, object defaults, object constraints, string[] namespaces) 方法中完成的:
1 if (namespaces == null && Namespaces != null) {
2 namespaces = Namespaces.ToArray();
3 }
1 Route route = Routes.MapRoute(name, url, defaults, constraints, namespaces);
2 route.DataTokens["area"] = AreaName;
3
4 // disabling the namespace lookup fallback mechanism keeps this areas from accidentally picking up
5 // controllers belonging to other areas
6 bool useNamespaceFallback = (namespaces == null || namespaces.Length == 0);
7 route.DataTokens["UseNamespaceFallback"] = useNamespaceFallback;
8
9 return route;
由于AreaRegistration可以让我们对Controller的设计不局限于ASP.NETMVCWeb程序中,而可以将Controller独立出去进行模块化设计,所以需要提供有关Area的特殊命名空间查找方式;
4】ASP.NETMVC ControllerBuilder 控制器创建入口设置
ControllerBuilder作为Controller创建的设置入口,可以用来设置ControllerFactory替换系统默认的DefaultControllerFactory,ControllerBuilder是Controller的创建过程框架扩展入口,可以借助ControllerBuilder方便做很多设置;
1 internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver) {
2 _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
3 () => _factoryThunk(),
4 new DefaultControllerFactory { ControllerBuilder = this },
5 "ControllerBuilder.GetControllerFactory"
6 );
7 }
在ControllerBuilder的构造函数中,初始化了一个SingleServiceResolver<IControllerFactory>类型的Resolver,目的是为了对Factory实现IOC方式的获取;在代码中,实例化了一个DefaultControllerFactory类型的实例作为默认的Factory,比较重要的是将ControllerBuilder做为参数设置到了ControllerBuilder属性中,目的是为了能在后面解析Controller命名空间的时候用到;
1 public HashSet<string> DefaultNamespaces {
2 get {
3 return _namespaces;
4 }
5 }
在此我们可以设置统一的命名空间,由于我们在设置Route的时候,都需要设置namesapce字段,但是如果有很多这样的Route的时候就很麻烦,我们可以通过此方式进行统一的设置;
1 public void SetControllerFactory(IControllerFactory controllerFactory) {
2 if (controllerFactory == null) {
3 throw new ArgumentNullException("controllerFactory");
4 }
5
6 _factoryThunk = () => controllerFactory;
7 }
还有一个比较重要的就是设置自定义的ControllerFactory,在方法SetControllerFactory中,我们可以设置一个IControllerFactory类型的对象,就可以接管系统默认的DefaultControllerFactory对象,包括后面的所有的IController缓存策略;
图2:
基本上我们可以通过ControllerBuilder进入到ControllerFactroy的创建环节来,使用SetControllerFactory方法直接将我们自定义的IControllerFactroy传入即可;
5】ASP.NETMVC 自定义IControllerFactory
既然知道了ContollerBulder可以使我们更改系统默认的控制器工厂,那么我们通过怎样的方式使用现在的Factroy;大致上我们只需要继承自DefaultControllerFactory然后进行相应的扩展即可;
1 public class CustomControllerFactory : DefaultControllerFactory
2 {
3 protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
4 {
5 Console.WriteLine(string.Format("{0}is create.", controllerType.Name));
6 return base.GetControllerInstance(requestContext, controllerType);
7 }
8 }
现在假设我们需要在系统创建所有Controller的时候能记录下创建的记录信息,这样就很方便的完成了,我们只需要在系统初始化的地方进行设置:
1 ControllerBuilder.Current.SetControllerFactory(new Api.Order.CustomControllerFactory());
这样我们就接管了ControllerFactory的部分功能;
IController控制器的创建过程的更多相关文章
- .NET/ASP.NET MVC Controller 控制器(IController控制器的创建过程)
阅读目录: 1.开篇介绍 2.ASP.NETMVC IControllerFactory 控制器工厂接口 3.ASP.NETMVC DefaultControllerFactory 默认控制器工厂 4 ...
- 三、ASP.NET MVC Controller 控制器(二:IController控制器的创建过程)
阅读目录: 1.开篇介绍 2.ASP.NETMVC IControllerFactory 控制器工厂接口 3.ASP.NETMVC DefaultControllerFactory 默认控制器工厂 4 ...
- ASP.NET Web API 控制器创建过程(一)
ASP.NET Web API 控制器创建过程(一) 前言 在前面对管道.路由有了基础的了解过后,本篇将带大家一起学习一下在ASP.NET Web API中控制器的创建过程,这过程分为几个部分下面的内 ...
- ASP.NET Web API 控制器创建过程(二)
ASP.NET Web API 控制器创建过程(二) 前言 本来这篇随笔应该是在上周就该写出来发布的,由于身体跟不上节奏感冒发烧有心无力,这种天气感冒发烧生不如死,也真正的体会到了什么叫病来如山倒,病 ...
- Web APi之控制器创建过程及原理解析(八)
前言 中秋歇了歇,途中也时不时去看看有关创建控制器的原理以及解析,时间拖得比较长,实在是有点心有余而力不足,但又想着既然诺下了要写完原理一系列,还需有始有终.废话少说,直入主题. HttpContro ...
- Web APi之过滤器创建过程原理解析【一】(十)
前言 Web API的简单流程就是从请求到执行到Action并最终作出响应,但是在这个过程有一把[筛子],那就是过滤器Filter,在从请求到Action这整个流程中使用Filter来进行相应的处理从 ...
- iOS开发UI篇—控制器的创建
iOS开发UI篇—控制器的创建 说明:控制器有三种创建方式,下面一一进行说明. 一.第一种创建方式(使用代码直接创建) 1.创建一个空的IOS项目. 2.为项目添加一个控制器类. 3.直接在代理方法中 ...
- MVC5 Controller简要创建过程(1):ControllerFactory的创建
即将离职,闲来无事回顾下MVC的源码,到了Controller创建这里,由于流程有点复杂,鉴于自己记性不太好,索性就记录一下吧,方便日后参照. 首先看MvcHandler: public class ...
- SpringMVC——DispatcherServlet的IoC容器(Web应用的IoC容器的子容器)创建过程
在上一篇<Spring--Web应用中的IoC容器创建(WebApplicationContext根应用上下文的创建过程)>中说到了Web应用中的IoC容器创建过程.这一篇主要讲Sprin ...
随机推荐
- 第16章 观察者模式(Oberver Pattern)
原文 第16章 观察者模式(Oberver Pattern) 观察者模式 概述: 在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系” ——一个对象(目标对象)的状态发生改变,所有的依 ...
- js 正则学习小记之NFA引擎
原文:js 正则学习小记之NFA引擎 之前一直认为自己正则还不错,在看 次碳酸钴,Barret Lee 等大神都把正则玩的出神入化后发现我只是个战五渣. 求抱大腿,求大神调教. 之前大致有个印象,正 ...
- POJ1258 基础最小生成树
本文出自:http://blog.csdn.net/svitter 题意:给出一个数字n代表邻接矩阵的大小,随后给出邻接矩阵的值.输出最小生成树的权值. 题解: prime算法的基本解法: 1.选择一 ...
- 工作流Jpbm4.4工作流知识点总结(工作流开发宝典)
原文:工作流Jpbm4.4工作流知识点总结(工作流开发宝典) Jbpm工作流开发过程中的一些知识点总结,方便以后开发使用! 目录: 一.工作流框架的搭建 二.工作流框架的流程开发 1.管理流程定义 ① ...
- JVM截至多少线程可以创建: unable to create new native thread
最近的测试需要很长的连接server.这些数据需要达到100W长连接,试client.一个线程来保持连接.查找linuxserver创建者默认3200当多个线程.这个错误将得到"java.l ...
- C语言 ## __VA_ARGS__ 宏
在GNU C中,宏可以接受可变数目的参数,就象函数一样 可以把__VA_ARGS__看成是将...赋值给该宏 //注意这里不能在函数中调用abc() #include <stdio.h> ...
- CentOS-6.3安装配置SVN
安装说明 系统环境:CentOS-6.3 安装方式:yum install (源码安装容易产生版本兼容的问题) 安装软件:系统自动下载SVN软件 检查已安装版本 #检查是否安装了低版本的SVN [ro ...
- 深入理解JavaScript(1)
才华横溢的Stoyan Stefanov,在他写的由O’Reilly初版的新书<JavaScript Patterns>(JavaScript模式)中,我想要是为我们的读者贡献其摘要,那会 ...
- MonkeyRunner源码分析之-谁动了我的截图?
本文章的目的是通过分析monkeyrunner是如何实现截屏来作为一个例子尝试投石问路为下一篇文章做准备,往下一篇文章本人有意分析下monkeyrunner究竟是如何和目标测试机器通信的,所以最好的办 ...
- SQL点滴24—监测表的变化
原文:SQL点滴24-监测表的变化(转载) 在网上看到一篇关于监测表中的插入,更新,删除的方法,使用触发器实现的,很有价值. 地址:http://www.dbaunion.com/u/livecoac ...