在開始以前首先解釋一下我認爲的依賴注入和控制反轉的意思。(新手理解,哪裏說得不正確還請指正和見諒)html
控制反轉:咱們向IOC容器發出獲取一個對象實例的一個請求,IOC容器便把這個對象實例「注入」到咱們的手中,在這個時候咱們不是一個建立者,咱們是以一個請求者的身份去請求容器給咱們這個對象實例。咱們全部的對象依賴於容器提供給你的資源,控制權落到了容器身上。在這裏的身份轉化或許就是控制反轉的核心吧。 spring
依賴注入:咱們向容器發出請求之後,得到這個對象實例的過程就叫依賴注入。也就是咱們在使用對象前咱們都須要先注入也就是這個意思吧。mvc
今天學習了下AutoFac依賴注入這個插件,而後還有之前用過的Unity這個插件簡單作個筆記整理。首先我分兩個部分記錄,第一部分一點有點的記錄今天學習的AutoFac這個插件,最後一部分直接補上之前使用Unity插件的代碼封裝不作詳細解釋。由於本篇幅寫完有點多就單獨寫一下UnIty。函數
Unity地址:【Unity】微軟的一款依賴注入組件post
仍是放上官網給出的整合流程吧;學習
經過一個控制檯簡單清晰的介紹瞭如何使用AutoFac這個插件。ui
建立一個控制檯程序叫AutoFacDome。this
這裏就不作過多解釋了,你們都會建立哈哈。url
使用我vs強大的nuget來進行添加引用:spa
直接在搜索欄輸入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的基本運用。基本運用還不行咱們還要知道一些知識。
第一種:就是單純的有多個構造
若是咱們同一個類存在多個構造函數會給咱們一個什麼結果哪這個在有時候是很是重要的。
因此咱們修改咱們的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();
再次運行就不會覆蓋:
而後咱們如何遍歷全部的註冊服務哪,使用循環:
using (var ioc = Container.BeginLifetimeScope()) { //var service = ioc.Resolve<IService>(); //service.PrintWord(); var serviceList = ioc.Resolve<IEnumerable<IService>>(); foreach (var item in serviceList) { item.PrintWord(); } }
運行結果:
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();
});
運行結果:
首先建立mvc項目就不過多解釋了。
這裏須要引用兩個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(); } }
運行看效果:
一個一個的去註冊控制是很繁瑣的事情,最怕的就是後期修改代碼增長了業務卻沒有及時添加相應的註冊而出錯,因此咱們會所有一次註冊,固然這不是必須的只是一種懶人操做。
RegisterAssemblyTypes方法:它會去掃描全部的dll並把每一個類註冊爲它所實現的接口。
咱們首先要建立一個接口基類什麼都不作只作爲註冊的類型檢測:
/// <summary> /// 註冊基類 /// </summary> public interface IDependency { }
而後因此須要註冊的接口都繼承此類便可:
/// <summary> /// 基類接口 /// </summary> public interface IBaseService:IDependency { /// <summary> /// 插入錯誤日誌數據 /// </summary> /// <returns></returns> int AddErrirLog(); /// <summary> /// 插入登陸日誌 /// </summary> /// <returns></returns> int AddLoginLog(); }
最後修改Global文件:
var builder = new ContainerBuilder(); // 經過程序集註冊全部控制器和屬性注入 //builder.RegisterControllers(typeof(MvcApplication).Assembly); builder.RegisterControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired(); //單個控制器注入 // builder.RegisterType<BaseService>().As<IBaseService>(); //集體自動注入 var baseType = typeof(IDependency); var assbembly = AppDomain.CurrentDomain.GetAssemblies().ToList(); builder.RegisterAssemblyTypes(assbembly.ToArray()) .Where(t => baseType.IsAssignableFrom(t) && t != baseType) .AsImplementedInterfaces().InstancePerLifetimeScope(); // 將依賴性分解器設置爲AutoFac。 var container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
主要是紅色部分