【框架學習與探究之依賴注入--Autofac】

聲明

本文歡迎轉載,原文地址:http://www.cnblogs.com/DjlNet/p/7603642.htmlhtml


一樣的又是一個雙11如期而至,淘寶/天貓實時數據顯示,開場3分鐘總交易額突破100億元人民幣,簡直可怕!同時產生了新的支付峯值誕生:25.6萬筆/秒,以及數據庫處理峯值4200萬次/秒,說這些不是再給某某打廣告哈,只是感嘆現在的技術和業務雙向驅動所帶來的巨大沖擊力,完成了史上基本不可能的事情,相信這毫不是極限,由於中國女人的支付能力是木有上限一說的,開玩笑啦,哈哈哈,好了廢話說了一通,趁着博主也完成剁手以後,而後趕忙回到了繼續學習的旅途中......git


前言

當看到這文章題目的時候,相信不少人都是感嘆,怎麼又是一個來說依賴注入,怎麼又是autofac淺析之類的,因此咯,此文只是博主閱讀文檔和平時的經驗整合,而後吶,懂的人或者熟悉的中高配玩家,就能夠繞道了,╮(╯﹏╰)╭,其實博主以前的我的項目仍是公司項目都是採用 Unity 來做爲 ioc 套件進行使用,使用過程也還算良好也是基本具有了一個ioc組件應該具備的價值,或許也是微軟企業套件爲數剩下來很少的好的組件了吧,可是無奈後期博主以爲Unity在性能或者靈活性上面逐漸展現出來的欠缺性,相比於今兒咱們熟知的autofac而言就越顯尷尬,固然兩者的官方文檔都還算字字走心還算詳細,可是重要的一點就是autofac在社區的熱度以及地位就比較高了,其次就是它的周邊擴展也是至關多的支持,固然啦,從一個系統或者項目俯視來看,它或者誰都是系統組件的一個冰山一角,都是做爲一個零件使用,可是咱們依然不能就此忽略其的地位,由於它算是在一個系統中處於貫穿的層次級別,因此在對象建立和依賴+生命週期統籌上面起到了決定性做用,因此各位同窗固然也得須要重點關注如何集成纔是......github


DIP、Ioc和DI概念(理解)

首先按照國際慣例給出各自的wiki,Dependency Injection (DI) Wiki : https://en.wikipedia.org/wiki/Dependency_injection ;Inversion of control (IOC) Wike : https://en.wikipedia.org/wiki/Inversion_of_control ; 這裏給出的是英文版解釋,固然英語能保留原滋原味,不過我相信中文的解釋更能讓人接受一些,因此這裏博主依然找出了中文相關的通俗解釋,這裏引用一篇好文當中的話(原文連接:http://www.cnblogs.com/liuhaorain/p/3747470.html);
一、依賴倒置原則(DIP):一種軟件架構設計的原則(抽象概念)。
二、控制反轉(IoC):一種反轉流、依賴和接口的方式(DIP的具體實現方式)。
三、依賴注入(DI):IoC的一種實現方式,用來反轉依賴(IoC的具體實現方式)。
四、IoC容器:依賴注入的框架,用來映射依賴,管理對象建立和生存週期(DI框架)。
至於每一個概念的解讀到理解以及示例展現,就能夠看上文給出的鏈接,文中的解析挺到位的,因此這裏備份連接地址在文中惠存,文中有相關的通俗解釋,能夠加深影響後加以理解,內化之....web


Autofac文檔摘取

固然也是由於 autofac 自己文檔完備性,相信也是大部分玩家選擇用它的緣由之一,簡直感受加一塊兒來就是一本薄的書了哈,因此這裏博主前後閱讀了兩三遍之,採用了一遍蜻蜓點水有個印象,二遍擇優而讀加深理解,三遍查漏補缺,可能不少人都以爲哪有這麼麻煩,先用起來到時候有問題有不懂的地方再去翻查文檔便可,當時候博主也是準備這樣的來的,其實方式並無好壞一說,只是看我的喜愛和學習方法而已,博主打算在完成此框架的學習以後,就着手與案例分析和框架集成的工做了,其實先前早些時候已經嘗試過了對通用開發框架的一些實踐,不過如今看看有些愣之嫩之,因此在此對開發當中需求的框架基本過一遍以後心中有譜兒以後,再次作集成工做就會顯得「穩」一些,上述是博主本身的想法......2333333數據庫


關於註冊那點事兒

看來都是經過一個主要對象ContainerBuilder完成對各式各樣的組件註冊,且經過名字也能夠知道此類是生成服務容器的生成器在生成容器以前,天然就是註冊各類相關組件或者服務啦,關於API註冊相關的話,大體有如下相關主要方式註冊,不限於此:
一、經過類型註冊( Register by Type ),相似:builder.RegisterType ();builder.RegisterType(typeof(NLogLogger)); ,同時這裏當使用該註冊件時,會經過反射實例化對象,且會選擇最合適的構造函數使用會自動傳遞依賴項進去,固然官方也提供了builder.RegisterType ().UsingConstructor(typeof(ILogger), typeof(IConfigReader)); 指定構造函數使用,這裏最合適體如今該組件的依賴項在容器的是否存在而定json

