ASP.NET没有魔法——ASP.NET MVC IoC

  之前的文章介绍了MVC如何通过ControllerFactory及ControllerActivator创建Controller,而Controller又是如何通过ControllerBase这个模板完成了功能的拓展及业务的执行。这一系列MVC类型的设计处处都体现了IoC的设计原则,所以本章将从以下几点对ASP.NET MVC中的IoC进行介绍:
  ● 什么是IoC
  ● ASP.NET MVC中的IoC
  ● 什么是DI
  ● IoC容器与依赖解析器(Dependency Resolver)
  ● ASP.NET MVC中使用DI

什么是IoC

  IoC(控制反转,Inversion of Control)它是软件开发中的一种设计原则,意思就是把控制权从自身(这里的自身大部分指代的是业务代码)转给其它对象来控制。IoC的意图是将任务的执行从实现中解耦,可以专注于模块任务的设计(如:操作数据库的组件,可以有操作SQL Server和My SQL的),系统本身不会关注这个模块做了什么,同时当系统替换了这个模块后不会对系统造成影响。在软件开发中有几种常见的基于IoC思想的实现:
  ● 工厂模式
  ● 模板模式
  ● 策略模式
  ● 依赖注入

  以上模式都是将实现解耦,便于拓展。特别是依赖注入(DI,Dependency Injection),看到IoC更多想到的就依赖注入。
  依赖倒置原则(DIP):上面说了那么多的实现,但有一个根本的原则就是依赖倒置原则“高层模块不依赖于低层模块,它们都应该依赖于抽象,抽象不应该依赖于实现,实现应该依赖于抽象”。在.Net中就是使用接口和抽象类对业务和对象进行抽象,抽象与实现分开,如果放弃了这一原则实现依赖于实现,那么就反转不了了(lll¬ω¬)。

ASP.NET MVC中的IoC

  之前的文章中分析了ASP.NET MVC中Controller的创建过程和执行中主要的参与对象分别有:
  ● DefaultControllerFactory:工厂模式。
  ● DefaultControllerActivator:依赖注入(注:创建Controller时首先从依赖解析器中获取Controller实例,无法获取才自己创建)。
  ● ControllerBase:模板模式(注:ControllerBase定义了Execute模板方法调用抽象方法ExecuteCore,ExecuteCore的实现在子类中)。

  这些对象的设计思想就是IoC。

什么是DI

  DI(Dependency Injection,依赖注入),它其实是两个部分,第一个是“依赖”,然后才是“注入”。
  1. 依赖:简单来说就是需要,在面向对象的一个类里面它“需要”其它类型来完成工作。如在MyBlog中的逻辑类需要仓储类来操作数据库,这种情况下就是逻辑类依赖仓储类:

   

  2. 注入:其实就是对类的依赖赋值,常用的注入的方式是:构造注入、属性注入、方法注入,说白了就是通过构造方法、属性(Setter方法)以及方法将依赖的对象传入类的实例中并赋值。但要注意的是这个“注”字,为什么不直接用赋值?
  举个栗子:制作一个水球,要么留一个注水口,要么在制作的时候把水灌入然后封死。
  对于有注水口的水球,在使用时如果注入红色的水,就变成了红色的水球,注入黄色的就是黄色的,而被封死的水球在制作时灌了什么颜色的水就是什么颜色的。
  而在程序开发中更是如此,以MyBlog为例,业务逻辑类通过仓储类来获取数据,业务类可以“封死”只用SQL Server的操作类,也可以开放,在运行时将数据操作这个实例“注入”,那么注入SQL Server操作类就使用SQL Server,注入MySQL的就使用My SQL。
  对水球注水首先要有水,其次是水球要有注水口,那么在程序里的“水”和“注水口”是什么?

IoC容器与依赖解析器(DependencyResolver)

  在程序中所谓的“水”应该是那些抽象的实现,或者说实现了抽象的类型。比如操作SQL Server的仓储类和操作MySQL的仓储操作类。那么这些“水”要放在哪里?“水”当然要放在容器里,所以就有了IoC容器。
  那IoC容器是什么?它可以简单到只是一个实例的数组或字典,通过实例的类型(或实现的父类型、接口等)从这个字典或数组中获取对应的实例,也可以是像Autofac、Ninject这些复杂成熟的IoC容器组件。
  以下代码分别是AutoFac和NinJect官方文档中为容器注“水”的过程:
  Ninject:https://github.com/ninject/ninject

  

  AutoFac:https://autofac.org/

  

  有了装满“水”的容器,想要把“水”注入到程序中,那就需要“注水口”依赖解析器(Denpendency Resolver)的支持。
  以下代码就是DefaultControllerActivator创建Controller的代码:

  

  想象一下,把上面的“注水口”连接到装满“水”的容器中,不是就能够把需要的东西注入到需要的位置吗?

ASP.NET MVC中使用DI

  DI的使用有两个必要条件就依赖容器依赖解析器,ASP.NET中建议使用成熟的依赖容器,如Autofac等,它们提供了强大的功能并支持多种组件注册到容器以及注入方式。而关于依赖解析器在ASP.NET MVC中是有默认解析器的,以下是ASP.NET MVC的解析器定义:

  

  它实际的作用是通过Current属性获取一个IDependencyResolver的对象。
  DependencyResolver中内置了一些实现该接口的对象,但是几乎是无用的如下图:

  

  所以一般情况下如果在ASP.NET中都是实现IDependencyResolver接口然后通过DependencyResolver.SetResolver方法修改默认的依赖解析器

  在ASP.NET MVC中,使用依赖注入最频繁的就是Controller,Controller作为业务逻辑执行的入口,它依赖业务逻辑的组件,而业务逻辑组件又依赖数据操作组件等等。所以在ASP.NET MVC中使用依赖注入实际上就是把Controller及其依赖都放入容器里,然后创建Controller时从容器中获取即可。根据Controller的创建流程将使用方法分为以下几种:
  1. 实现IDependencyResolver接口然后通过DependencyResolver.SetResolver方法修改默认的依赖解析器(替换默认的“注水口”)。
  2. 实现IControllerActivator接口,然后在创建DefaultControllerFactory时将其以参数的形式传入到DefaultControllerFactory中(在ControllerActivator中添加自己的“注水口”,并替换掉原有的ControllerActivator)。
  3. 继承DefaultControllerFactory使用从容器中获取Controller的方法将原有的GetControllerInstance方法替换掉(添加“注水口”放置在ControllerFactory中,放弃ControllerActivator的使用)。

  注:由于在Controller类型中包含一个IDependencyResolver属性,所以方法2和方法3没有替换默认依赖解析器会导致Controller中使用该属性无法访问到真实的容器,如果需要在其基础上使用方法1将默认的依赖解析器替换掉。

小结

  IoC容器和依赖注入在软件开发中是一个非常中要的概念,现在主流的一些开发框架的核心都是基于依赖注入的,即框架中的所有组件如日志、缓存、队列等都会通过容器将其注入到使用的地方。本章主要以文字的形式介绍了IoC、DI的概念及其在ASP.NET MVC中的使用的三种方法,在下一篇文章中将会用代码的形式介绍如何使用这三种方法在ASP.NET MVC中实现依赖注入。

参考:

  https://en.wikipedia.org/wiki/Inversion_of_control

本文连接:http://www.cnblogs.com/selimsong/p/7682808.html 

ASP.NET没有魔法——目录

ASP.NET没有魔法——ASP.NET MVC IoC代码篇

posted @ 2017-10-20 11:53  7m鱼  阅读(3199)  评论(6编辑  收藏