在開始之前首先解釋一下我認為的依賴註入和控制反轉的意思。(新手理解,哪裡說得不正確還請指正和見諒) 控制反轉:我們向IOC容器發出獲取一個對象實例的一個請求,IOC容器便把這個對象實例“註入”到我們的手中,在這個時候我們不是一個創建者,我們是以一個請求者的身份去請求容器給我們這個對象實例。我們所有的 ...
在開始之前首先解釋一下我認為的依賴註入和控制反轉的意思。(新手理解,哪裡說得不正確還請指正和見諒)
控制反轉:我們向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實例。
當然我沒也可以阻止這個行為使用PreserveExistingDefaults()方法:
//註冊對象 builder.RegisterType<Service>().As<IService>(); builder.RegisterType<ServiceTwo>().As<IService>().PreserveExistingDefaults();
再次運行就不會覆蓋:
屬性註入
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(); } }
運行看效果: