上一篇, 出现了一个至关重要的类:MvcHandler, 接下来就来看一下MvcHandler吧. 先不看具体方法, 先看一下类里面的情况.

//这里实现了两个重要的接口, 异步处理和同步处理的接口
public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
{
// Fields
private ControllerBuilder _controllerBuilder;
private static readonly object _processRequestTag;
internal static readonly string MvcVersion;
public static readonly string MvcVersionHeaderName; // Methods
static MvcHandler();
public MvcHandler(RequestContext requestContext);
protected internal virtual void AddVersionHeader(HttpContextBase httpContext);
protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state);
protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state);
protected internal virtual void EndProcessRequest(IAsyncResult asyncResult);
private static string GetMvcVersionString();
protected virtual void ProcessRequest(HttpContext httpContext);
protected internal virtual void ProcessRequest(HttpContextBase httpContext);
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory);
private void RemoveOptionalRoutingParameters();
IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData);
void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result);
void IHttpHandler.ProcessRequest(HttpContext httpContext); // Properties
internal ControllerBuilder ControllerBuilder { get; set; }
public static bool DisableMvcResponseHeader { get; set; }
protected virtual bool IsReusable { get; }
public RequestContext RequestContext { get; private set; }
bool IHttpHandler.IsReusable { get; }
}

从上面看, 有两种执行方式, 一种是同步的, 一种是异步的. 那默认情况下, 其实会走异步的方式. 但是这里呢, 我想用同步的方式去分析, 其实过程原理都是一样的, 只是方式不同.

一、解析

注意到这个类, 实现了三个接口, 那第三个接口是干啥的呢? 先看一下这个接口的内容.

public interface IRequiresSessionState
{
}

点进去, 发现这个接口没有任何方法, 那么他是干什么的呢?

其实他是一种标志, 或者叫标记, 表示这个类有对Session的访问权限, 包括读写. 如果你想自定义Http处理程序, 又想操作Session的话, 记得实现这个接口.

接下来, 回归正题了.  来看一下这里的 PR 方法.

protected virtual void ProcessRequest(HttpContext httpContext)
{
HttpContextBase base2 = new HttpContextWrapper(httpContext);
this.ProcessRequest(base2);
} protected internal virtual void ProcessRequest(HttpContextBase httpContext)
{
IController controller;
IControllerFactory factory;
this.ProcessRequestInit(httpContext, out controller, out factory);
try
{
controller.Execute(this.RequestContext);
}
finally
{
factory.ReleaseController(controller);
}
}

这里也就只有三个方法了, 我们一个一个分析.

1. ProcessRequestInit()

//MvcHandler
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
HttpContext current = HttpContext.Current;
if ((current != null) && (ValidationUtility.IsValidationEnabled(current) == true))
{
ValidationUtility.EnableDynamicValidation(current);
}
   //添加 Http Header : HTTP/1.1 200 OK ......
this.AddVersionHeader(httpContext);
this.RemoveOptionalRoutingParameters();
   //从路由中获取控制器的名称
string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
   //获取控制器工厂
factory = this.ControllerBuilder.GetControllerFactory();
   //利用控制器工厂创建控制器类
controller = factory.CreateController(this.RequestContext, requiredString);
if (controller == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
      MvcResources.ControllerBuilder_FactoryReturnedNull, new object[] { factory.GetType(), requiredString }));
}
}

1.1 从路由中获取控制器名称 - GetRequiredString

public string GetRequiredString(string valueName)
{
object obj2;
if (this.Values.TryGetValue(valueName, out obj2))
{
string str = obj2 as string;
if (!string.IsNullOrEmpty(str))
{
return str;
}
}
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture,
    SR.GetString("RouteData_RequiredValue"), new object[] { valueName }));
}

1.2 获取创建工厂 - GetControllerFactory

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

1.3 创建控制器类 - CreateController

//DefaultControllerFactory类
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 = this.GetControllerType(requestContext, controllerName);
return this.GetControllerInstance(requestContext, controllerType);
}

1.3.1 GetControllerType()

protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)
{
object obj2;
Type type;
if (string.IsNullOrEmpty(controllerName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
if ((requestContext != null) && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out obj2))
{
IEnumerable<string> source = obj2 as IEnumerable<string>;
if ((source != null) && source.Any<string>())
{
HashSet<string> namespaces = new HashSet<string>(source, StringComparer.OrdinalIgnoreCase);
type = this.GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, namespaces);
if (type == null)
{
bool flag = false;
if (!flag.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"]))
{
goto Label_0092;
}
}
return type;
}
}
Label_0092:
if (this.ControllerBuilder.DefaultNamespaces.Count > )
{
HashSet<string> set2 = new HashSet<string>(this.ControllerBuilder.DefaultNamespaces,
      StringComparer.OrdinalIgnoreCase);
type = this.GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, set2);
if (type != null)
{
return type;
}
}
return this.GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null);
}

这里会从路由中, 获取控制器所在的命名空间, 有一点需要注意, 尽量不要在不同的namespace里面命名相同的控制器,不管有这个限制还是没有这个限制, 都是能正常创建控制器的(不重名的情况下). 先看这里.

private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces)
{
   //MVC-ControllerTypeCache.xml文件中获取Controller的缓存
this.ControllerTypeCache.EnsureInitialized(this.BuildManager);
   //从之前获取的缓存中, 来获取控制器的类型
ICollection<Type> controllerTypes = this.ControllerTypeCache.GetControllerTypes(controllerName, namespaces);
switch (controllerTypes.Count)
{
case :
return null; case :
return controllerTypes.First<Type>();
}
throw CreateAmbiguousControllerException(route, controllerName, controllerTypes);
}

1.3.2 GetControllerInstance()

protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(0x194,
      string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_NoControllerFound,   
      new object[] { requestContext.HttpContext.Request.Path }));
}
if (!typeof(IController).IsAssignableFrom(controllerType))
{
throw new ArgumentException(  
      string.Format(CultureInfo.CurrentCulture,
      MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,
      new object[] { controllerType }), "controllerType");
}
return this.ControllerActivator.Create(requestContext, controllerType);
}

来看一下这里的Create方法, 看一下具体是怎么创建的.

public IController Create(RequestContext requestContext, Type controllerType)
{
IController controller;
try
{
controller = (IController) (this._resolverThunk().GetService(controllerType) ??
      Activator.CreateInstance(controllerType));
}
catch (Exception exception)
{
throw new InvalidOperationException(
      string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController,
      new object[] { controllerType }), exception);
}
return controller;
}

这里有一个扩展点, 像之前篇幅提到的Autofac Mvc部分, 在这里, 反馈的GetService(controllerType)就不是一个空值了.

如果没有搜索到这些扩展, 就会默认反射的方式来创建控制器.

Demo: 值的注意的是, 这些扩展都是要实现 IDependencyResolve 接口的, 此例子是从Autofac.Integration.Mvc.dll中来的

public class AutofacDependencyResolver : IDependencyResolver
{
// Fields
private readonly Action<ContainerBuilder> _configurationAction;
private readonly ILifetimeScope _container;
private ILifetimeScopeProvider _lifetimeScopeProvider; // Methods
public AutofacDependencyResolver(ILifetimeScope container);
public AutofacDependencyResolver(ILifetimeScope container, ILifetimeScopeProvider lifetimeScopeProvider);
public AutofacDependencyResolver(ILifetimeScope container, Action<ContainerBuilder> configurationAction);
public AutofacDependencyResolver(ILifetimeScope container,
    ILifetimeScopeProvider lifetimeScopeProvider,
    Action<ContainerBuilder> configurationAction);
public object GetService(Type serviceType);
public IEnumerable<object> GetServices(Type serviceType); // Properties
public ILifetimeScope ApplicationContainer { get; }
public static AutofacDependencyResolver Current { get; }
public ILifetimeScope RequestLifetimeScope { get; }
}

2. Execute()

这里执行的是ControllerBase的Execute方法. 是一个虚方法. 这里的方法, 留到下一篇分析了.

protected virtual void Execute(RequestContext requestContext)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (requestContext.HttpContext == null)
{
throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");
}
this.VerifyExecuteCalledOnce();
   //在这里创建了控制器上下文, ControllerContext
this.Initialize(requestContext);
using (ScopeStorage.CreateTransientScope())
{
     //加载 TempData, 创建及执行 Action, 处理 Action 返回的 ActionResult, 保存TempData数据
this.ExecuteCore();
}
}

3. ReleaseController()

这个方法, 顾名思义, 是释放资源的. 来看一下, DefaultControllerFactory 类中的此方法

public virtual void ReleaseController(IController controller)
{
IDisposable disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}

从上面的分析, 基本可以看到控制器的创建过程, 至于他的执行过程, 就放到下一篇去了, 内容还是很多的.

二、扩展

从上面的分析能看到这里有一个依赖注入扩展, 那么下面, 就这个依赖注入扩展, 来举一个小例子.(残破的例子, 别介意, 能演示功能的)

用的Autofac, 不过与上面不同, 并没有引用上面的程序集, 只引用Autofac.dll一个程序集就可以了.

我先从之前Autofac篇章, 弄了一个Helper类过来, 精简一下.

public class IocContainer
{
private static ContainerBuilder builder; private static IContainer container; static IocContainer()
{
builder = new ContainerBuilder();
} public static void RegisterTypes(params Type[] types)
{
builder.RegisterTypes(types);
} public static void Build()
{
container = builder.Build();
} public static object Create(Type t)
{
return container.Resolve(t);
}
}

然后, 我建了一个Resolver.

public class MyDependencyResolver : IDependencyResolver
{
public object GetService(Type serviceType)
{
bool flag = true;
string path = @"F:\MVC解析\MyControllerFac\1.txt";
if (!File.Exists(path))
{
File.Create(path).Close();
} try
{
return IocContainer.Create(serviceType);
}
catch (Exception)
{
flag = false;
//这里需要注意, 需要返回一个null值, 否则会报错
return null;
}
finally
{
using (FileStream fs = new FileStream(path, FileMode.Append))
{
var msg = flag ? "命中" : "飘过";
byte[] bt = System.Text.Encoding.UTF8.GetBytes(serviceType.ToString() + " : " + msg + "\r\n");
fs.Write(bt, , bt.Length);
           fs.Close();
}
}
} public IEnumerable<object> GetServices(Type serviceType)
{
var res = new List<object>();
try
{
var obj = IocContainer.Create(serviceType);
res.Add(obj);
}
catch (Exception)
{ }
return res;
}
}

现在前置工作做好了, 可以去MVC中, 注册我自己的方法了.

protected void Application_Start()
{
AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
  
   //这里我就省懒了, 并没有写到单独的方法中取
IocContainer.RegisterTypes(System.Reflection.Assembly.Load("MyMvc").GetTypes()); IocContainer.Build(); DependencyResolver.SetResolver(new MyDependencyResolver());
}

OK, 万事俱备, 只差测试. 跑起来吧

页面能正常访问, 那么是不是走的我哪里呢? 从页面上肯定看不出来, 所以我加了点东西, 看一下1.txt

从这里来看, 并不是只有 HomeController 走了我自定义的那个方式, 这也是为什么一定要返回一个null了. 对于这些飘过的, 就只能使用MVC默认的方式了. 还是很神奇的.

目录已同步

