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


聲明

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


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


前言

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


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);
1、依賴倒置原則(DIP):一種軟件架構設計的原則(抽象概念)。
2、控制反轉(IoC):一種反轉流、依賴和接口的方式(DIP的具體實現方式)。
3、依賴注入(DI):IoC的一種實現方式,用來反轉依賴(IoC的具體實現方式)。
4、IoC容器:依賴注入的框架,用來映射依賴,管理對象創建和生存周期(DI框架)。
至於每個概念的解讀到理解以及示例展示,就可以看上文給出的連接,文中的解析挺到位的,所以這里備份鏈接地址在文中惠存,文中有相關的通俗解釋,可以加深影響后加以理解,內化之....


Autofac文檔摘取

當然也是因為 autofac 本身文檔完備性,相信也是大部分玩家選擇用它的原因之一,簡直感覺加一起來就是一本薄的書了哈,所以這里博主先后閱讀了兩三遍之,采用了一遍走馬觀花有個印象,二遍擇優而讀加深理解,三遍查漏補缺,可能很多人都覺得哪有這么麻煩,先用起來到時候有問題有不懂的地方再去翻查文檔即可,當時候博主也是准備這樣的來的,其實方式並沒有好壞一說,只是看個人喜好和學習方法而已,博主打算在完成此框架的學習之后,就着手與案例分析和框架集成的工作了,其實先前早些時候已經嘗試過了對通用開發框架的一些實踐,不過現在看看有些愣之嫩之,所以在此對開發當中需求的框架基本過一遍之后心中有譜兒之后,再次做集成工作就會顯得“穩”一些,上述是博主自己的想法......2333333


關於注冊那點事兒

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

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

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

4、泛型組件注冊 (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 >();


控制作用域和組件存活生命周期

根據官方的說法,其中有兩點是值得開發人員注意的:
1、生命周期范圍是可嵌套的,它們控制組件如何共享。例如,單例服務可能從根生存期范圍解決,而單個工作單元可能需要自己的其他服務實例,你可以通過在注冊時設置其實例范圍來確定組件的共享方式。例如 UserService 需要 RoleRepository 需要它們的scope處於處於同一個層次
2、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的相關思想,但並非全部,因為要基於顯示考慮問題才是嘛......未完待續!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM