3.     “依赖注入”登场

于是诸多优秀的IT工程师开始想出了更加轻量便利、更加具有可测试性和可维护性的设计模式——IoC模式。IoC,即Inversion of Control的缩写,中文里被称作“控制反转”。至于为什么会有这么一个看似古怪的名字,我们稍后会做解释。2004年著名软件工程学者和工程师Martin Fowler在其论文《Inversion ofControl Containers and the Dependency Injection pattern》中将IoC更名为DependencyInjection,从此依赖注入模式成为实现组件间松耦合的主流设计思想。

3.1.    依赖注入的原理

那么应用了依赖注入,我们的开发模式将会发生什么样的变化呢?我们还是先来看看刚才的例子。下面的代码就是应用了依赖注入之后的储户Depositor类:

public class Depositor {

@In private Bank bank; // @In标识此处是一个注入点

@In private DepositBook depositBook; // @In标识此处是一个注入点

public Cash withDraw(BigDecimal amount) {

return bank.withDraw(depositBook, amount);

}

}

可以看到,这个Depositor类几乎就是最开始我们定义的那个最简单的、没有任何初始化依赖的过程的类!唯一不同的是在定义依赖成员变量的时候,多加了一个JavaSE5标准的注解——@In。这里@In注解是现今主流依赖注入框架之一JBoss
Seam所提供的一个注解,对于其它的依赖注入框架有着同样的使用方式,例如SpringFramework中提供了@Autowired注解等同于Seam的@In,Google
Guice框架中提供了@Inject注解等同于Seam的@In。关于依赖注入框架的话题我们稍候再做介绍,现在先来了解一下依赖注入框架的关于依赖注入的实现过程,这里我们还是以Seam框架为例进行介绍。

依赖注入框架的核心是依赖注入容器,也常被称为IoC容器或者叫Injector。其起的作用是管理一切注册到其中的依赖以及将这些依赖提供给请求依赖的依赖者。说到此或许有人会问,那这个依赖注入容器与前一节提到的ServiceLocator容器有什么不一样,不都是一个管理依赖对象的容器吗?答案是非常的不一样。不一样在于,首先依赖注入容器是极其轻量可控的,注册到其中的依赖对象可以方便的由开发者决定其标识符以及注册过程等,并且可以方便的放到单元测试环境中执行;其次依赖注入容器不仅仅是替开发者管理依赖,更重要的是它能够根据需要自动的去管理依赖对象的作用域及其生命周期,以及增强依赖对象的行为(即AOP),这一点我们在后面的章节中还会有详细的介绍。

回到刚才的例子:

@In private Bank bank; // @In标识此处是一个注入点

@In private DepositBook depositBook; //
@In标识此处是一个注入点

这里被@In标注的地方被称作“注入点”(injectionpoint),声明注入点起到的作用就是,告诉依赖注入容器“在这里请把依赖对象给我!”,也就是@In向依赖注入容器发出了一个请求,之后容器会利用Java的反射机制将其保管的对象set到注入点,即完成了一次依赖注入。

如果@In注入点所请求的依赖对象自身还有依赖,那么依赖注入容器同样会以同样的方式先去设定好这个“依赖的依赖”,也就是说,依赖注入容器会帮助开发者构建一套完整的依赖关系图,最后将一个完全初始化好的依赖对象提供给依赖者。

严格地讲,依赖注入同样有很多种模式,当遇到诸如循环依赖模式、部分依赖模式等情况时,依赖注入容器并不能将“完全初始化”的对象提供给依赖者。这些情况比较特殊,各种依赖注入框架也有不同的解决方式,我们在此就不再详加讨论了。

至于如何将依赖注册到容器中,每种依赖注入框架都有不同的运用方式,但是基本上都是大同小异,这里我们看一段以Seam框架为例的代码:

@Name("bank")

