zoukankan      html  css  js  c++  java
  • MVC源码分析

    上一篇, 出现了一个至关重要的类: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 > 0)
        {
            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 0: return null; case 1: 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解析MyControllerFac1.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 + "
    ");
                    fs.Write(bt, 0, 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默认的方式了. 还是很神奇的.

    目录已同步

  • 相关阅读:
    LR通过SiteScope监控mysql
    安装apk文件报waiting for device 时解决办法
    VMware虚拟机中设置端口映射(转载)
    Android模拟器中安装APK文件(转)
    netstat监控大量ESTABLISHED连接与Time_Wait连接问题(转载)
    linux 安装sysstat使用iostat、mpstat、sar、sa(转载)
    SQL联合查询中的关键语法(转)
    JVM调优总结 -Xms -Xmx -Xmn -Xss(转载)
    JVM系列三:JVM参数设置、分析(转载)
    查找进程并杀进程的命令
  • 原文地址:https://www.cnblogs.com/elvinle/p/6289545.html
Copyright © 2011-2022 走看看