好久没有写技术文章了,真是惭愧!
这几个月以来人很闲,也就读了些.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 {
3
4}
5
6 protected void Session_Start(Object sender, EventArgs e)
7 {
8
9}
10
11 protected void Application_Error(Object sender, EventArgs e)
12 {
13
14}
15
16 protected void Session_End(Object sender, EventArgs e)
17 {
18
19}
20
21 protected void Application_End(Object sender, EventArgs e)
22 {
23
24}
这些事件处理函数是怎样注册到相应的事件中去的呢?
通过.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数组中的方法注册到相应的事件中去。好了我们来看代码,代码我做了相应注释:
private void ReflectOnApplicationType()
{
ArrayList handlers = new ArrayList();
MethodInfo[] methods;
Debug.Trace("PipelineRuntime", "ReflectOnApplicationType");
// 枚举所有Global.asax中的类里定义的方法
methods = _theApplicationType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
foreach (MethodInfo m in methods)
{
//如果方法具有Application和HttpModules里的事件处理函数的签名
if (ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
handlers.Add(m);
}
// 枚举所有Global.asax中的类的基类(基类继承于HttpApplication)里定义的方法
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);
}
}
private bool ReflectOnMethodInfoIfItLooksLikeEventHandler(MethodInfo m)
{
if (m.ReturnType != typeof(void))
return false;
//必须有两个参数(object,eventargs)
ParameterInfo[] parameters = m.GetParameters();
switch (parameters.Length)
{
case 0:
// ok
break;
case 2:
// 第一个参数必须是object
if (parameters[0].ParameterType != typeof(System.Object))
return false;
// 第二个参数必须是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;
// 特殊处理情况
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;
}
private void HookupEventHandlersForApplicationAndModules(MethodInfo[] handlers)
{
_currentModuleCollectionKey = HttpApplicationFactory.applicationFileName;
if(null == _pipelineEventMasks)
{
Dictionary<string, RequestNotification> dict = new Dictionary<string, RequestNotification>();
BuildEventMaskDictionary(dict);
if(null == _pipelineEventMasks)
{
_pipelineEventMasks = dict;
}
}
//枚举获取的方法数组
for (int i = 0; i < handlers.Length; i++)
{
MethodInfo appMethod = handlers[i];
String appMethodName = appMethod.Name;
int namePosIndex = appMethodName.IndexOf('_');
String targetName = appMethodName.Substring(0, namePosIndex);
//找到方法名中‘_’的前面部分(Application或者Modules) 并获取实例
Object target = null;
if (StringUtil.EqualsIgnoreCase(targetName, "Application"))
target = this;
else if (_moduleCollection != null)
target = _moduleCollection[targetName];
if (target == null)
continue;
//获取Application或者Modules的事件
Type targetType = target.GetType();
EventDescriptorCollection events = TypeDescriptor.GetEvents(targetType);
string eventName = appMethodName.Substring(namePosIndex+1);
EventDescriptor foundEvent = events.Find(eventName, true);
//可能有OnEventName这种情况
if (foundEvent == null&& StringUtil.EqualsIgnoreCase(eventName.Substring(0, 2), "on"))
{
eventName = eventName.Substring(2);
foundEvent = events.Find(eventName, true);
}
MethodInfo addMethod = null;
if (foundEvent != null)
{
EventInfo reflectionEvent = targetType.GetEvent(foundEvent.Name);
Debug.Assert(reflectionEvent != null);
if (reflectionEvent != null) {
addMethod = reflectionEvent.GetAddMethod();
}
}
if (addMethod == null)
continue;
ParameterInfo[] addMethodParams = addMethod.GetParameters();
if (addMethodParams.Length != 1)
continue;
//建立委托对象
Delegate handlerDelegate = null;
ParameterInfo[] appMethodParams = appMethod.GetParameters();
if (appMethodParams.Length == 0)
{
if (addMethodParams[0].ParameterType != typeof(System.EventHandler))
continue;
//通过委托代理
ArglessEventHandlerProxy proxy = new ArglessEventHandlerProxy(this, appMethod);
handlerDelegate = proxy.Handler;
}
else
{
try
{
handlerDelegate = Delegate.CreateDelegate(addMethodParams[0].ParameterType, this, appMethodName);
}
catch
{
continue;
}
}
//通过AddXXX()注册事件处理函数
try
{
addMethod.Invoke(target, new Object[1]{handlerDelegate});
}
catch
{
if (HttpRuntime.UseIntegratedPipeline) {
throw;
}
}
if (eventName != null)
{
if (_pipelineEventMasks.ContainsKey(eventName)) {
if (!StringUtil.StringStartsWith(eventName, "Post")) {
_appRequestNotifications |= _pipelineEventMasks[eventName];
}
else {
_appPostNotifications |= _pipelineEventMasks[eventName];
}
}
}
}
}
看了其中的代码,相信大家就知道了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上没有找到答案。
但Scott(K. Scott Allen) 的一篇文章 Inside AutoEventWireup说了当AutoEventWireup为true时,运行时会查找每个方法并注册到相应的事件里,代码就像下面这样:
bool throwOnFailure = false ;
Delegate d = null ;
d = Delegate.CreateDelegate(
typeof (EventHandler), this ,
" Page_Load " , ignoreCase,
throwOnFailure
);
Scott说的非常好,但是运行时又是怎么实现的呢?没办法,又是一阵猛翻asp.net源码,终于找到了,代码如下:
private void BuildAutomaticEventHookup()
{
// Skip if we're only generating the intermediate class
if (_sourceDataClass == null)
return;
CodeMemberProperty prop;
// If FAutoEventWireup is turned off, generate a SupportAutoEvents prop that
// returns false.
if (!Parser.FAutoEventWireup)
{
prop = new CodeMemberProperty();
prop.Attributes &= ~MemberAttributes.AccessMask;
prop.Attributes &= ~MemberAttributes.ScopeMask;
prop.Attributes |= MemberAttributes.Override | MemberAttributes.Family;
prop.Name = "SupportAutoEvents";
prop.Type = new CodeTypeReference(typeof(bool));
prop.GetStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(false)));
_sourceDataClass.Members.Add(prop);
return;
}
}
BuildAutomaticEventHookup()方法当AutoEventWireup为false时生成了一个重载TemplateControl类的属性SupportAutoEvents,在TemplateControl里该属性返回true,重载代码如下:
{
get
{
return false;
}
}
而在TemplateControl类里通过调用HookUpAutomaticHandlers()方法来进行自动事件处理函数注册:
internal void HookUpAutomaticHandlers()
{
//SupportAutoEvents属性为false直接返回
if (!SupportAutoEvents)
{
return;
}
// 从缓存获取事件列表
object o = _eventListCache[GetType()];
IDictionary dictionary = null;
if (o == null)
{
lock (_lockObject)
{
//再次获取,可能另一线程缓存了 这是单例模式的实现
o = _eventListCache[GetType()];
if (o == null)
{
dictionary = new ListDictionary();
GetDelegateInformation(dictionary);
if (dictionary.Count == 0)
{
o = _emptyEventSingleton;
}
else
{
o = dictionary;
}
// 缓存
_eventListCache[GetType()] = o;
}
}
}
if (o == _emptyEventSingleton)
{
return;
}
dictionary = (IDictionary)o;
foreach (string key in dictionary.Keys)
{
EventMethodInfo info = (EventMethodInfo)dictionary[key];
Debug.Assert(_eventObjects[key] != null);
bool eventExists = false;
MethodInfo methodInfo = info.MethodInfo;
Delegate eventDelegates = Events[_eventObjects[key]];
if (eventDelegates != null)
{
foreach (Delegate eventDelegate in eventDelegates.GetInvocationList())
{
// 已经添加
if (eventDelegate.Method.Equals(methodInfo))
{
eventExists = true;
break;
}
}
}
if (!eventExists)
{
// 代理委托
IntPtr functionPtr = methodInfo.MethodHandle.GetFunctionPointer();
EventHandler handler = (new CalliEventHandlerDelegateProxy(this, functionPtr, info.IsArgless)).Handler;
Events.AddHandler(_eventObjects[key], handler);
}
}
}
而GetDelegateInformation()方法通过多次调用后会调用到GetDelegateInformationFromMethod()方法来实现根据相应方法(Page_Load等)建立委托,代码如下:
private bool GetDelegateInformationFromMethod(string methodName, IDictionary dictionary)
{
// 第一步,找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页重写了:
private const string _pagePreInitEventName = "Page_PreInit";
private const string _pageInitEventName = "Page_Init";
private const string _pageInitCompleteEventName = "Page_InitComplete";
private const string _pageLoadEventName = "Page_Load";
private const string _pagePreLoadEventName = "Page_PreLoad";
private const string _pageLoadCompleteEventName = "Page_LoadComplete";
private const string _pagePreRenderCompleteEventName = "Page_PreRenderComplete";
private const string _pageDataBindEventName = "Page_DataBind";
private const string _pagePreRenderEventName = "Page_PreRender";
private const string _pageSaveStateCompleteEventName = "Page_SaveStateComplete";
private const string _pageUnloadEventName = "Page_Unload";
private const string _pageErrorEventName = "Page_Error";
private const string _pageAbortTransactionEventName = "Page_AbortTransaction";
private const string _onTransactionAbortEventName = "OnTransactionAbort";
private const string _pageCommitTransactionEventName = "Page_CommitTransaction";
private const string _onTransactionCommitEventName = "OnTransactionCommit";
说到这里我想再补充下,上面的方法名常量有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/