源码解析HttpApplicationFactory类的功能

一、综述: 

  HttpApplicationFactory类的主要作用是创建并且管理用于处理Http请求的HttpApplication对象。

   HttpApplication是ASP.NET Pipline(管道)的载体,管理了请求的整个生命周期。正是在这个类里,ASP.NET以管道的模式处理所有的请求。

  

  HttpApplication将管道处理的不同阶段要处理的事情以事件的方式对外开放,关注某事件Module(模块)在自己的内部注册这些事件,

然后HttpApplication对象根据http请求所处的不同阶段,分别触发这些事件,完成对http请求的处理。 

 

      如SessionStateModule模块就注册了获取状态和发布状态的事件。获取状态事件被触发时,该模块会获取一个Session对象,赋值给HttpContext对象,这样在

执行处理程序阶段,我们就可以访问Session对象进行存取数据(对于MVC5,这个过程是在控制器的Action中)。等到了发布状态事件时,该模块会根据适当的情况,

将Session中的数据持久保存起来,然后将Session对象从HttpContext对象中删除。已在其他随笔里介绍了SessionStateModule模块的核心功能。

 

  当然在这里只是简单介绍HttpApplication的作用,该类的功能十分强大完善,但也特别复杂,所以会在另外的随笔里单独介绍。

  在这里介绍HttpApplication只是为了引出HttpApplicationFactory。

 

二、详细介绍: 

  HttpApplicationFactory内部使用了Stack(栈)来存储多个HttpApplication对象。

   主要功能如下:

   1、创建存储HttpApplication对象,包括循环使用等。

   2、将我们在Global.asax文件中定义的事件整理出来,然后在HttpApplication初始化时传递初始化方法。在下面的示例代码中,我们注册了请求开始事件,

   这个事件将会向文档中输出一句话,而这句话也会显示在我们的网页里。

 1     public class MvcApplication : System.Web.HttpApplication
 2     {
 3         protected void Application_Start()
 4         {
 5             AreaRegistration.RegisterAllAreas();
 6             FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
 7             RouteConfig.RegisterRoutes(RouteTable.Routes);
 8             BundleConfig.RegisterBundles(BundleTable.Bundles);
 9         }
10 
11         protected void Application_BeginRequest(object sender,EventArgs e)
12         {
13             HttpApplication application = sender as HttpApplication;
14             HttpResponse resp = application.Context.Response;
15             resp.Write("<h1>要开始处理请求了;</h1>");
16         }
17     }

   我们在Global.asax中写的方法,就是在HttpApplicationFactory中被整理处理,然后传递到HttpApplication内部。

   3、HttpApplicationFactory会生成两种不同类型HttpApplication对象。一种类型的对象用于触发特殊的事件,如Application_Start(应用程序开始,

该方法只会被调用一次),Session_End等。这种类型的对象存储在另外一个栈中,且不会参与我们上面介绍的处理请求的生命周期。另外一种类型的就是就是我们上面介绍的,参互

http请求的整个生命周期的对象。

   4、调用Application_Start、Session_End等方法。类似于Application_BeginRequest这样的事件,是在HttpApplication内部作为http请求的生命周期的一部分调用的。

但是诸如Application_Start这样的事件,是在HttpApplication的外部HttpApplicationFactory内部被调用的,没有作为请求生命周期中的一部分。

  好了,功能的文字介绍已经完毕,下面我们将从代码触发,来逐个分析这些功能的实现。

 

三、代码分析:

  HttpRuntime通过调用HttpApplicationFactory的GetApplicationInstance方法来获取一个HttpApplication对象,然后开始一个请求的生命周期。

 1         internal static IHttpHandler GetApplicationInstance(HttpContext context) {
 2             if (_customApplication != null)
 3                 return _customApplication;
 4 
 5             // Check to see if it's a debug auto-attach request
 6             if (context.Request.IsDebuggingRequest)
 7                 return new HttpDebugHandler();
 8 
 9             _theApplicationFactory.EnsureInited();
10 
11             _theApplicationFactory.EnsureAppStartCalled(context);
12 
13             return _theApplicationFactory.GetNormalApplicationInstance(context);
14         }

 

   EnsureInited()方法保证了这个类单例对象被初始化,最主要的功能就是将我们在  Global.asax文件中定义的方法整理处理。

   EnsureAppStartCalled()确保我们在 Global.asax文件中定义的Application_Start()方法被调用且仅被调用一次。

   GetNormalApplicationInstance()返回一个普通的用于处理http请求的HttpApplication实例。

  

  下面我们从EnsureInited()开始。

 

  3.1  EnsureInited()

  

