简介:
控制反转:我们向IOC容器发出获取一个对象实例的一个请求,IOC容器便把这个对象实例“注入”到我们的手中,在这个过程中你不是一个控制者而是一个请求者,依赖于容器提供给你的资源,控制权落到了容器身上。这个过程就是控制反转。
依赖注入:我们向容器发出请求以后,获得这个对象实例的过程就叫依赖注入。
关于Ioc的框架有很多,比如astle Windsor、Unity、Spring.NET、StructureMap,我们这边使用微软提供的Unity做示例,你可以使用 Nuget 添加 Unity ,也可以引用Microsoft.Practices.Unity.dll和Microsoft.Practices.Unity.Configuration.dll,下面我们就一步一步的学习下 Unity依赖注入 的详细使用。
一、使用 Nuget 添加 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); } }
点击运行,实现的结果是一样的。
配置文件注册:
其实使用上面 RegisterType 方法进行注册,每次添加和删除一个注册都需要去修改代码和重新编译,这样不符合“高内聚、低耦合”的编程思想,所以我们可以采用配置文件的方式去注册,这样每次添加和修改注册就不需要去修改代码和重新发布了。配置文件注册用 UnityConfigurationSection 的 Configure加载配置文件注册。
代码(如果是控制台程序,配置写在App.config,如果是Web程序,就写在 Web.config):
<?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] 属性。
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.MVC在Web中的应用
下面的例子是在Unity在Web项目中的使用:
1、安装Unity.MVC
2、在目录下会生成一个 BootStrapper.cs 的类文件,打开进行 编辑(如果没有生成,自己创建,名称随意)。
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>(); } } }
3、在 Global.asax 文件中添加 BootStrapper.Initialise() 的方法。
因为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(); } }
4、在Controller里调用,
在每个调用的接口添加 [Dependency] 属性即可,也就是属性输入,也可以采用构造函数注入和方法注入。
public class HomeController : Controller { [Dependency] public IUerBll bll { get; set; } public ActionResult Index() { bll.Display("王文建"); return View(); } }