MVC源码分析 - Controller创建和创建扩展的更多相关文章

  1. Spring源码分析之Bean的创建过程详解

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...

  2. dubbo源码分析3-service bean的创建与发布

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

  3. ASP.NET MVC源码分析

    MVC4 源码分析(Visual studio 2012/2013) HttpModule中重要的UrlRoutingModule 9:this.OnApplicationPostResolveReq ...

  4. asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证

    原文:asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证 在前面的文章中我们曾经涉及到ControllerActionInvoker类GetPara ...

  5. ASP.NET MVC 源码分析(一)

    ASP.NET MVC 源码分析(一) 直接上图: 我们先来看Core的设计: 从项目结构来看,asp.net.mvc.core有以下目录: ActionConstraints:action限制相关 ...

  6. 精尽Spring MVC源码分析 - HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  7. 精尽Spring MVC源码分析 - HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  8. WebForm / MVC 源码分析

    ASP.NET WebForm / MVC 源码分析   浏览器 Url:https//localhost:6565/Home/Index ,https//localhost:6565/WebForm ...

  9. ASP.NET WebForm / MVC 源码分析

    浏览器 Url:https//localhost:6565/Home/Index ,https//localhost:6565/WebForm1.aspx,请求服务器(构建请求报文,并且将请求报文发送 ...

随机推荐

  1. PhpStorm创建Drupal模块项目开发教程(4)

    编码器是一个检查和操纵代码的Drupal-specific工具. 探测器则是发现异常代码,通常被用于开发中的编码错误预警. 接下来将介绍编码器与探测器在PhpStorm中整合工作的各个步骤,实现PHP ...

  2. GNU中宏定义对可变参数的支持(引自百度)

    http://zhidao.baidu.com/question/125413478.html 问:#define PDEBUG(fmt,args...) fprintf(stderr,fmt, ## ...

  3. at System.Data.EntityClient.EntityConnection.GetFactory(String providerString)

    最近在做一个WinForm的项目. 使用vs2013开发. 数据库使用的是oracle. 在本地写了一个webservice .测试正常.发布到服务器的时候.就是提示了错误. 打开服务器上的日志.看到 ...

  4. 脚手架快速搭建springMVC框架项目

    apid-framework脚手架快速搭建springMVC框架项目   rapid-framework介绍:   一个类似ruby on rails的java web快速开发脚手架,本着不重复发明轮 ...

  5. 有关Android存储的相关概念辨析

    我想念许多Android开发人员在碰到有关存储的相关问题时,肯定遇到过“内部存储/内存”.“外部存储/外存”等类似的概念,尤其是将相关概念跟非开发人员解释时,那真是“秀才遇到兵,有理说不清哪”.包括我 ...

  6. Visual Studio 2013 IIS Explorer 停止调试继续访问站点

    升级到2013后,在做调试的时候默认调试服务器是 IIS Explorer,当终止调试的时候再次访问调试站点时已经无法访问了.此时想预览一下感觉很不方便. 为了能够预览可以参考一下配置: Tools  ...

  7. 最简单的修改HashMap value值的方法

    说到遍历,首先应该想到for循环,然而map集合的遍历通常情况下是要这样在的,先要获得一个迭代器. Map<Integer,String> map = new HashMap<> ...

  8. Routing(路由) & Multiple Views(多个视图) step 7

    Routing(路由) & Multiple Views(多个视图) step 7 1.切换分支到step7,并启动项目 git checkout step-7 npm start 2.需求: ...

  9. Linux 宿主机安装 MiniGUI

    去MiniGUI官方网站看的时候,很兴奋,安装竟然这么容易. 上帝总是在给你一个苹果之后,赏你一巴掌.我的确是高兴太早了. 首先看一下官网文档的说明步骤: (截取于官方文档) Installing r ...

  10. iOS基础 - Copy

    copy和mutableCopy 一个对象使用copy或mutableCopy方法可以创建对象的副本 copy – 需要先实现NSCoppying协议,创建的是不可变副本(如NSString.NSAr ...