一、依賴注入
引入依賴注入的目的是為了解耦和。說白了就是面向介面程式設計,通過呼叫介面的方法,而不直接例項化物件去呼叫。這樣做的好處就是如果新增了另一個種實現類,不需要修改之前程式碼,只需要修改注入的地方將實現類替換。上面的說的通過介面呼叫方法,實際上還是需要去例項化介面的實現類,只不過不需要我們手動new 構造實現類,而是交給如微軟的DI、Autofac這些工具去構建實現類。我們只需要告訴它們,某個類是某個介面的實現類,當用到的時候,工具會自動通過建構函式例項化類。
二、.Net Core中自帶的DI
本來想寫依賴注入原始碼的講解的,看到網上有篇文章關於原始碼講解的,很詳細、清楚,就不再寫了。地址:http://www.cnblogs.com/bill-shooting/p/5540665.html。我在這裡就說說使用吧。
依賴注入有三種生命週期,每種生命週期的注入方式大同小異,下面我以作用域生命週期舉例,其他兩種跟這個不同,我會特別說明。
下面為用到的兩個服務。
public class UserService : IUserService { public string GetName() { return "UserName"; } } public interface IUserService { string GetName(); }
public class ConfigReader : IConfigReader { private string configFilePath;//需要傳一個路徑,去讀取路徑下檔案的內容 public ConfigReader(string configFileName) { this.configFilePath = configFileName; } public string Reader() { return File.ReadAllText(configFilePath); } } public interface IConfigReader { string Reader(); }
1、最常用的注入方式,以介面形式暴露服務
services.AddScoped(typeof(IUserService), typeof(UserService));
services.AddScoped<IUserService, UserService>();
兩種注入方式是一個意思,這種方式適合實現類為無參建構函式或者有參建構函式中引數已經被注入過了。
2、自己注入自己,以實現形式暴露服務
services.AddScoped<UserService>();
services.AddScoped(typeof(UserService));
這種注入方式適合只有實現類,沒有藉口類的註冊。
3、需要傳參的建構函式的類的注入
services.AddScoped<IConfigReader, ConfigReader>(x => { return new ConfigReader("c:/a.txt"); });
services.AddScoped<IConfigReader>(x => { return new ConfigReader("c:/a.txt"); });
services.AddScoped(typeof(IConfigReader), x => { return new ConfigReader("c:/a.txt"); });
前兩個匿名方法引數是IServiceProvider,返回值為一個例項,第三個返回值是Object。上面舉的例子沒有用到IServiceProvider ,下面再舉一個例子。修改上面的UserService類,將構造方法需要一個IConfigReader引數。
public class UserService : IUserService {private IConfigReader configReader; public UserService(IConfigReader configReader) { this.configReader = configReader; } public string GetName() { return "UserName" + configReader.Reader(); } }
註冊的時候,如下:
services.AddScoped<IConfigReader, ConfigReader>(x => { return new ConfigReader("c:/a.txt"); });
//通過ServiceProvider獲取已經註冊的IConfigReader services.AddScoped<IUserService, UserService>(x => { return new UserService(x.GetService<IConfigReader>()); });
//或者
services.AddScoped<IUserService, UserService>(x => { return new UserService(new ConfigReader("c:/a.txt")); });
單例型別的生命週期多了兩種注入方式:
services.AddSingleton<IConfigReader>(new ConfigReader("c:/a.txt"));
services.AddSingleton(typeof(IConfigReader), new ConfigReader("C:/a.txt"));
自帶的依賴注入工具也可以批量注入
var assembly = Assembly.GetExecutingAssembly() .DefinedTypes .Where(a => a.Name.EndsWith("Service") && !a.Name.StartsWith("I")); foreach (var item in assembly) { services.AddTransient(item.GetInterfaces().FirstOrDefault(), item); }
注意:當一個服務有多個實現時,呼叫的時候通過 IEnumerable<IPayService> PayServices 獲取所有的實現服務。
services.AddTransient<IPayService, AliPayService>();
services.AddTransient<IPayService, WeChatPayService>();
使用的時候:
三、Autofac
1、以介面形式暴露服務
public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); var builder = new ContainerBuilder(); builder.Populate(services); builder.RegisterType<UserService>().As<IUserService>().InstancePerLifetimeScope(); var container = builder.Build(); return new AutofacServiceProvider(container); }
2、通過實現類暴露服務
builder.RegisterType<UserService>();
3、需要傳參的建構函式的類的注入
builder.Register(c => new ConfigReader("c:/a.txt")).As<IConfigReader>();
4、通過程式集註入
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) .Where(c => c.Name.EndsWith("Service")) .AsImplementedInterfaces();
總結:
不論是微軟的依賴注入元件還是Autofac 原理都是先將介面和對應的實現類注入到容器中,當要使用的時候,元件會自動通過建構函式建立例項。這裡有個問題如果有個實現類有多個建構函式,元件會找滿足引數最多的那個建構函式。