好久没有写技术文章了,真是惭愧!
这几个月以来人很闲,也就读了些.net源码,深感.net功能的强大和实现的繁琐,也使得我们这些严重依赖IDE的人越来越不想去探索.net的原理。这不前段时间想写个从应用程序周期看asp.net实现专题,但发现看书看代码容易,动手写文章真的不容易,到现在身边只剩下一堆便签和便签上的记录,所以也对博客园首页的更新速度佩服有加啊,毕竟一篇文章背后付出的努力确实是艰辛的。
好了废话少说,进入主题。大家都知道,asp.net里Global.asax文件的重要性,其中可以包含用于响应应用程序级别事件的代码,这里的应用程序级别事件包括任何由 HttpApplication 类引发的事件(语法为Application_EventName)和任何由HttpModules引发的事件(语法为ModuleName_OnEventName)。同样Global.asax也支持异步事件,只不过要显示调用AddOnEventNameAsync(System.Web.BeginEventHandler, System.Web.EndEventHandler) 方法为该事件进行注册(这个与本文主题无关)。甚至一些第三方控件和HttpModules都建议在Global.asax进行相关的本地设置。不过忘了说明一下,虽然Global.asax文件如此重要,但却不是必须的。
我一直对.net自动注册(.net代码里都是用“Hookup”,也有说成挂钩的)事件处理函数很感兴趣,例如下面这些:
2
这些事件处理函数是怎样注册到相应的事件中去的呢?
通过.NET Reflector, 当然现在.net源码能直接查看了,里面有个内部类HttpApplicationFactory是用来初始化Application的。在实例化Global.asax里的继承于HttpApplication类之前(至于asp.net 2.0 运行时流程我就不多说了,早在06年初dudu就发表了一篇 asp.net 2.0 运行时简要分析,如果想细究的话可以直接看asp.net源码了), 内部类HttpApplicationFactory会通过ReflectOnApplicationType() 方法依次枚举Global.asax里的方法,并通过ReflectOnMethodInfoIfItLooksLikeEventHandler()方法来判断Global.asax里的方法是否具有事件处理函数签名,将拥有Application事件和httpmodules事件处理函数签名的方法放进一个MethodInfo数组中。而HookupEventHandlersForAppplicationAndModules()方法则会将这个MethodInfo数组中的方法注册到相应的事件中去。好了我们来看代码,代码我做了相应注释:
看了其中的代码,相信大家就知道了Global.asax里的事件处理函数注册到相应的事件中去的实现了吧,当然你也可以自己定义事件处理函数注册到你自己定义的相应的事件中去,然后再global.asax中的 Application_Start方法中按照上面的代码进行注册,因为Application_Start方法只会在实例化Application时调用一次,不过不推荐你这样做了,因为.net现在定义的这种架构已经很好用了,再做无非有点画蛇添足了。
到这里Global.asax事件处理函数的自动注册就告一段落了,但是ASP.NET 页框架也支持以自动方式将页事件与方法相关联,这个是怎么实现的呢?
Google了一番,大家讨论AutoEventWireup 问题可不少,Page 指令的 AutoEventWireup 属性被设置为 true(或者如果缺少此属性,因为它默认为 true),该页框架将自动调用页事件,即 Page_Init 、 Page_Load等14个方法,在这种情况下,不需要任何显式的 Handles 子句或委托。但这是怎么实现的呢?.net又怎样根据AutoEventWireup 属性来动态编译或者预编译页面呢?我在Google上没有找到答案。
但Inside AutoEventWireup说了当AutoEventWireup为true时,运行时会查找每个方法并注册到相应的事件里,代码就像下面这样:
Scott说的非常好,但是运行时又是怎么实现的呢?没办法,又是一阵猛翻asp.net源码,终于找到了,代码如下:
BuildAutomaticEventHookup()方法当AutoEventWireup为false时生成了一个重载TemplateControl类的属性SupportAutoEvents,在TemplateControl里该属性返回true,重载代码如下:
而在TemplateControl类里通过调用HookUpAutomaticHandlers()方法来进行自动事件处理函数注册:
来实现根据相应方法(Page_Load等)建立委托,代码如下:
{
// 第一步,找EventHandler签名的处理函数,类似protected void Page_Load(object sender, EventArgs e)
//scott说的原理终于现形了,呵呵
EventHandler e = (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), this,
methodName, true /*ignoreCase*/, false /*throwOnBindFailure*/);
if (e != null)
{
dictionary[methodName] = new EventMethodInfo(e.Method, false);
return true;
}
// 如果不是则找VoidMethod的形式了
VoidMethod vm = (VoidMethod)Delegate.CreateDelegate(typeof(VoidMethod), this,
methodName, true /*ignoreCase*/, false /*throwOnBindFailure*/);
if (vm != null)
{
dictionary[methodName] = new EventMethodInfo(vm.Method, true);
return true;
}
return false;
}
其中参数methodName就是我们通常使用的Page_Load等等方法名了,方法名是一个私有字符串常量,所以就不能在Page页重写了:
说到这里我想再补充下,上面的方法名常量有16个,但是前面我说了只有14个方法,这又是怎么回事呢?
原来Page_Init,Page_Load,Page_DataBind,Page_PreRender,Page_Unload,Page_Error这六个方法无论TemplateControl实例是什么(TemplateControl实例可能是用户控件,或者页面,因为UserControl和Page类都是继承于TemplateControl类的)都会自动注册到相应的事件中去,而Page_PreInit,Page_InitComplete,Page_PreLoad,
Page_LoadComplete,Page_PreRenderComplete,Page_SaveStateComplete只有在TemplateControl实例同样是Page类实例的情况下才会自动注册到相应的事件中去。仔细看剩下的四个方法名,就会发现这是相对应的两组事件处理函数名,默认会添加Page_AbortTransaction和Page_CommitTransaction,如果找不到这两个方法则会查找OnTransactionAbort和OnTransactionCommit方法进行注册,所以说页面会自动注册14个事件处理函数到相应的事件中去(前提是只有这些方法都存在,才会添加成功哦)。
附注:AbortTransaction和CommitTransaction事件用于Enterprise Service Transactions的情况下(@Page指令必须含有Transaction属性,如Transaction=“Required”). 如果需要了解更多信息,可以看看意大利人Alberto Venditti 04年在codeproject上的这篇文章 .NET Distributed Transactions on Enterprise Services: a demo ,或者联系Scott(此Scott仍然是Allen,并非Scott Guthrie),我看过他的博客,他可是这方面的行家。
好了,写的比较乱,要去看 Lost 了~
欢迎大家拍砖!
作者:Dean出处:http://deanme.cnblogs.com/