二、實例對象註冊( Instance Components ),Demo代碼所示:var output = new StringWriter();builder.RegisterInstance(output).As ().ExternallyOwned(); , 其中方法 ExternallyOwned 能夠忽略容器對 實例對象的釋放控制,同時也能夠註冊 單例對象 使用,且須要注意註冊單例實例對象釋放應該由開發人員控制而不是容器 性能優化

三、Lambda 表達式組件註冊 ( Lambda Expression Components ),builder.Register(c => new A(c.Resolve()));,其中表達式的靈活性和性能優化,是官方推薦的註冊方式,分別體如今 利用表達式能夠自定義解析依賴項和傳遞須要的構造函數( 也包含註冊依賴的屬性和根據參數自定義註冊流程 )等等,其次就是表達式對於性能有必定的提高相對於反射而言。架構

四、泛型組件註冊 (Open Generic Components):框架

builder.RegisterGeneric(typeof(NHibernateRepository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();

這個即是咱們常用的API了,也就是咱們對倉儲服務的註冊,可是咱們實體有不少,因此這裏就 須要泛型註冊能夠批量注入到容器中去,並且須要單獨對某一個具體類型例如 NHibernateRepository 註冊就會覆蓋泛型的注入,同理解析服務的時候也會實例具體對象的服務實例。 分佈式

其餘備註注意事項:
關於 組件與服務 的注意點:當一個組件以下注冊爲服務的時候 :

builder.RegisterType<CallLogger>().As<ILogger>().As<ICallInterceptor>();

若是 須要解析組件自己須要加上AsSelf() ,關鍵的 AsSelf 來表示,否則註冊爲服務會覆蓋掉組件自己的註冊結果。
默認註冊狀況下, 多個組件註冊同一個接口,以最後一個爲準,除非使用 Named 支持多組件同一個服務註冊關係,且若是使用API:PreserveExistingDefaults ,能夠是的第一個註冊爲默認項。
條件註冊:主要API,OnlyIf 方法提供Lambda表達式表示指定條件下發生,IfNotRegistered 方法同理註定沒註冊條件下發生。
參數傳遞給組件做爲註冊需求 ,經過 表達式註冊或者顯示使用 WithParameter 方法傳遞便可,一樣也能夠在解析的傳遞參數也是能夠的。
屬性註冊:經過表達式或者利用OnActivated事件來主動解析,若是註冊的是反射組件須要使用 PropertiesAutowired 來突出屬性的注入,一樣提供了 WithProperty 來主動注入對應屬性名的依賴項。
程序集掃描註冊:RegisterAssemblyTypes 能夠自定義掃描程序集邏輯指定類型註冊,可使用 Except 排除不須要的類型,AsImplementedInterfaces 註冊爲類型對應的公開接口服務類型,RegisterAssemblyModules 註冊自定義module,實現批量套件式註冊,注意若是宿主程序使用iis,若是使用iis回收,觸發程序集重載,須要使用 var assemblies = BuildManager.GetReferencedAssemblies().Cast (); 獲取最新加載的程序集掃描


關於解析那點事兒

解析主要的方法即是 Resolve ,解析能夠經過 NamedParameter 、TypedParameter 、ResolvedParameter 三種方式傳遞解析組件所須要的參數依賴,關於容器中的不一樣關係類型的不一樣解析方式有所不一樣,具體參考:http://autofac.readthedocs.io/en/latest/resolve/relationships.html#supported-relationship-types,其實官方提供這麼多重載或者API函數只是爲了廣大玩家的易用性,至於平時開發過程當中,使用的API數量相對來講仍是有限的,那博主自身狀況設定,直接依賴和延遲實例化對象用到過了,還有一點:關於可枚舉對象服務組件的註冊,也就是一個接口多個實現類,解析接口默認若是沒有註冊回拋出異常,可是以下方式則是爲empty,scope.Resolve<IEnumerable >();


控制做用域和組件存活生命週期

根據官方的說法,其中有兩點是值得開發人員注意的:
一、生命週期範圍是可嵌套的,它們控制組件如何共享。例如,單例服務可能從根生存期範圍解決,而單個工做單元可能須要本身的其餘服務實例,你能夠經過在註冊時設置其實例範圍來肯定組件的共享方式。例如 UserService 須要 RoleRepository 須要它們的scope處於處於同一個層次
二、using scope會主動追蹤範圍內組件,並在釋放範圍內組件在scope結束的時候。例如,若是您有一個實現了IDisposable的組件,而且您能夠從scope範圍內解析出來,則該scope範圍將爲你處理該對象的釋放工做,自動處理讓使用者沒必要知道底層實現。可是若是不須要自動管理,你能夠控制此行爲或添加新的處置行爲。

始終從scope範圍內解析服務而非根容器,這一點很重要。因爲生命週期範圍的跟蹤特性,若是您從跟容器中解析了大量的一次性組件,則可能會無心中形成內存泄漏,由於根容器將持有對那些一次性組件的引用(一般是應用程序的生命週期)。若是Autofac檢測到單例或共享組件的使用狀況,它會自動將其放入適當的跟蹤範圍。因此上訴的狀況,能夠理解爲儘可能使用自建scope來解析服務組件,而跟容器就應該註冊相似單例服務,與容器和應用程序本屬於一個生命週期的東西

官方例子很好地說明了,在一個web應用程序中應該如何分配組件註冊的生命週期的選擇問題:
如今應用程序具備一個全局單例日誌記錄服務,而後兩個同時請求進入Web應用程序,每一個請求都是一個邏輯的「工做單元」,每一個請求都須要本身的訂單處理服務,每一個訂單處理服務都須要將信息記錄到日誌服務中。在這種狀況下,擁有包含單例記錄服務的根生存期範圍,而且每一個請求都有一個子生命週期範圍child scope,每一個範圍都有本身的訂單處理服務:

+---------------------------------------------------+
|                 Autofac Container                 |
|                Root Lifetime Scope                |
|                                                   |
|                  Logging Service                  |
|            (shared across all requests)           |
|                                                   |
| +----------------------+ +----------------------+ |
| |  First Request Scope | | Second Request Scope | |
| |                      | |                      | |
| |   Order Processor    | |   Order Processor    | |
| +----------------------+ +----------------------+ |
+---------------------------------------------------+

當每一個請求結束時,請求生存期範圍結束,相應的訂單處理器被釋放(若是實現了IDisposable)。Log日誌記錄服務做爲一個單例,在未來的請求中保持共享。

關於具體註冊的時候可供選擇的實例生命週期範圍,默認狀況下是使用的:InstancePerDependency 也就是瞬態,每次解析都是一個新的對象,SingleInstance 單例很少說,InstancePerLifetimeScope scope範圍內共享實例,經過 BeginLifetimeScope 便可創建一個scope範圍,InstancePerMatchingLifetimeScope 指定具體name的scope範圍內實例對象共享,其中pre request scope就是利用這道理實現了每次請求一個獨立的做用域,InstancePerRequest web應用單次請求組件單例,注意若是木有請求的環境下,解析組件會拋出異常,InstancePerOwned 基本用不到,Thread Scope 參照線程啓動的時候構建InstancePerLifetimeScope類型的做用範圍曲線救國就能夠了

關於服務依賴致使各自依賴項長期存活問題,通常狀況下,應用程序中,上層服務組件所須要依賴的下層服務的生命週期要小於等於上層服務的生命週期便可,這樣就能夠正確的表示依賴關係和隸屬關係,可是得除開單例狀況,由於它是貫穿了整個應用生命週期的存在。

關於組件對象的釋放問題,通常的ioc容器包括autofac都已經實現了自動處理釋放問題,根據不一樣的註冊生命週期選擇,則會自動釋放,不須要開發者關係,對象殘留的問題。如須要手動釋放其餘自定義非託管資源能夠利用 OnRelease event來自定義處理邏輯,注意這會覆蓋base邏輯,因此若是自己組件實現了idisposable就要先調用base.Diposable()方法


註冊配置來源

這裏博主仍是推薦代碼註冊,由於配置文件的管理也是挺麻煩的,除非須要再上線以後須要動態調整一下注冊參數或者生命週期的選擇等等,那就須要xml json等註冊方式,通常狀況不會自定註冊源,且autofac module 的方法很好劃分了大量組件分類註冊的問題。


各類應用程序集成

這裏直接複製粘貼官方對每一個應用的demo代碼便可,思路都是替換既有系統的默認的註冊依賴組價,基本上已經足夠咱們的使用,到時開發的時候直接參考sample便可
這裏官方給出了最佳實踐的一些開發方式或者注意點:http://autofac.readthedocs.io/en/latest/best-practices/index.html 我這裏就不翻譯,相信你們都看得懂


小總結

原本這篇水文在早些時候都應該要完成的,拖拖踏踏到如今,連廣州都已經也到了冬天了,好冷......,還好有防寒服加持,至此,差很少主要博主想研究的框架都差很少了,至於說net core相關的也在偶爾持續關於 github issues 的動態,畢竟官方給了road map ,跪着也要實現不是嘛!接下來,博主就要基於實際應用開發集成各式組件,構建屬於本身的單體應用架構,畢竟走向分佈式的前提是,單體架構不夠使用的狀況下,固然後面設計的時候會相對引入一些ddd的相關思想,但並不是所有,由於要基於顯示考慮問題纔是嘛......未完待續!

相關文章
相關標籤/搜索