MVC之前的那點事兒系列(7):WebActivator的實現原理詳解
文章內容
上篇文章,我們分析如何動態註冊HttpModule的實現,本篇我們來分析一下通過上篇程式碼原理實現的WebActivator類庫,WebActivator提供了3種功能,允許我們分別在HttpApplication初始化之前,之後以及ShutDown的時候分別執行指定的程式碼,示例如下:
[assembly: WebActivator.PreApplicationStartMethod(typeof(A.InitClass1), "PreStart")] [assembly: WebActivator.PostApplicationStartMethod(typeof(A.InitClass1), "PostStart")] [assembly: WebActivator.ApplicationShutdownMethod(typeof(A.InitClass1), "ShutDown")]
另外還有一點和系統自帶的PreApplicationStartMethodAttribute不同的是,WebActivator的每種特性都可以使用多次,比如:
[assembly: WebActivator.PreApplicationStartMethod(typeof(A.InitClass1), "PreStart")] [assembly: WebActivator.PreApplicationStartMethod(typeof(A.InitClass2), "PreStart")] [assembly: WebActivator.PreApplicationStartMethod(typeof(A.InitClass3), "PreStart")]
因為它的原始碼很少,所以今天我們就來全面分析一下WebActivator的實現原理,首先下載WebActivator的最新1.5原始碼,原始碼地址:https://bitbucket.org/davidebbo/webactivator/src
解壓程式碼,我們可以看到WebActivator專案裡總共有6個重要的cs檔案,以及一個packages.config檔案(用於標記本專案引用了Microsoft.Web.Infrastructure.dll類庫),下面我們來分析一下每個檔案的原始碼。
3個XXXMethodAttribute屬性:
根據上面的用法,我們指導WebActivator提供了3個MethodAttribute,我們先來看看這3個檔案都是如何實現的,查閱程式碼發現3個類(PreApplicationStartMethodAttribute/ PostApplicationStartMethodAttribute/ ApplicationShutdownMethodAttribute)的內容都是一樣的,都是繼承於BaseActivationMethodAttribute類,然後提供建構函式所需要的Type型別和方法名稱, 3個特性類都支援使用多次並且只能用於Assembly,程式碼如下:
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
通用的基類BaseActivationMethodAttribute:
using System; using System.Reflection; namespace WebActivator { // Base class of all the activation attributes [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] public abstract class BaseActivationMethodAttribute : Attribute { private Type _type; private string _methodName; public BaseActivationMethodAttribute(Type type, string methodName) { _type = type; _methodName = methodName; } public Type Type { get { return _type; } } public string MethodName { get { return _methodName; } } public int Order { get; set; } public void InvokeMethod() { // Get the method MethodInfo method = Type.GetMethod( MethodName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); if (method == null) { throw new ArgumentException( String.Format("The type {0} doesn't have a static method named {1}", Type, MethodName)); } // Invoke it method.Invoke(null, null); } } }
通過程式碼,我們首先可以看到,除了Type和MethodName以外,還多了一個Order屬性,用來標記多次使用同一個Attribute的時候的執行順序。然後提供了一個InvokeMethod方法,用來執行該類裡傳入當前Type類的MethodName靜態方法。
Assembly擴充套件方法AssemblyExtensions:
using System.Collections.Generic; using System.Linq; using System.Reflection; namespace WebActivator { static class AssemblyExtensions { // Return all the attributes of a given type from an assembly public static IEnumerable<T> GetActivationAttributes<T>(this Assembly assembly) where T : BaseActivationMethodAttribute { return assembly.GetCustomAttributes( typeof(T), inherit: false).OfType<T>(); } } }
該擴充套件方法主要是用於獲取某一個程式集Assembly下指定型別的所有Attribute(並且不包括繼承的類),也就是查詢上述3種特性的Attributes(因為每種都允許宣告多次)。
主管理類ActivationManager:
該類主要分為如下幾個部分:
1 私有靜態函式Assemblies, GetAssemblyFiles主要是獲取當前應用程式下的所有DLL程式集,以供其它方法從這個程式集集合裡遍歷相應的特性宣告。
// 載入所有獲取的程式集 private static IEnumerable<Assembly> Assemblies { get { if (_assemblies == null) { // Cache the list of relevant assemblies, since we need it for both Pre and Post _assemblies = new List<Assembly>(); foreach (var assemblyFile in GetAssemblyFiles()) { try { // Ignore assemblies we can't load. They could be native, etc... _assemblies.Add(Assembly.LoadFrom(assemblyFile)); } catch { } } } return _assemblies; } } // 獲取程式集檔案路徑集合 private static IEnumerable<string> GetAssemblyFiles() { // When running under ASP.NET, find assemblies in the bin folder. // Outside of ASP.NET, use whatever folder WebActivator itself is in string directory = HostingEnvironment.IsHosted ? HttpRuntime.BinDirectory : Path.GetDirectoryName(typeof(ActivationManager).Assembly.Location); return Directory.GetFiles(directory, "*.dll"); }
2 獲取所有AppCode資料夾下程式碼編譯後的程式集。
// Return all the App_Code assemblies private static IEnumerable<Assembly> AppCodeAssemblies { get { // Return an empty list if we;re not hosted or there aren't any if (!HostingEnvironment.IsHosted || !_hasInited || BuildManager.CodeAssemblies == null) { return Enumerable.Empty<Assembly>(); } return BuildManager.CodeAssemblies.OfType<Assembly>(); } }
3 執行3種特性裡所指定的方法
public static void RunPreStartMethods() { RunActivationMethods<PreApplicationStartMethodAttribute>(); } public static void RunPostStartMethods() { RunActivationMethods<PostApplicationStartMethodAttribute>(); } public static void RunShutdownMethods() { RunActivationMethods<ApplicationShutdownMethodAttribute>(); } // Call the relevant activation method from all assemblies private static void RunActivationMethods<T>() where T : BaseActivationMethodAttribute { foreach (var assembly in Assemblies.Concat(AppCodeAssemblies)) { foreach (BaseActivationMethodAttribute activationAttrib in assembly.GetActivationAttributes<T>().OrderBy(att => att.Order)) { activationAttrib.InvokeMethod(); } } }
從程式碼可以看出,3個特性執行方法呼叫的都是同一個泛型方法RunActivationMethods<T>,在這個方法裡,主要是從所有的程式集裡,通過泛型方法查詢所有標記的特性(按Order排序),並且執行每個特性聲明裡指定的方法。另外從Assemblies.Concat(AppCodeAssemblies)可以發現,所有的程式集還要包括App_Code目錄下程式碼編譯的程式集哦。
4 自定義HttpModule
class StartMethodCallingModule : IHttpModule { private static object _lock = new object(); private static int _initializedModuleCount; public void Init(HttpApplication context) { lock (_lock) { // Keep track of the number of modules initialized and // make sure we only call the post start methods once per app domain if (_initializedModuleCount++ == 0) { RunPostStartMethods(); } } } public void Dispose() { lock (_lock) { // Call the shutdown methods when the last module is disposed if (--_initializedModuleCount == 0) { RunShutdownMethods(); } } } }
該Module主要是用於在 Init的時候執行PostStart型別的方法,並且在Dispose的時候執行Shutdown型別的方法,並且只執行一次。
5.最重要的入口方法
public static void Run() { if (!_hasInited) { RunPreStartMethods(); // Register our module to handle any Post Start methods. But outside of ASP.NET, just run them now if (HostingEnvironment.IsHosted) { Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(StartMethodCallingModule)); } else { RunPostStartMethods(); } _hasInited = true; } }
Run方法看起來很容易理解了,首先執行PreStart型別的方法,然後判斷HostingEnvironment是否Host成功,如果成功就動態註冊我們上面自定義的HttpModule,以便讓該Module在HttpApplication初始化和Dispose的時候分別執行PostStart型別的方法和ShutDown型別的方法,如果沒有Host成功,那隻執行PostStart型別的方法。
注:由程式碼實現可以看出,在PreStart型別的方法裡,不能使用HttpContext物件進行輸入輸出,因為該物件在此時還沒用建立成功呢。
6.誰呼叫了入口方法Run()
這個就不用多說了吧,肯定是使用.Net4.0自帶的PreApplicationStartMethodAttribute特性,程式碼如下:
[assembly: PreApplicationStartMethod(typeof(WebActivator.ActivationManager), "Run")]
你可以讓這段程式碼放在WebActivator專案裡任何類檔案的namespace外部,但為了統一起見,一般都是放在Properties目錄下的AssemblyInfo類檔案裡,WebActivator就是這麼做的。
總結,好了,這就是WebActivator的全部原始碼,實現起來其實很簡單,對吧?那以後專案再有類似需求的時候,就大膽使用這個類庫吧,另外NInject.MVC也是基於這個類庫來實現的。
參考資料:
同步與推薦
MVC之前的那點事兒系列文章,包括了原創,翻譯,轉載等各型別的文章,如果對你有用,請推薦支援一把,給大叔寫作的動力。
相關推薦
MVC之前的那點事兒系列(7):WebActivator的實現原理詳解
文章內容 上篇文章,我們分析如何動態註冊HttpModule的實現,本篇我們來分析一下通過上篇程式碼原理實現的WebActivator類庫,WebActivator提供了3種功能,允許我們分別在HttpApplication初始化之前,之後以及ShutDown的時候分別執行指定的程式碼,示例如下: [
MVC之前的那點事兒系列(8):UrlRouting的理解
文章內容 根據對Http Runtime和Http Pipeline的分析,我們知道一個ASP.NET應用程式可以有多個HttpModuel,但是隻能有一個HttpHandler,並且通過這個HttpHandler的BeginProcessRequest(或ProcessRequest)來處理並返回請求,前
MVC之前的那點事兒系列(9):MVC如何在Pipeline中接管請求的?
文章內容 上個章節我們講到了,可以在HttpModules初始化之前動態新增Route的方式來自定義自己的HttpHandler,最終接管請求的,那MVC是這麼實現的麼?本章節我們就來分析一下相關的MVC原始碼來驗證一下我們的這個問題。 先建立一個MVC3的Web Application,選擇預設的模
MVC之前的那點事兒系列(3):HttpRuntime詳解分析(下)
文章內容 話說,經過各種各樣複雜的我們不知道的內部處理,非託管程式碼正式開始呼叫ISPAIRuntime的ProcessRequest方法了(ISPAIRuntime繼承了IISPAIRuntime介面,該介面可以和COM進行互動,並且暴露了ProcessRequest介面方法)。至於為什麼要呼叫這個方法,
MVC之前的那點事兒系列(4):Http Pipeline詳細分析(上)
文章內容 繼續上一章節的內容,通過HttpApplicationFactory的GetApplicationInstance靜態方法獲取例項,然後執行該例項的BeginProcessRequest方法進行執行餘下的Http Pipeline 操作,程式碼如下: // Get application i
MVC之前的那點事兒系列(5):Http Pipeline詳細分析(下)
文章內容 接上面的章節,我們這篇要講解的是Pipeline是執行的各種事件,我們知道,在自定義的HttpModule的Init方法裡,我們可以新增自己的事件,比如如下程式碼: public class Test : IHttpModule { public void Init(HttpAp
MVC之前的那點事兒系列(2):HttpRuntime詳解分析(上)
文章內容 從上章文章都知道,asp.net是執行在HttpRuntime裡的,但是從CLR如何進入HttpRuntime的,可能大家都不太清晰。本章節就是通過深入分析.Net4的原始碼來展示其中的重要步驟。請先看下圖: 首先,CLR在初始化載入的時候,會載入一個非常重要的類AppManagerApp
MVC之前的那點事兒系列(6):動態註冊HttpModule
文章內容 通過前面的章節,我們知道HttpApplication在初始化的時候會初始化所有配置檔案裡註冊的HttpModules,那麼有一個疑問,能否初始化之前動態載入HttpModule,而不是隻從Web.config裡讀取? 答案是肯定的, ASP.NET MVC3釋出的時候提供了一個Microsof
MVC之前的那點事兒系列(10):MVC為什麼不再需要註冊萬用字元(*.*)了?
文章內容 很多教程裡都提到了,在部署MVC程式的時候要配置萬用字元對映(或者是*.mvc)到aspnet_ISPAI.dll上,在.NET4.0之前確實應該這麼多,但是.NET4.0之後已經不要再費事了,因為它預設就支援了。 你可以會問,沒有對映配置,請求這麼可能會走到aspnet_ISPAI.dll
MVC之前的那點事兒系列(1):進入CLR
MVC之前的那點事兒系列,是筆者在2012年初閱讀MVC3原始碼的時候整理的,主要講述的是從HTTP請求道進入MVCHandler之前的內容,包括了原創,翻譯,轉載,整理等各型別文章,當然也參考了部落格園多位大牛的文章,對此表示感謝,這次有時間貼出來,希望對大家有用。 主要內容 本文講解的是:伺服器接受H
Quartz.Net系列(七):Trigger之SimpleScheduleBuilder詳解
所有方法圖 SimpleScheduleBuilder方法 RepeatForever:指定觸發器將無限期重複。 WithRepeatCount:指定重複次數 var trigger = TriggerBuilder.Create().WithSimpleSchedule(s=&
Quartz.Net系列(九):Trigger之DailyTimeIntervalScheduleBuilder詳解
1.介紹 中文意義就是每日時間間隔計劃生成 2.API講解 (1)WithInterval、WithIntervalInHours、WithIntervalInMinutes、WithIntervalInSeconds WithInterval:指定要生成觸發器的時間單位和間隔。 WithIntervalIn
arcgis jsapi介面入門系列(7):滑鼠在地圖畫線
初始化,每個map執行一次就行 drawPolylineInit: function () { //畫幾何物件初始化 //新建一個圖形圖層用於存放畫圖過程中的圖形 let layer = new t
python快速學習系列(7):迭代器
迭代器協議 1.迭代器協議: ·迭代器是一個物件 ·迭代器可以被next()函式呼叫,並返回一個值 ·迭代器可以被iter()函式呼叫,並返回迭代器自己 ·連續被next()呼叫時返回一系列的值 ·如果到了迭代的末尾,則丟擲StopIteration異常 ·迭代器也可以沒有末尾,只要被nex
解讀ASP.NET 5 & MVC6系列(7):依賴注入
在前面的章節(Middleware章節)中,我們提到了依賴注入功能(Dependency Injection),ASP.NET 5正式將依賴注入進行了全功能的實現,以便開發人員能夠開發更具彈性的元件程式,MVC6也利用了依賴注入的功能重新對Controller和View的服務注入功能進行了重新設計;未來的依賴
ABP入門系列(7)——分頁實現
完成了任務清單的增刪改查,咱們來講一講必不可少的的分頁功能。 首先很慶幸ABP已經幫我們封裝了分頁實現,實在是貼心啊。 來來來,這一節咱們就來捋一捋如何使用ABP的進行分頁吧。 一、分頁請求DTO定義 資料傳輸物件(Data Transfer Objects)用於應用層和展現層的資料傳輸。 展現層傳入資料
springCloud(7):Ribbon實現客戶端側負載均衡-消費者整合Ribbon
spring cloud ribbon 消費者整合ribbon 一、簡介 Ribbon是Netfix發布的負載均衡器,它有助於控制HTTP和TCP客戶端的行為。為Ribbon配置服務提供者地址列表後,Ribbon就可基於某種負載均衡算法,自動地幫助服務消費者去請求。Ribbon默認為我們提供了很
WebAssembly 系列(四):WebAssembly 工作原理
WebAssembly 是除了 JavaScript 以外,另一種可以在網頁中執行的程式語言。過去如果你想在瀏覽器中執行程式碼來對網頁中各種元素進行控制,只有 JavaScript 這一種選擇。 所以當人們談論 WebAssembly 的時候,往往會拿 JavaScript 來進行比較。但
Tensorflow入門系列(四)--tutorials/image/mnist程式詳解
mnist官方程式詳解 在之前的文章中,我們在github上clone了TensorFlow/model這一個專案,這一次讓我們一起來看一下其下tutorials/image/mnist的程式。 首先,讓我們從程式的起始點開始看起。 parser =
python網路爬蟲(7)爬取靜態資料詳解
目的 爬取http://seputu.com/資料並存儲csv檔案 匯入庫 lxml用於解析解析網頁HTML等原始碼,提取資料。一些參考:https://www.cnblogs.com/zhangxinqi/p/9210211.html requests請求網頁 chardet用於判斷網頁中的字元編