引言

  前面一篇博文我们介绍了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方法。看源代码:

 private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
// If request validation has already been enabled, make it lazy. This allows attributes like [HttpPost] (which looks
// at Request.Form) to work correctly without triggering full validation.
// Tolerate null HttpContext for testing.
HttpContext currentContext = HttpContext.Current;
if (currentContext != null)
{
bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(currentContext);
if (isRequestValidationEnabled == true)
{
ValidationUtility.EnableDynamicValidation(currentContext);
}
} AddVersionHeader(httpContext);
RemoveOptionalRoutingParameters(); // 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(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.ControllerBuilder_FactoryReturnedNull,
factory.GetType(),
controllerName));
}
}

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

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

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

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

         private static ControllerBuilder _instance = new ControllerBuilder();
private Func<IControllerFactory> _factoryThunk = () => null;
private HashSet<string> _namespaces = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private IResolver<IControllerFactory> _serviceResolver; public ControllerBuilder() : this(null) { } internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
{
_serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
() => _factoryThunk(),
new DefaultControllerFactory { ControllerBuilder = this },
"ControllerBuilder.GetControllerFactory");
} [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Calling method multiple times might return different objects.")]
public IControllerFactory GetControllerFactory()
{
return _serviceResolver.Current;
}

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

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

 internal class SingleServiceResolver<TService> : IResolver<TService>
where TService : class
{
private Lazy<TService> _currentValueFromResolver;
private Func<TService> _currentValueThunk;
private TService _defaultValue;
private Func<IDependencyResolver> _resolverThunk;
private string _callerMethodName; public SingleServiceResolver(Func<TService> currentValueThunk, TService defaultValue, string callerMethodName)
{
if (currentValueThunk == null)
{
throw new ArgumentNullException("currentValueThunk");
}
if (defaultValue == null)
{
throw new ArgumentNullException("defaultValue");
}
//默认情况下是DefaultDependencyResolver
_resolverThunk = () => DependencyResolver.Current;
_currentValueFromResolver = new Lazy<TService>(GetValueFromResolver);
_currentValueThunk = currentValueThunk;
_defaultValue = defaultValue;
_callerMethodName = callerMethodName;
} internal SingleServiceResolver(Func<TService> staticAccessor, TService defaultValue, IDependencyResolver resolver, string callerMethodName)
: this(staticAccessor, defaultValue, callerMethodName)
{
if (resolver != null)
{
_resolverThunk = () => resolver;
}
} public TService Current
38 {
39 get { return _currentValueFromResolver.Value ?? _currentValueThunk() ?? _defaultValue; }
40 } private TService GetValueFromResolver()
{
TService result = _resolverThunk().GetService<TService>(); if (result != null && _currentValueThunk() != null)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.SingleServiceResolver_CannotRegisterTwoInstances, typeof(TService).Name.ToString(), _callerMethodName));
} return result;
}
}

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

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

 public object GetService(Type serviceType)
{
// Since attempting to create an instance of an interface or an abstract type results in an exception, immediately return null
// to improve performance and the debugging experience with first-chance exceptions enabled.
if (serviceType.IsInterface || serviceType.IsAbstract)
{
return null;
} try
{
return Activator.CreateInstance(serviceType);
}
catch
{
return null;
}
}

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

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

 public void SetControllerFactory(IControllerFactory controllerFactory)
{
if (controllerFactory == null)
{
throw new ArgumentNullException("controllerFactory");
} _factoryThunk = () => controllerFactory;
}

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

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

激活(创建)控制器实例

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

 public virtual IController CreateController(RequestContext requestContext, string controllerName)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (String.IsNullOrEmpty(controllerName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
Type controllerType = GetControllerType(requestContext, controllerName);
IController controller = GetControllerInstance(requestContext, controllerType);
return controller;
}

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

  第一步获取控制器类型:

 protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)
{
if (String.IsNullOrEmpty(controllerName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
} // first search in the current route's namespace collection
object routeNamespacesObj;
Type match;
if (requestContext != null && requestContext.RouteData.DataTokens.TryGetValue("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(requestContext.RouteData.Route, controllerName, namespaceHash); // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true"
if (match != null || false.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"]))
{
// got a match or the route requested we stop looking
return match;
}
}
} // then search in the application's default namespace collection
if (ControllerBuilder.DefaultNamespaces.Count > )
{
HashSet<string> namespaceDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, namespaceDefaults);
if (match != null)
{
return match;
}
} // if all else fails, search every namespace
return GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null /* namespaces */);
}

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

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

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

 /// <summary>
/// 通过控制器类型来创建控制器实例
/// </summary>
/// <param name="requestContext"></param>
/// <param name="controllerType"></param>
/// <returns></returns>
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(,
String.Format(
CultureInfo.CurrentCulture,
MvcResources.DefaultControllerFactory_NoControllerFound,
requestContext.HttpContext.Request.Path));
}
if (!typeof(IController).IsAssignableFrom(controllerType))
{
throw new ArgumentException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,
controllerType),
"controllerType");
}
return ControllerActivator.Create(requestContext, controllerType);
}

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

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

  我们接着往下看:

  public DefaultControllerFactory()
: this(null, null, null)
{
} public DefaultControllerFactory(IControllerActivator controllerActivator)
: this(controllerActivator, null, null)
{
} internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver)
{
//如果不存在自定义的控制器激活器,就使用默认的DefaultControllerActivator控制器激活器
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 }
}

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

 /// <summary>
