zoukankan      html  css  js  c++  java
  • 解读System.Web.UI.Page中关键方法ProcessRequestMain()

         为了更好地优化博客园程序的性能,最近我在优化代码的同时,更深入地去研究asp.net的源代码。asp.net的源代码通过Reflector工具一鉴无遗, 虽然不是原版的代码,但已经足够了,其中的原理与思想已经清楚地摆在我们面前。这是.NET开发人员的幸运! 
        在我们开发asp.net应用程序时, System.Web.UI.Page是我们最熟悉并用的最多的一个类。但有多少人真正对这个类的源代码仔细研究过? 从相关搜索中可以看出并不是很多,比如,用Google搜索ProcessRequestMain方法中开始的“OnPageStartSessionObjects”,结果只有四个。
         今天我花了半天时间,研究了Page中处理请求的最关键的方法:ProcessRequestMain(),在这里我将自己的理解写出来与大家共享,欢迎大家批评并指正。一切尽在代码注释中:

    注:为了方便阅读与理解,已去掉源代码中输出Trace信息的部分。

    public class Page
    {
        
    private void ProcessRequestMain()
        
    {
            
    try
            
    {
                
    if (this.IsInAspCompatMode)
                
    {
                    AspCompatApplicationStep.OnPageStartSessionObjects();
                }

                
    //将当前Context的Session中的对象传递给asp的OnStartPage
                
    //通过<%@ ASPCOMPAT="true" %>进行设置
                
    //参考文章: http://samples.gotdotnet.com/quickstart/aspplus/doc/cominterop.aspx

                
    this._requestValueCollection = this.DeterminePostBackMode();
                
    //检查PostBackMode, 如果启用了PostBack, 获取VIEWSTATE数据并赋值给_requestValueCollection

                
    base.InitRecursive(null);
                
    //调用基类的InitRecursive方法通过递归对子控件进行初始化, 比如: 生成控件ID,设置控件的Page属性。
                
    //OnInit()方法将会在此时被调用
                
                
    if (this.IsPostBack)
                
    {
                    
    this.LoadPageViewState();
                    
    //从_requestValueCollection通过反序列化载入视图状态数据,如果页面的Layout发生了改变,子控件重新递归载入(LoadViewStateRecursive)视图状态
                    
    //载入后,从视图状态数据从得到所有要处理PostBack的控件并注册到_controlsRequiringPostBack. 

                    
    this.ProcessPostData(this._requestValueCollection, true);
                    
    //处理PostBack数据, 从PostBack数据中得到所有控件ID并检查每个控件,如果不能在当前页面中找到该控件(FindControl), 将其存入_leftoverPostData.
                    
    //如果存在该控件,继续检查,若该控件没有实现IPostBackDataHandler, 但实现了 IPostBackEventHandler,注册该控件进行事件处理。
                    
    //若该控件实现了System.Web.UI.IPostBackDataHandler,该控件的LoadPostData()方法在此时被调用,并将其加入到_changedPostDataConsumers,
                    
    //并从._controlsRequiringPostBack(LoadPageViewState时对它进行了赋值)中移除该控件.
                    
    //这样就从_controlsRequiringPostBack中移除了所有实现IPostBackDataHandler接口的控件.
                    
    //接着继续对 _controlsRequiringPostBack中余下的控件进行处理,但奇怪的是又对这些余下的控件检查是否存在并实现了IPostBackDataHandler, 如果实现,
                    
    //调用该控件的LoadPostData()(有点多此一举了, 可能是Relector生成的代码有误)并放入_changedPostDataConsumers,如果没实现,放入一个新ArrayList变量,
                        //检查结束后,将其赋值给
    _controlsRequiringPostBack.那现在_controlsRequiringPostBack中剩下什么呢?没有实现IPostBackDataHandler, 
                        //但实现了 IPostBackEventHandler的控件以及
    没有被Load的控件, 也就是在PostBack数据中存在但FindControl没有找到的控件。
                        //为什么会有找不到的控件呢?我们这里需要注意的是OnLoad()事件还没执行,
                    
    //有些控件还没有被加载.下面的base.LoadRecursive()就是触发OnLoad()事件的。

                }


                
    base.LoadRecursive();
                
    //触发页面的OnLoad()事件->递归触发子控件的OnLoad()事件->将页面的_controlState状态设置为ControlState.Loaded

                
    if (this.IsPostBack)
                
    {
                    
    this.ProcessPostData(this._leftoverPostData, false);
                    
    //理解了ProcessPostData(this._requestValueCollection, true)之后, 这个就很好理解了,就是检查_controlsRequiringPostBack中的控件是否存在并实现了
                    
    //IPostBackDataHandler, 如果实现,调用该控件的LoadPostData()方法并将其加入到_changedPostDataConsumers.ProcessPostData(this._leftoverPostData, false)
                    
    //这个方法就是为OnLoad()之后加载的控件服务的。
                         }
                    
    this.RaiseChangedEvents();
                    
    //在_changedPostDataConsumers(在两个ProcessPostData方法中向它添加了数据)中没有实现IPostBackDataHandler接口的控件触发RaisePostDataChangedEvent.

                    
    this.RaisePostBackEvent(this._requestValueCollection);
                    
    //触发_registeredControlThatRequireRaiseEvent及PostBack数据中实现IPostBackEventHandler接口的控件的RaisePostBackEvent事件。
            
            
                
    base.PreRenderRecursiveInternal();
                
    //首先调用EnsureChildControls,检查子控件是否创建, 如果没有, 调用进行创建CreateChildControls.
                
    //触发OnPreRende事件.
                
    //递归调用子控件的PreRenderRecursiveInternal方法
                
    //设置._controlState为 ControlState.PreRendered

                
    this.SavePageViewState();
                
    //保存视图状态数据至._viewStateToPersist 

                
    base.RenderControl(this.CreateHtmlTextWriter(this.Response.Output));
                
    //输出当前及所有子控件的内容

            }

            
    catch (ThreadAbortException)
            
    {
                
    base.UnloadRecursive(true);
                
    return;
            }

            
    catch (ConfigurationException)
            
    {
                
    throw;
            }

            
    catch (Exception exception1)
            
    {
                PerfCounters.IncrementCounter(AppPerfCounter.ERRORS_DURING_REQUEST);
                PerfCounters.IncrementCounter(AppPerfCounter.ERRORS_TOTAL);
                
    if (!this.HandleError(exception1))
                
    {
                    
    throw;
                }

                
    return;
            }

        }

    }

  • 相关阅读:
    oracle空表导出的问题
    虚拟机连不上网
    mongodb查询返回内嵌符合条件的文档
    gson转换带有objectId的问题
    java部署项目,ip访问不到,localhost能访问到,浏览器启用了代理的问题
    mongodb replica set(副本集)设置步骤
    a different object with the same identifier value was already associat
    头文件.h的应用以及fortran和c的混合编程
    Matlab符号运算总结
    fortran自由格式每行最多编写132个字符
  • 原文地址:https://www.cnblogs.com/dudu/p/259328.html
Copyright © 2011-2022 走看看