二、依赖注入的应用模式

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

1.     依赖注入的方式

前面我们提到,所谓“依赖”,最简单地去解释就是一个Java类里的成员变量。我们都知道,给一个类中的私有成员变量赋值的方法通常有:通过Constructor构造方法、通过Setter方法、通过反射机制将私有变量的可见性设为true这三种方法。同样道理,依赖注入框架也是利用这三种方式来完成依赖对象的设定的,我们分别称之为Constructor注入、Setter注入、成员变量注入。

这里有两点需要注意的地方。一是除了这三种常见的注入方式,还有一些其他的注入方式,例如Spring框架中提供的lookup-method为代表的方法注入模式,也被称作“装饰器”模式,还有像Guice中的方法参数注入等等,我们在此不再详加讨论。二是并不是所有的依赖注入框架都提供这三种常见的注入方式,比如Seam2.x版本里就不支持Constructor注入的方式。

1.1.    Constructor注入

利用Constructor注入,即在依赖者类的构造函数上声明注入点,而要被注入的对象则是构造函数的参数。

我们来看利用Spring框架的例子。首先我们将Bank和DepositBook两个依赖类声明为Spring容器管理的组件:

@Component // 声明此依赖为Spring组件

public class BankICBC implements Bank { // ……  }

@Component // 声明此依赖为Spring组件

public class DepositBookICBC implements DepositBook { // ……  }

这里我们采用Spring
3.x提供的@Component方式来声明Spring组件,当然还有很多其它的方式,就不再详细阐述了。将这两个依赖声明为Spring容器管理的组件之后,它们就可以在被需要的时候由容器提供给依赖者了。例如在储户Depositor类中,我们定义了以Constructor为注入点的依赖请求:

@Component

public class Depositor {

@Autowired  // Constructor注入点

public Depositor(Bank bank, DepositBook
depositBook) {

this.bank = bank;

this.depositBook = depositBook;

}

private Bank bank;

private DepositBook depositBook;

public Cash withDraw(BigDecimal amount) {

return bank.withDraw(depositBook,
amount);

}

}

之后主程序在调用这个Depositor类的时候,容器就会自动将保管的依赖对象设定好之后分发给主程序了。

public void runSpringConstructorDI() {

AnnotationConfigApplicationContext context =

new
AnnotationConfigApplicationContext("tutorial.di.ch01");

Depositor depositor =
context.getBean(Depositor.class);

depositor.withDraw(new
BigDecimal(10000));

}

1.2.    Setter注入

第二种常用的声明注入点的方法是Setter注入,即依赖注入框架利用反射机制调用依赖者类的Setter方法来完成依赖注入。

我们来看利用Seam框架的例子。首先还是要将Bank和DepositBook两个依赖类声明为Seam容器管理的组件:

@Name("bank")

public class BankICBC implements
Bank { // ……  }

@Name("depositBook")

public class DepositBookICBC
implements DepositBook { // …… 
}

之后在依赖者Depositor类中的Setter方法处声明注入点:

@Name("depositor")

public class Depositor {

private Bank bank;

private DepositBook depositBook;

@In  // bank的Setter注入点

public void setBank(Bank bank) {

this.bank = bank;

}

@In  // depositBook的Setter注入点

public void setDepositBook(DepositBook
depositBook) {

this.depositBook = depositBook;

}

public Cash withDraw(BigDecimal amount) {

return bank.withDraw(depositBook,
amount);

}

}

注意Seam框架缺省是将与该依赖字段同名的标识符所绑定的依赖对象注入进来,如果想注入与字段名不同的标识符得依赖对象,需要使用@In(“依赖对象标识符”)这样的语法。

1.3.    成员变量注入

成员变量注入是指依赖注入框架利用反射机制,直接将依赖者类的成员变量set为容器管理的依赖对象。

仍以Seam框架为例,与刚才Setter注入模式唯一不同的就是将@In标注在成员变量旁,而不是其Setter方法上。

@Name("depositor")

public class Depositor {

@In  // bank的成员变量注入点

private Bank bank;

@In  // depositBook的成员变量注入点

private DepositBook depositBook;

public Cash withDraw(BigDecimal amount) {

return bank.withDraw(depositBook,
amount);

}

}

1.4.    注入模式的选择

对于该怎么使用前面介绍的这几种注入模式,究竟在什么情况下该使用哪种,这是一个讨论非常广泛的话题。这里只举一个简单的例子来说明,比如要设计一个不可变(final)依赖的类,则必须使用Constructor注入方式:

@Component

public class Depositor {

@Autowired  // Constructor注入点

public Depositor(Bank bank, DepositBook
depositBook) {

this.bank = bank;

this.depositBook = depositBook;

}

private final Bank bank; // 不可变的依赖

private final DepositBook depositBook; //
不可变的依赖

public Cash withDraw(BigDecimal amount) {

return bank.withDraw(depositBook,
amount);

}

}

再如如果一个类所拥有的依赖数量过多、而开发者又不想构造方法的参数成为“过长参数列表”(这是一种影响代码的可读性和可维护性的反模式,Martin
Fowler于1999年在其《Refactoring:Improving the Design of Existing
Code》一书中提出),则可考虑Setter注入或者成员变量注入。此外,如果遇到了类似“循环依赖”的情况,例如宿主和寄生动物这两个对象就属于相互依存的“循环依赖”模式,这样的情况下,我们就可能需要Constructor和Setter两种混合的模式才能解决。总而言之,选择依赖注入模式是一个很大的话题,我们在此就不再深入讨论了。

依赖注入及AOP简述(五)——依赖注入的方式 .的更多相关文章

  1. 依赖注入及AOP简述(三)——依赖注入的原理

    3.     “依赖注入”登场 于是诸多优秀的IT工程师开始想出了更加轻量便利.更加具有可测试性和可维护性的设计模式——IoC模式.IoC,即Inversion of Control的缩写,中文里被称 ...

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

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

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

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

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

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

  5. 依赖注入及AOP简述(四)——“好莱坞原则”和依赖注入框架简介 .

    3.2.    “好莱坞原则” 看了前面关于依赖注入概念的描述,我们来提炼出依赖注入的核心思想.如果说传统的组件间耦合方式,例如new.工厂模式等,是一种由开发者主动去构建依赖对象的话,那么依赖注入模 ...

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

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

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

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

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

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

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

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

随机推荐

  1. vi常用命令笔记

    1.Vi 删除全部内容,删除某行到结尾,删除某段内容 (1)转到文件指定行 nG (2)删除所有内容(先用G转到文件尾) ,使用: $G :1,.d (3)删除第9行到第200行的内容(先用200G转 ...

  2. JavaScript-打开新窗口

    open()方法可以查找一个已经存在或者新建一个新的浏览器窗口. 语法:window.open([URL], [窗口名称], [参数字符串]) 参数解释: URL:可选参数,在窗口中显示网页的网址或路 ...

  3. Android学习总结——适配器

    适配器是AdapterView视图(如ListView - 列表视图控件.Gallery - 缩略图浏览器控件.GridView - 网格控件.Spinner - 下拉列表控件.AutoComplet ...

  4. [生成树][Uva1395][Slim Span]

    代码: #include <set> #include <queue> #include <cmath> #include <cstdio> #incl ...

  5. 2014年1月9日 Oracle 内存与结构

    Oracle启动时为启动一个实例 主要为 实例 SVG 数据库文件 其它文件 1.Oracle:  内存 进程  其他文件 1.1 SVG内存(Cache)  1.1.1 共享池(Shared Poo ...

  6. Single Number,Single Number II

    Single Number Total Accepted: 103745 Total Submissions: 218647 Difficulty: Medium Given an array of ...

  7. makefile常用指令和常见变量。

    引用文章A:http://blog.csdn.net/liang13664759/article/details/1771246 文章介绍:非常详细的文章,讲解上都是比较基础的知识. 本文可能会持续更 ...

  8. CDZSC_2015寒假新人(1)——基础 c

    Description FatMouse prepared M pounds of cat food, ready to trade with the cats guarding the wareho ...

  9. 理解中WebAPI的属性和相关操作 FormBody和 FormUri等(WebAPI 二)

    1.FromUri使用 将数据通过url方式传递.我们需要在webapi方法标明,这个参数只接受url中参数的值, $("#Save").click(function () { $ ...

  10. AngularJS的指令(Directive) compile和link的区别及使用示例

    如果我想实现这样一个功能,当一个input失去光标焦点时(blur),执行一些语句,比如当输入用户名后,向后台发ajax请求查询用户名是否已经存在,好有及时的页面相应. 输入 camnpr 失去焦点后 ...