通過前面的章節,我們知道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;
}
本欄目