/// MVC框架默认的自定义控制器激活器
/// </summary>
private class DefaultControllerActivator : IControllerActivator
{
private Func<IDependencyResolver> _resolverThunk; public DefaultControllerActivator()
: this(null)
{
} public DefaultControllerActivator(IDependencyResolver resolver)
{
if (resolver == null)
{
//DependencyResolver.Current默认是DefaultDependencyResolver
_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(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.DefaultControllerFactory_ErrorCreatingController,
controllerType),
ex);
}
}
}

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

探索ASP.NET MVC框架之控制器的查找与激活机制的更多相关文章

  1. asp.net MVC 框架中控制器里使用Newtonsoft.Json对前端传过来的字符串进行解析

    下面我用一个实例来和大家分享一下我的经验,asp.net MVC 框架中控制器里使用Newtonsoft.Json对前端传过来的字符串进行解析. using Newtonsoft.Json; usin ...

  2. 探索ASP.NET MVC框架之路由系统

    引言 对于ASP.NET MVC的路由系统相信大家肯定不陌生.今天我们就深入ASP.NET的框架内部来看一下路由系统到底是怎么通过我们给出的地址(例如:/Home/Index)解析出Controlle ...

  3. ASP.NET MVC学习之控制器篇(二)

    原文链接:http://www.asp.net/learn/mvc/ 这篇教程探索了ASP.NET MVC控制器(controller).控制器动作(controller action)和动作结果(a ...

  4. 写自己的ASP.NET MVC框架(上)

    http://www.cnblogs.com/fish-li/archive/2012/02/12/2348395.html 阅读目录 开始 ASP.NET程序的几种开发方式 介绍我的MVC框架 我的 ...

  5. 学习“迷你ASP.NET MVC框架”后的小结

    看蒋老师MVC的书第二个大收获可以是算是看了这个迷你ASP.NET MVC框架了,虽然它远不如真正ASP.NET MVC(下文简称“MVC”)那么复杂庞大,但在迷你版中绕来绕去也够呛的.这部分我看了几 ...

  6. BrnShop开源网上商城第二讲:ASP.NET MVC框架

    在团队设计BrnShop的web项目之初,我们碰到了两个问题,第一个是数据的复用和传递,第二个是大mvc框架和小mvc框架的选择.下面我依次来说明下. 首先是数据的复用和传递:对于BrnShop的每一 ...

  7. ASP.NET MVC框架开发系列课程 (webcast视频下载)

    课程讲师: 赵劼 MSDN特邀讲师 赵劼(网名“老赵”.英文名“Jeffrey Zhao”,技术博客为http://jeffreyzhao.cnblogs.com),微软最有价值专家(ASP.NET ...

  8. 【ASP.NET MVC系列】浅谈ASP.NET MVC 视图与控制器传递数据

    ASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作 ...

  9. ASP.NET MVC 5 04 - 控制器

    PS: 唉.本来这一篇前几天早就应该发了的,可是谁每月没有那么几天啊... 呵呵.开个玩笑.反正就是各种烦气,所以也就一直没上来继续发了. 年底了,摆正一下心态吧.好好干,整点钱,过年回家能跟亲朋好友 ...

随机推荐

  1. OSI七层模型

    OSI 七层模型通过七个层次化的结构模型使不同的系统不同的网络之间实现可靠的通讯,因此其最主要的功能就是帮助不同类型的主机实现数据传输 . 完成中继功能的节点通常称为中继系统.在OSI七层模型中,处于 ...

  2. 学习和研究下unity3d的四元数 Quaternion

    学习和研究下unity3d的四元数 Quaternion 今天准备学习和研究下unity3d的四元数 Quaternion 四元数在电脑图形学中用于表示物体的旋转,在unity中由x,y,z,w 表示 ...

  3. 分页ajax+springmvc的简单实现

    页面部分源码: <li class="paginItem"><a href="javascript:getNewsList(2);">2 ...

  4. Windows下Nginx Virtual Host多站点配置详解

    Windows下Nginx Virtual Host多站点配置详解 此教程适用于Windows系统已经配置好Nginx+Php+Mysql环境的同学. 如果您还未搭建WNMP环境,请查看 window ...

  5. 搭建TFS 2015 Build Agent环境(二)

    在执行和安装配置的过程中,注意一定要使用管理员权限运行:ConfigureAgent.cmd 和RunAgent.cmd.配置的过程中,要注意几个内容:1.TFS地址不要写DefaultCollect ...

  6. Swagger+AutoRest 生成web api客户端(.Net)

    简介 对于.net来说,用web api来构建服务是一个不错的选择,都是http请求,调用简单,但是如果真的要在程序中调用,则还有些工作要做,比如我们需要手写httpClient调用,并映射Model ...

  7. 微信小程序之小豆瓣图书

    最近微信小程序被炒得很火热,本人也抱着试一试的态度下载了微信web开发者工具,开发工具比较简洁,功能相对比较少,个性化设置也没有.了解完开发工具之后,顺便看了一下小程序的官方开发文档,大概了解了小程序 ...

  8. Binding

    Binding基础  绑定某个对象的属性值到控制上,写法如下: public class Order : INotifyPropertyChanged//只要实现此接口 { public event  ...

  9. 【MVVM】模型认识理解,

     MVVM:模型-视图-视图模型(Model-View-ViewModel) 注意:它是双向绑定的 源:http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mv ...

  10. C#属性-索引器-里氏替换-多态-虚方法-抽象-接口-泛型-

    1.属性 //属性的2种写法 public class person { private string _name; public string Name { get { return _name; ...