public class BankICBC implements
Bank { // ……  }

@Name("depositBook")

public class DepositBookICBC
implements DepositBook { // …… 
}

这里@Name标注的类代表这个类需要作为依赖对象被注册到Seam容器中,在Seam容器随着应用程序启动过程中即可完成这个注册过程。之后注册到Seam容器的这两个依赖就可以在被请求的时候提供给依赖者了。Seam框架中除了@Name标注,还可以用XML文件的<component>标签配置的方式来声明一个需要被注册到容器中的组件类。而如果使用的是Spring框架,则可通过@Component或者XML的<bean>标签等方式来声明组件类;Guice框架中则是通过Module的binding等方式来完成这一过程,总而言之各种依赖注入框架都提供了方便的向其容器去注册依赖的方法。

需要注意的是,注解(如前面举例的@In)并不是定义注入点的唯一方式。一般依赖注入框架还会提供API的方式和XML配置的方式来声明注入点。例如API的方式中,使用Seam框架时调用其Component#getInstance的API,或者使用Spring框架时调用其BeanFactory#getBean的API,或者使用Guice时调用其Injector#
getInstance的API等,都是声明注入点的一种方法。再比如XML配置方式中,Seam和Spring框架都提供了<property>标签用于声明依赖者类中的注入点。

我们可以看到,应用了依赖注入设计思想之后,我们的Depositor类就可以以一种最简单的姿态出现在我们的代码库中。对于今后的维护,不管Bank和DepositBook两个依赖类发生了什么样的构造方法的变化,都不会影响到Depositor类本身。并且在单元测试中,我们可以方便的用Mock类替换掉实际的依赖。只需将Mock类绑定为相同的标识符,那么当依赖者请求依赖的时候,容器就会将Mock类提供给依赖者。例如:

@Name("bank")  // 采用相同的标识符"bank"

public class BankMock implements
Bank { // 这是一个Mock依赖类  }

@Name("depositBook") // 采用相同的标识符"depositBook"

public class DepositBookMock
implements DepositBook { // 这是一个Mock依赖类 
}

依赖注入及AOP简述(三)——依赖注入的原理的更多相关文章

  1. 依赖注入及AOP简述(九)——单例和无状态Scope .

    三.依赖注入对象的Scope及其生命周期 在前面的章节我们讲到,依赖注入容器之所以能够区别于以往的ServiceLocator等容器,是在于其不但能够自动构建多层次的.完整的依赖关系图,并且可以管理依 ...

  2. 依赖注入及AOP简述(五)——依赖注入的方式 .

    二.依赖注入的应用模式 前面我们了解了依赖注入的基本概念,也对一些依赖注入框架进行了简单的介绍,这一章我们主要来讨论作为开发者如何利用依赖注入框架来实现依赖注入的设计思想. 1.     依赖注入的方 ...

  3. 依赖注入及AOP简述(七)——FQCN请求模式

    2.2.    FQCN请求模式 为了弥补纯字符串请求模式中的类型安全问题,全类名(FQCN)请求模式就应运而生了.其思想便是,在向容器请求依赖对象的时候,不是通过字符串的标识符.而是通过被请求的依赖 ...

  4. 依赖注入及AOP简述(六)——字符串请求模式 .

    2.     依赖注入对象的请求模式 前一节我们讨论了关于声明注入点的几种方法,这一节主要来介绍在注入点上如何定位到所需要的标识符的话题.基本上,我们可以用字符串为标识符来请求依赖对象.或者用全类名( ...

  5. 依赖注入及AOP简述(一)——“依赖”的概念 .

    一.入门:依赖注入 作为一种全新的设计模式理念,“依赖注入”这个词汇在软件设计开发中已经是越来越耳熟能详了,而各种流行于开源社区的“依赖注入框架”,也越来越多的被当作软件工程开发过程中使用的基础框架. ...

  6. 依赖注入及AOP简述(十一)——生命周期管理 .

    2.     生命周期管理 各种依赖注入框架提供了替开发者管理各种Scope的便利功能,随之而来的就必然是被管理的依赖对象的生命周期管理的问题.所谓生命周期管理,就是一个对象在它所属的Scope中从被 ...

  7. 依赖注入及AOP简述(十三)——AOP应用举例(完结) .

    2.     AOP应用举例 在一般的应用程序开发中,有一些典型的AOP应用,使得开发者可以专注于业务逻辑本身,而不是与之完全无关的一些“方面”. l        首先就是关于前面介绍过的日志输出类 ...

  8. 依赖注入及AOP简述(十二)——依赖注入对象的行为增强(AOP) .

    四.依赖注入对象的行为增强(AOP) 前面讲到,依赖注入框架的最鲜明的特点就是能够提供受容器管理的依赖对象,并且可以对对象提供行为增强(AOP)功能,所以这一章我们来讨论有关AOP的话题. 1.    ...

  9. 依赖注入及AOP简述(十)——Web开发中常用Scope简介 .

    1.2.    Web开发中常用Scope简介 这里主要介绍基于Servlet的Web开发中常用的Scope. l        第一个比较常用的就是Application级Scope,通常我们会将一 ...

随机推荐

  1. Median of Two Sorted Arrays (找两个序列的中位数,O(log (m+n))限制) 【面试算法leetcode】

    题目: There are two sorted arrays A and B of size m and n respectively. Find the median of the two sor ...

  2. Java中取小数点后两位(四种方法)

    摘自http://irobot.iteye.com/blog/285537 Java中取小数点后两位(四种方法)   一 Long是长整型,怎么有小数,是double吧     java.text.D ...

  3. Linux 下的php,nginx,mysql的安装

    yum -y install wget make vim install gcc gcc-c++ ncurses ncurses-devel autoconf libjpeg libjpeg-deve ...

  4. java-四个线程修改一个变量

    package 经典; public class 四个线程 { private int j=10; /** * @param args */ public static void main(Strin ...

  5. Alter的用法(添加字段,删除字段,修改字段名)

    1.在表emp中新增字段sexy(性别) alter table emp add sexy varchar2(2); 新增多个字段cxx 和shoneworn alter table emp add  ...

  6. C#中“@”的作用和用法

    “@”在看别人程序的时候偶尔看到,总结了一下两个用途 1. 不常用,也不推介用的用法. @关键字 可以作为标识符来使用,说白了,就是讲关键字变成非关键字. 2.逐字字符串字面量,以@开头,后面是由引导 ...

  7. 常用的SQL数据库语句总结

    1as 的用处 as可以对表和列取别名 在开发过程中经常遇到开始给某一个的字段去field1的名称,但后来有感觉field1字段指定不确切,于是又把此字段改成了field2,由于开始认 为field1 ...

  8. 在Yii框架中使用PHPExcel

    PHPExcel是一个比较好用的php读取excel文件的类库,今天遇到了在yii中如何加载PHPExcel类文件的问题,因为Yii的autoload机制是安装类名去找文件,即文件名就是相应的类名,而 ...

  9. nginx+webpy 出现 upstream timed out

    关于nginx配置webpy应用出现的错误 upstream timed out (: Connection timed out) while reading response header from ...

  10. HttpClient---------demo

    public class aa { public static void main(String[] args) { // 创建HttpClient实例 HttpClient httpclient = ...