通過前面的章節,我們知道HttpApplication在初始化的時候會初始化所有配置文件裡注冊的HttpModules,那麼有一個疑問,能否初始化之前動態加載HttpModule,而不是只從Web.config裡讀取?
答案是肯定的, ASP.NET MVC3發布的時候提供了一個Microsoft.Web.Infrastructure.dll文件,這個文件就是提供了動態注冊HttpModule的功能,那麼它是如何以注冊的呢?我們先去MVC3的源碼看看該DLL的源代碼。
注:該DLL位置在C:\Program Files\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies\下
我們發現了一個靜態類DynamicModuleUtility,裡面有個RegisterModule方法引起了我的注意:
// Call from PreAppStart to dynamically register an IHttpModule, just as if you had added it to the // <modules> section in Web.config. [SecuritySafeCritical] public static void RegisterModule(Type moduleType) { if (DynamicModuleReflectionUtil.Fx45RegisterModuleDelegate != null) { // The Fx45 helper exists, so just call it directly. DynamicModuleReflectionUtil.Fx45RegisterModuleDelegate(moduleType); } else { // Use private reflection to perform the hookup. LegacyModuleRegistrar.RegisterModule(moduleType); } }
通過代碼和注釋我們可以看到,這個方法就是讓我們動態注冊IHttpModule的,而且由於.Net4.5已經有helper類支持了,所以直接就可以用,其它版本使用LegacyModuleRegistrar.RegisterModule來動態注冊IHttpModule 的。而這個方法裡又分IIS6和IIS7集成或經典模式之分,代碼大體上是一致的,我們這裡就只分析IIS6版本的代碼:
private static void AddModuleToClassicPipeline(Type moduleType) { // This works by essentially adding a new entry to the <httpModules> section defined // in ~/Web.config. Need to set the collection to read+write while we do this. // httpModulesSection = RuntimeConfig.GetAppConfig().HttpModules; // httpModulesSection.Modules.bReadOnly = false; // httpModulesSection.Modules.Add(new HttpModuleAction(...)); // httpModulesSection.Modules.bReadOnly = true; HttpModulesSection httpModulesSection = null; try { object appConfig = _reflectionUtil.GetAppConfig(); httpModulesSection = _reflectionUtil.GetHttpModulesFromAppConfig(appConfig); _reflectionUtil.SetConfigurationElementCollectionReadOnlyBit(httpModulesSection.Modules, false /* value */); DynamicModuleRegistryEntry newEntry = CreateDynamicModuleRegistryEntry(moduleType); httpModulesSection.Modules.Add(new HttpModuleAction(newEntry.Name, newEntry.Type)); } finally { if (httpModulesSection != null) { _reflectionUtil.SetConfigurationElementCollectionReadOnlyBit(httpModulesSection.Modules, true /* value */); } } }
上面代碼的注釋非常重要,通過注釋我們可以看到,該方法先從RuntimeConfig.GetAppConfig().HttpModules獲取 HttpModules集合,然後在集合裡添加需要注冊的新HttpModule,那就是說HttpApplication在初始化所有 HttpModule之前必須將需要注冊的HttpModule添加到這個集合裡,那是在哪個周期呢?HttpApplication之前是 HostingEnvironment,那是不是在這裡可以注冊呢?我們去該類查看一下相關的代碼,在Initialize方法裡突然發現一個貌似很熟悉的代碼BuildManager.CallPreStartInitMethods(),代碼如下:
// call AppInitialize, unless the flag says not to do it (e.g. CBM scenario). // Also, don't call it if HostingInit failed (VSWhidbey 210495) if(!HttpRuntime.HostingInitFailed) { try { BuildManager.CallPreStartInitMethods(); if ((hostingFlags & HostingEnvironmentFlags.DontCallAppInitialize) == 0) { BuildManager.CallAppInitializeMethod(); } } catch (Exception e) { // could throw compilation errors in 'code' - report them with first http request HttpRuntime.InitializationException = e; if ((hostingFlags & HostingEnvironmentFlags.ThrowHostingInitErrors) != 0) { throw; } } }
通過去BuildManager類去查看該方法的詳情,最終發現了如下這個方法:
internal static ICollection<MethodInfo> GetPreStartInitMethodsFromAssemblyCollection(IEnumerable<Assembly> assemblies) { List<MethodInfo> methods = new List<MethodInfo>(); foreach (Assembly assembly in assemblies) { PreApplicationStartMethodAttribute[] attributes = null; try { attributes = (PreApplicationStartMethodAttribute[])assembly.GetCustomAttributes(typeof(PreApplicationStartMethodAttribute), inherit: true); } catch { // GetCustomAttributes invokes the constructors of the attributes, so it is possible that they might throw unexpected exceptions. // (Dev10 bug 831981) } if (attributes != null && attributes.Length != 0) { Debug.Assert(attributes.Length == 1); PreApplicationStartMethodAttribute attribute = attributes[0]; Debug.Assert(attribute != null); MethodInfo method = null; // Ensure the Type on the attribute is in the same assembly as the attribute itself if (attribute.Type != null && !String.IsNullOrEmpty(attribute.MethodName) && attribute.Type.Assembly == assembly) { method = FindPreStartInitMethod(attribute.Type, attribute.MethodName); } if (method != null) { methods.Add(method); } else { throw new HttpException(SR.GetString(SR.Invalid_PreApplicationStartMethodAttribute_value, assembly.FullName, (attribute.Type != null ? attribute.Type.FullName : String.Empty), attribute.MethodName)); } } } return methods; }
本欄目