---------从代码总可以看出,这个方法也只会被调用一次-------------
private void EnsureInited() {
            if (!_inited) {
                lock (this) {
                    if (!_inited) {
                        Init();
                        _inited = true;
                    }
                }
            }
        }
-------------最主要是CompileApplication();--------------
        private void Init() {
            if (_customApplication != null)
                return;

            try {
                try {
                    _appFilename = GetApplicationFile();

                    CompileApplication();
                }
                finally {
                    // Always set up global.asax file change notification, even if compilation
                    // failed.  This way, if the problem is fixed, the appdomain will be restarted.
                    SetupChangesMonitor();
                }
            }
            catch { // Protect against exception filters
                throw;
            }
        }
------------最重要ReflectOnApplicationType();-----------------------
        private void CompileApplication() {
            // Get the Application Type and AppState from the global file

            _theApplicationType = BuildManager.GetGlobalAsaxType();

            BuildResultCompiledGlobalAsaxType result = BuildManager.GetGlobalAsaxBuildResult();

            if (result != null) {

                // Even if global.asax was already compiled, we need to get the collections
                // of application and session objects, since they are not persisted when
                // global.asax is compiled.  Ideally, they would be, but since <object> tags
                // are only there for ASP compat, it's not worth the trouble.
                // Note that we only do this is the rare case where we know global.asax contains
                // <object> tags, to avoid always paying the price (VSWhidbey 453101)
                if (result.HasAppOrSessionObjects) {
                    GetAppStateByParsingGlobalAsax();
                }

                // Remember file dependencies
                _fileDependencies = result.VirtualPathDependencies;
            }

            if (_state == null) {
                _state = new HttpApplicationState();
            }


            // Prepare to hookup event handlers via reflection

            ReflectOnApplicationType();
        }
EnsureInited

 

  上面的方法最终调用ReflectOnApplicationType(),所以把上面的过程折叠起来,重点介绍这个方法。

        private void ReflectOnApplicationType() {
            ArrayList handlers = new ArrayList();
            MethodInfo[] methods;

            //_theApplicationType 如果有此处的_theApplicationType变量,就是Global.asax文件中定义的MvcApplication:System.Web.HttpApplication这个类
            //如果没有Global.asax这个文件,那么就是默认的System.Web.HttpApplication
            
            //获取类中非公共的、公共的、实例的、静态的方法
            methods = _theApplicationType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
            foreach (MethodInfo m in methods) {
            
            //ReflectOnMethodInfoIfItLooksLikeEventHandler
            //该方法用于判断这是否是一个事件处理器,并且单独保存Application_Start这样的方法
            
                if (ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
                    handlers.Add(m);
            }
            
            // get base class private methods (GetMethods would not return those)
            Type baseType = _theApplicationType.BaseType;
            if (baseType != null && baseType != typeof(HttpApplication)) {
                methods = baseType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
                foreach (MethodInfo m in methods) {
                    if (m.IsPrivate && ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
                        handlers.Add(m);
                }
            }

            
            //集合保存为一个数组
            _eventHandlerMethods = new MethodInfo[handlers.Count];
            for (int i = 0; i < _eventHandlerMethods.Length; i++)
                _eventHandlerMethods[i] = (MethodInfo)handlers[i];
        }

   通过这个方法,将我们定义在Global.asax文件中的事件处理器整理了处理。并且额外保存了Application_Start、Application_End、Session_End这样的方法。

   这些方法会在HttpApplicationFactory中被调用.

 

        //判断Global.asax中定义的方法是否合法
        private bool ReflectOnMethodInfoIfItLooksLikeEventHandler(MethodInfo m) {
            if (m.ReturnType != typeof(void))
                return false;

            // 必须有0个参数或两个参数(object, eventargs)
            ParameterInfo[] parameters = m.GetParameters();

            switch (parameters.Length) {
                case 0:
                    // ok
                    break;
                case 2:
                    // 索引为0的参数必须是object类型
                    if (parameters[0].ParameterType != typeof(System.Object))
                        return false;
                    // 索引为1的参数必须是EventArgs类型或者EventArgs的子类
                    if (parameters[1].ParameterType != typeof(System.EventArgs) &&
                        !parameters[1].ParameterType.IsSubclassOf(typeof(System.EventArgs)))
                        return false;
                    // ok
                    break;

                default:
                    return false;
            }
            
            //检查方法名是否合法(必须有下划线,且下划线不在第一个或最后一个)
            String name = m.Name;
            int j = name.IndexOf('_');
            if (j <= 0 || j > name.Length-1)
                return false;

            // 特殊的事件
            //这几个事件会被记录在HttpApplicationFactory类内部
            
            if (StringUtil.EqualsIgnoreCase(name, "Application_OnStart") ||
                StringUtil.EqualsIgnoreCase(name, "Application_Start")) {
                _onStartMethod = m;
                _onStartParamCount = parameters.Length;
            }
            else if (StringUtil.EqualsIgnoreCase(name, "Application_OnEnd") ||
                     StringUtil.EqualsIgnoreCase(name, "Application_End")) {
                _onEndMethod = m;
                _onEndParamCount = parameters.Length;
            }
            else if (StringUtil.EqualsIgnoreCase(name, "Session_OnEnd") ||
                     StringUtil.EqualsIgnoreCase(name, "Session_End")) {
                _sessionOnEndMethod = m;
                _sessionOnEndParamCount = parameters.Length;
            }

            return true;
        }

  

   到此,EnsureInited()方法已经介绍完毕,这个方法的主要功能就是完成类的初始化,尤其是将我们定义在Global.asax中的方法整理出来,保存在数组中,后期HttpApplication的初始化时传递给HttpApplication。

 

3.2 EnsureAppStartCalled

    这个方法就是用来调用我们我们在3.1中整理出来的Application_Start方法,而且保证这个方法只会被调用一次

        private void EnsureAppStartCalled(HttpContext context) {
            if (!_appOnStartCalled) {
                lock (this) {
                    if (!_appOnStartCalled) {
                        using (new DisposableHttpContextWrapper(context)) {
                            // impersonation could be required (UNC share or app credentials)

                            WebBaseEvent.RaiseSystemEvent(this, WebEventCodes.ApplicationStart);

                            // fire outside of impersonation as HttpApplication logic takes
                            // care of impersonation by itself
                            FireApplicationOnStart(context);
                        }

                        _appOnStartCalled = true;
                    }
                }
            }
        }
        
        private void FireApplicationOnStart(HttpContext context) {
            if (_onStartMethod != null) {
            
                //得到一个特殊的应用程序实例,下面会介绍这个方法
                HttpApplication app = GetSpecialApplicationInstance();
                //使用刚刚获取的实例处理特殊请求,其中的参数之一就是_onStartMethod(类型是MethfoInfo)
                //而这个方法就是我们在3.1中结束的,从Global.asax文件中的Application_Start()方法
                app.ProcessSpecialRequest(
                                         context,
                                         _onStartMethod,
                                         _onStartParamCount,
                                         this, 
                                         EventArgs.Empty, 
                                         null);
                //将这个对象放入栈内
                RecycleSpecialApplicationInstance(app);
            }
        }
EnsureAppStartCalled

  app.ProcessSpecialRequest(....)这个方法的参数之一就是_onStartMethod变量--private MethodInfo   _onStartMethod;        // Application_OnStart

  而在方法的内部,则有这样的代码,HttpApplicationFactory类控制了Application_OnStart这样的方法是否会被调用。

        //
        // Support for external calls into the application like app_onStart
        //

        [ReflectionPermission(SecurityAction.Assert, Flags=ReflectionPermissionFlag.RestrictedMemberAccess)]
        private void InvokeMethodWithAssert(MethodInfo method, int paramCount, object eventSource, EventArgs eventArgs) {
            if (paramCount == 0) {
                method.Invoke(this, new Object[0]);
            }
            else {
                Debug.Assert(paramCount == 2);

                method.Invoke(this, new Object[2] { eventSource, eventArgs });
            }
        }

 

  下面重点介绍系统是如何获取这样一个特殊的应用程序实例对象的

        //重点介绍系统是如何获取特殊的应用程序实例的
        private HttpApplication GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) {
            HttpApplication app = null;

            //free list of special (context-less) app objects 空闲的特殊(无上下文)应用程序对象列表
            //to be used for global events (App_OnEnd, Session_OnEnd, etc.) 用于全局事件
            //private Stack _specialFreeList = new Stack();
            lock (_specialFreeList) {
                if (_numFreeSpecialAppInstances > 0) {
                    app = (HttpApplication)_specialFreeList.Pop();
                    _numFreeSpecialAppInstances--;
                }
            }
            
            if (app == null) {
                //
                //  Put the context on the thread, to make it available to anyone calling
                //  将上下文放在线程上,以便任何人调用
                
                //  HttpContext.Current from the HttpApplication constructor or module Init
                //  HttpContext.Current来自HttpApplication构造函数或模块Init
                
                using (new DisposableHttpContextWrapper(context)) {
                    // If ran out of instances, create a new one
                    app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType);
                    //调用app对象的方法进行特殊的初始化
                    using (new ApplicationImpersonationContext()) {
                        app.InitSpecial(_state, _eventHandlerMethods, appContext, context);
                    }
                }
            }

            return app;
        }

   上面的方法重点介绍了系统是如何获取一个特殊HttpApplication对象的。我们看到 _specialFreeList(Stack)这样类型的一个变量。

    Stack这是一种数据结构---栈,具体的用法可以参考微软的官方文档。当这个特殊的app对象使用结束以后,会调用的下面的方法存储在栈中。

        private void RecycleSpecialApplicationInstance(HttpApplication app) {
            if (_numFreeSpecialAppInstances < _maxFreeSpecialAppInstances) {
                lock (_specialFreeList) {
                    _specialFreeList.Push(app);
                    _numFreeSpecialAppInstances++;
                }
            }
            // else: don't dispose these
        }

 

  3.3 GetNormalApplicationInstance

  到此,前面两个方法已经介绍完毕,HttpAppliactionFactory已经被初始化了,Application_Start这样的方法也被调用了,在MVC5中,我们在这个方法里

注册路由规则,添加过滤器等全局的内容等。所以在这个方法里,只能操作一些全局内容,如全局变量,任何实例化的操作都无济于事,因为这个特殊的app对象

会被存储在_specialFreeList这种的栈中,不会用于处理普通的请求。下面就开始介绍如何获取一个通常的实例。

        private HttpApplication GetNormalApplicationInstance(HttpContext context) {
            HttpApplication app = null;

            lock (_freeList) {
                if (_numFreeAppInstances > 0) {
                    app = (HttpApplication)_freeList.Pop();
                    _numFreeAppInstances--;

                    if (_numFreeAppInstances < _minFreeAppInstances) {
                        _minFreeAppInstances = _numFreeAppInstances;
                    }
                }
            }

            if (app == null) {
                // If ran out of instances, create a new one
                app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType);

                using (new ApplicationImpersonationContext()) {
                    app.InitInternal(context, _state, _eventHandlerMethods);
                }
            }

            if (AppSettings.UseTaskFriendlySynchronizationContext) {
                // When this HttpApplication instance is no longer in use, recycle it.
                app.ApplicationInstanceConsumersCounter = new CountdownTask(1); // representing required call to HttpApplication.ReleaseAppInstance
                app.ApplicationInstanceConsumersCounter.Task.ContinueWith((_, o) => RecycleApplicationInstance((HttpApplication)o), app, TaskContinuationOptions.ExecuteSynchronously);
            }
            return app;
        }

        private void RecycleNormalApplicationInstance(HttpApplication app) {
            lock (_freeList) {
                _freeList.Push(app);
                _numFreeAppInstances++;
            }
        }

  这里就可以看到,系统的普通实例是存储在_freeList这个栈中的,我们从浏览器来的请求都会从这个栈中或创建一个实例用于后面的处理。

  使用完毕后,会放入这个栈中。而且可以发现一些区别,存入_specialFreeList栈前是会判断这个栈中元素数量,如果超过一个值,则不再加入栈中。

  而在存入_freeList栈时,直接放入。因为这个栈中对象用于处理常规请求,使用的量很大。

 

结束语:

  至此HttpApplicationFactory功能已经介绍完毕。其他的诸如单例模式、反射、锁、栈、事件等不做介绍。

  简而言之,这个类就是负责HttpApplication对象的创建存取初始化等。也负责创建一些特殊的HttpApplication对象,用于处理一些特殊的全局

方法,如Application_Start,而且这样的特殊对象不用于处理普通的请求。

   当然这里的介绍是不全面的。这个类中还有很多的内容。

 

posted @ 2018-05-29 01:01  来分享_share  阅读(354)  评论(0编辑  收藏  举报