控制反轉:咱們向IOC容器發出獲取一個對象實例的一個請求,IOC容器便把這個對象實例「注入」到咱們的手中,在這個過程當中你不是一個控制者而是一個請求者,依賴於容器提供給你的資源,控制權落到了容器身上。這個過程就是控制反轉。 編程
依賴注入:咱們向容器發出請求之後,得到這個對象實例的過程就叫依賴注入。app
關於Ioc的框架有不少,好比astle Windsor、Unity、Spring.NET、StructureMap,咱們這邊使用微軟提供的Unity作示例,你可使用 Nuget 添加 Unity ,也能夠引用Microsoft.Practices.Unity.dll和Microsoft.Practices.Unity.Configuration.dll,下面咱們就一步一步的學習下 Unity依賴注入 的詳細使用。框架
添加一個接口和一個實現類,經過Main()方法調用測試。函數
/// <summary> /// 顯示信息 /// </summary> public interface IUserDao { void Display(string mes); } class UserImpl : IUserService { public IUserDao IUserDao; //構造函數設置值 public UserImpl(IUserDao UserDao) { IUserDao = UserDao; } /// <summary> /// 顯示信息 /// </summary> /// <param name="mes"></param> public void Display(string mes) { IUserDao.Display(mes); } }
/// <summary> /// 顯示信息 /// </summary> public interface IUserDao { void Display(string mes); } public class UserDaoImpl : IUserDao { public void Display(string mes) { Console.WriteLine(mes); } }
class Program { public IUserService IUserService { get; set; } public static void Main(string[] args) { //建立容器 UnityContainer container = new UnityContainer(); //註冊依賴對象 container.RegisterType<IUserService, UserImpl>(); container.RegisterType<IUserDao, UserDaoImpl>(); //返回調用者 IUserService IUser = container.Resolve<UserImpl>(); //執行 IUser.Display("王建"); Console.ReadLine(); } }
點擊運行,成功輸出。學習
構造器注入
構造器注入(Constructor Injection):IoC容器會智能地選擇選擇和調用適合的構造函數以建立依賴的對象。
若是被選擇的構造函數具備相應的參數,IoC容器在調用構造函數以前解析註冊的依賴關係並自行得到相應參數對象。
RegisterType:能夠看作是自來水廠決定用什麼做爲水源,能夠是水庫或是地下水,我只要「註冊」開關一下就好了。
Resolve:能夠看作是自來水廠要輸送水的對象,能夠是農村或是城市,我只要「控制」輸出就好了。
屬性注入(Property Injection),就是經過 set 設值對對象進行設值,只須要在調用對象的上面加上 [Dependency] 標記便可。當依賴對象被容器初始化之後,會自動對該對象設值。測試
class UserImpl : IUserService { //只須要在對象成員前面加上[Dependency], //就是把構造函數去掉,成員對象上面加[Dependency]注入 [Dependency] public IUserDao IUserDao { get; set; } //public UserImpl(IUserDao UserDao) //{ // IUserDao = UserDao; //} /// <summary> /// 顯示信息 /// </summary> /// <param name="mes"></param> public void Display(string mes) { IUserDao.Display(mes); } }
點擊運行,實現的結果是同樣的。ui
其實使用上面 RegisterType 方法進行註冊,每次添加和刪除一個註冊都須要去修改代碼和從新編譯,這樣不符合「高內聚、低耦合」的編程思想,因此咱們能夠採用配置文件的方式去註冊,這樣每次添加和修改註冊就不須要去修改代碼和從新發布了。配置文件註冊用 UnityConfigurationSection 的 Configure加載配置文件註冊。spa
代碼(若是是控制檯程序,配置寫在App.config,若是是Web程序,就寫在 Web.config):code
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" /> </configSections> <unity xmlns="http://schemas.microsoft.com/practces/2010/unity"> <containers> <!--MyContainer爲自定義名稱 只須要和調用時名稱保持一致便可--> <container name="MyContainer"> <!--type爲對象的名稱,mapTo爲注入對象的名稱 寫法爲用逗號隔開兩部分,一是類的所有,包括命名空間,二是程序集名稱--> <register type="ThreadDemo.Bll.IUserBll,ThreadDemo" mapTo="ThreadDemo.Bll.impl.UserBll,ThreadDemo"> <lifetime type="singleton" /> </register> <register type="ThreadDemo.Dal.IUserDal,ThreadDemo" mapTo="ThreadDemo.Dal.impl.UserDal,ThreadDemo"/> </container> </containers> </unity> <!--startup必須放在<configSections>節點下面,不然報錯--> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> </startup> </configuration>
class Program { public IUserBll UserBll { get; set; } public static void Main(string[] args) { //建立容器 //UnityContainer container = new UnityContainer(); //註冊依賴對象 //container.RegisterType<IUserService, UserImpl>(); //container.RegisterType<IUserDao, UserDaoImpl>(); //返回調用者 //IUserService IUser = container.Resolve<UserImpl>(); //建立容器 UnityContainer container = new UnityContainer(); UnityConfigurationSection config = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName); //加載到容器 config.Configure(container, "MyContainer"); //返回調用者 IUserBll IUser = container.Resolve<IUserBll>(); //執行 IUser.Display("王建"); Console.ReadLine(); } }
方法注入和構造注入差很少,只不過把構造函數變成了一個普通的方法,在方法前面加 [InjectionMethod] 屬性。xml
namespace ThreadDemo.Bll.impl { public class UserBll : IUserBll { public IUserDal IDal; /// <summary> /// 方法注入-加[InjectionMethod]屬性 /// </summary> /// <param name="IUserDal"></param> [InjectionMethod] public void SetInjection(IUserDal IUserDal) { IDal = IUserDal; } /// <summary> /// 顯示信息 /// </summary> /// <param name="mes"></param> public void Display(string mes) { IDal.Display(mes); } } }
這幾種方法運行結果都是同樣的。
下面的例子是在Unity在Web項目中的使用:
namespace ShowWeatherWebUI { public class BootStrapper { /// <summary> /// 獲取容器-註冊依賴關係 /// </summary> /// <returns></returns> public static IUnityContainer Initialise() { var container = BuildUnityContainer(); DependencyResolver.SetResolver(new UnityDependencyResolver(container)); return container; } /// <summary> /// 加載容器 /// </summary> /// <returns></returns> private static IUnityContainer BuildUnityContainer() { var container = new UnityContainer(); RegisterTypes(container); return container; } /// <summary> /// 實施依賴注入 /// </summary> /// <param name="container"></param> private static void RegisterTypes(UnityContainer container) { //依賴關係能夠選擇代碼形式,也能夠用配置文件的形式 //UnityConfigurationSection config = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName); //加載到容器 //config.Configure(container, "MyContainer"); container.RegisterType<IUerBll, UerBll>(); container.RegisterType<IUserDal, UserDal>(); } } }
由於Global.asax是應用程序啓動的時候會執行,因此會去加載容器
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); //加載容器-註冊依賴 BootStrapper.Initialise(); } }
在每一個調用的接口添加 [Dependency] 屬性便可,也就是屬性輸入,也能夠採用構造函數注入和方法注入。
public class HomeController : Controller { [Dependency] public IUerBll bll { get; set; } public ActionResult Index() { bll.Display("王文建"); return View(); } }