控制反转:咱们向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(); } }