【AutoFac】依賴注入和控制反轉的使用
在開始之前首先解釋一下我認為的依賴注入和控制反轉的意思。(新手理解,哪裡說得不正確還請指正和見諒)
控制反轉:我們向IOC容器發出獲取一個物件例項的一個請求,IOC容器便把這個物件例項“注入”到我們的手中,在這個時候我們不是一個建立者,我們是以一個請求者的身份去請求容器給我們這個物件例項。我們所有的物件依賴於容器提供給你的資源,控制權落到了容器身上。在這裡的身份轉化或許就是控制反轉的核心吧。
依賴注入:我們向容器發出請求以後,獲得這個物件例項的過程就叫依賴注入。也就是我們在使用對向前我們都需要先注入也就是這個意思吧。
今天學習了下AutoFac依賴注入這個外掛,然後還有以前用過的Unity這個外掛簡單做個筆記整理。首先我分兩個部分記錄,第一部分一點有點的記錄今天學習的AutoFac這個外掛,最後一部分直接補上以前使用Unity外掛的程式碼封裝不做詳細解釋。
AutoFac入門
還是放上官網給出的整合流程吧;
- 按照 控制反轉 (IoC) 的思想構建你的應用.
- 新增Autofac引用.
- 在應用的 startup 處...
- 建立 ContainerBuilder.
- 註冊元件.
- 建立容器,將其儲存以備後續使用.
- 應用執行階段...
- 從容器中建立一個生命週期.
- 在此生命週期作用域內解析元件例項.
建立簡單的例子
通過一個控制檯簡單清晰的介紹瞭如何使用AutoFac這個外掛。
建立專案
建立一個控制檯程式叫AutoFacDome。
這裡就不做過多解釋了,大家都會建立哈哈。
引用Autofac
使用我vs強大的nuget來進行新增引用:
直接在搜尋欄輸入Autofac直接就可以查找出來:
Nuget命令列:
Install-Package Autofac -Version 4.8.1
建立一個依賴關係類:
首先我們定義一個服務介面=>IService
/// <summary> /// 服務介面 /// 描述:為了方法的繼承和擴充套件更好的演示下面的例子 /// </summary> public interface IService { //定義一個輸出方法 void PrintWord(); }
然後我們在建立一個服務類去實現介面(IService)=>Service
/// <summary> /// 服務類具體實現 /// </summary> public class Service : IService { //輸出方法的實現 public void PrintWord() { Console.WriteLine("我是service列印的Hello word"); } }
好了現在我們有了一個服務介面和一個服務類 ,並且類下面實現了輸出會列印:我是service列印的Hello word。常規我們想呼叫這個方法我們都是在mian函式中示例化該類進行呼叫,類似於這樣
IService service = new Service(); service.PrintWord(); //或者 Service service2 = new Service(); service2.PrintWord();
但是今天我們說的不是這些我們說的另外的方式。那我們看看 autofac是怎麼呼叫的。
註冊容器
其實萬變不離其宗,不管是autofac,unity,spring.net等,其實都是這麼一個套路就是先註冊容器然後才能從容器取出,其實這個也非常好理解容器本身是沒有東西的,你想用東西就要提前放進去,只有容器有了你請求才會給你。不同的外掛只不過是各自的封裝方法或者形式存在著差異。autofac的註冊方式:
// 建立容器 var builder = new ContainerBuilder(); //註冊物件 builder.RegisterType<Service>().As<IService>(); Container = builder.Build();
這個時候我們就相當於把service類放入了容器,這樣在後面你才可以取出來使用。
使用容器
這裡我寫了兩個使用的方法根據不同情況使用把:
//使用方法一 using (var ioc = Container.BeginLifetimeScope()) { var service = ioc.Resolve<IService>(); service.PrintWord(); } //使用方法二 //var service = Container.Resolve<IService>(); //service.PrintWord();
執行
我們可以任意註釋一個方法來檢測一下結果:
這樣我們就完成了autofac的簡單運用。
AutoFac新手村
通過上面的例子我們已經知道autofac的基本運用。基本運用還不行我們還要知道一些知識。
多建構函式
第一種:就是單純的有多個構造
如果我們同一個類存在多個建構函式會給我們一個什麼結果哪這個在有時候是非常重要的。
所以我們修改我們的service類:
/// <summary> /// 服務類具體實現 /// </summary> public class Service : IService { //預設構造 public Service() { Console.WriteLine("我是service的預設構造"); } //一個引數的構造 public Service(int a) { Console.WriteLine("我是service的一個引數構造"); } //兩個引數的構造 public Service(int a,int b) { Console.WriteLine("我是service的兩個引數構造"); } //輸出方法的實現 public void PrintWord() { Console.WriteLine("我是service列印的Hello word"); } }
其他都不變執行程式碼:
這裡就是執行了預設構造,所有在一個類有多個構造情況下預設的形式是返回給我們預設建構函式的類例項。
第二種:多構造引數型別並且引數型別也註冊容器
這個什麼意思哪就是說有兩個類,其中一個類的建構函式引數是另一個類。並且引數型別也進行註冊容器
我們增加一個ServiceTwo類:
public class ServiceTwo :IService { //輸出方法的實現 public void PrintWord() { Console.WriteLine("我是serviceTwo列印的Hello word"); } }
修改service類中的一個引數構造為:
//一個引數的構造 public Service(ServiceTwo two) { Console.WriteLine("我是service的一個引數構造"); }
main函式增加註冊:
//註冊物件 builder.RegisterType<Service>().As<IService>(); builder.RegisterType<ServiceTwo>(); Container = builder.Build();
然後執行:
這裡就和上面的結果不一樣了,所有在使用時需要注意,autofac官方解釋為:當使用基於反射的元件時, Autofac 自動為你的類從容器中尋找匹配擁有最多引數的構造方法。
說白了就是如果使用註冊類並且註冊類多建構函式,並且其構造引數為其他註冊類時候,查詢的建構函式包含註冊類最多的建構函式返回。
指定建構函式
由容器掌握我們的建構函式總是不好的,所有我們要自己指定想建立誰建立誰=>UsingConstructor(引數型別)可以多個
在這裡需要注意我們既然指定了建構函式就要為建構函式傳參不然會抱錯,引數可以是註冊時候傳也可以解析時候傳,我寫了一個解析時候傳的:
// 建立容器 var builder = new ContainerBuilder(); //註冊物件 builder.RegisterType<Service>().As<IService>().UsingConstructor(typeof(int), typeof(int)); // builder.RegisterType<ServiceTwo>(); Container = builder.Build(); //使用方法一 using (var ioc= Container.BeginLifetimeScope()) { var service = ioc.Resolve<IService>(new NamedParameter("a", 1), new NamedParameter("b", 1)); service.PrintWord(); }
執行結果:
類的覆蓋
如果我兩個類或者多個類同時實現一個介面並且註冊的時候都與介面做了關聯。
那麼會存在覆蓋現象。
下面我們把main函式改造讓serviceTwo也註冊與IService關聯
// 建立容器 var builder = new ContainerBuilder(); //註冊物件 builder.RegisterType<Service>().As<IService>(); builder.RegisterType<ServiceTwo>().As<IService>(); Container = builder.Build(); //使用方法一 using (var ioc = Container.BeginLifetimeScope()) { var service = ioc.Resolve<IService>(); service.PrintWord(); }
執行結果:
這個時候我們得到的是serviceTwo類的示例。如果改變Service和ServiceTwo的位置就會返回service例項。
屬性注入
WithProperty:繫結一個屬性和他的值
我們給ServiceTwo類新增name屬性並擴充套件一個列印方法:
public string name { get; set; } public void PrintName() { Console.WriteLine($"name屬性:{name}"); }
然後main函式改為
builder.RegisterType<Service>().As<IService>(); builder.RegisterType<ServiceTwo>().WithProperty("name", "張三"); Container = builder.Build(); //使用方法一 using (var ioc = Container.BeginLifetimeScope()) { var service = ioc.Resolve<ServiceTwo>(); service.PrintName(); }
執行結果:
方法注入
OnActivating:方法注入
我們在ServiceTwo類新增設定名稱方法
public void setName() { name = "李四"; }
然後main函式改為:
builder.RegisterType<ServiceTwo>().OnActivating(e=> {
e.Instance.setName();
});
執行結果:
AutoFac整合-MVC
首先建立mvc專案就不過多解釋了。
引用dll:
這裡需要引用兩個dll檔案: Autofac.Mvc5和 Autofac。注意這裡我的是mvc5所以我安裝的Autofac.Mvc5 這個要根據mvc版本做對應不然會報錯。
通過nuget安裝就可以了。
相關類
還是我們的Service類和IService類來做示例演示:
/// <summary> /// 服務介面 /// 描述:為了方法的繼承和擴充套件更好的演示下面的例子 /// </summary> public interface IService { //定義一個輸出方法 void PrintWord(); } /// <summary> /// 服務類具體實現 /// </summary> public class Service : IService { //預設構造 public Service() { Console.WriteLine("我是service的預設構造"); } //輸出方法的實現 public void PrintWord() { System.Diagnostics.Debug.WriteLine("調起了service中的方法"); } }
配置檔案
配置Global檔案,來注入控制器。下面我只做建構函式注入和屬性注入
protected void Application_Start() { var builder = new ContainerBuilder(); // 通過程式集註冊所有控制器和屬性注入 //builder.RegisterControllers(typeof(MvcApplication).Assembly); builder.RegisterControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired(); builder.RegisterType<Service>().As<IService>(); // 將依賴性分解器設定為AutoFac。 var container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); }
控制器如何使用:
開啟home控制器:
public class HomeController : Controller { /// <summary> /// 建構函式注入 /// </summary> /// <param name="serviceClient"></param> public HomeController(IService serviceClient) { this.Service = serviceClient; } public IService Service; /// <summary> /// 屬性注入 /// </summary> /// <returns></returns> // public IService Service2 { get; set; } public ActionResult Index() { //使用方法一 Service.PrintWord(); //Service2.PrintWord(); return View(); } }
執行看效果: