15.2 依赖注入
15.2 依赖注入
在过去数年间,依赖注入技术作为代码可测试性的一个解决方案已经被广泛应用。实际上,Spring
、谷歌Guice
等伟大框架都采用了依赖注入技术。那么,什么是依赖注入技术?
什么是依赖注入
很多人在使用中并不区分依赖注入和控制反转(IoC
),尽管Martin Fowler
在其文章中已分析了二者的不同。
http://martinfowler.com/articles/injection.html
实例
简单来说,依赖注入的情况如下。
有两个组件A
和B
,A
依赖于B
。假定A
是一个类,且A
有一个方法importantMethod
使用到了B
,如下:
1 | public class A { |
要使用**B
类,A
类必须先获得组件B
的实例的引用**。若B
是一个具体类,则可通过new
关键字直接创建组件B
实例。但是,如果B
是接口,且有多个实现,则问题就变得复杂了。我们固然可以任意选择接口B
的一个实现类,但这也意味着A
的可重用性大大降低了,因为无法采用B
的其他实现。
依赖注入是这样处理此类情景的:接管对象的创建工作,并将该对象的引用注入需要该对象的组件
。
以上述例子为例,依赖注入框架会分别创建对象A
和对象B
,将对象B
注入到对象A
中。
依赖注入方式
**为了能让框架进行依赖注入,程序员需要编写特定的set
方法或者构造方法
**。
setter方法注入
例如,为了能将B
注入到A
中,类A
会被修改成如下形式:
1 | public class A { |
修改后的类A
新增了一个setter
方法,该setter
方法将会被框架调用,用以注入一个B
的实例。由于对象依赖由依赖注入,类A
的importantMethod
方法不再需要在调用B
的usefulMethod
方法前去创建一个B
的实例。
构造器注入
当然,也可以采用构造器方式注入,如下所示:
1 | public class A { |
本例中,**Spring
会先创建B
的实例,再创建实例A
,然后把B
注入到实例A
中。
注意Spring
管理的对象称为beans
。
通过提供一个控制反转容器**(或者依赖注入容器),Spring
为我们提供一种可以“聪明”地管理Java
对象依赖关系的方法。其优雅之处在于,程序员无须了解Spring
框架的存在,更不需要引入任何Spring
类型。
版本支持
- 从1.0版本开始,**
Spring
就同时支持setter
和构造器方式
的依赖注入**。 - 从2.5版本开始,通过
Autowired
注解,Spring
支持基于field
方式的依赖注入,但缺点是程序必须引入org.springframework.beans.factory.annotation.Autowired
,这对Spring
产生了依赖,这样,程序无法直接迁移到另一个依赖注入容器内。
使用Spring
,程序几乎将所有重要对象的创建工作移交给Spring
,并配置如何注入依赖。
Spring
支持XML
和注解两种配置方式。此外,还需要创建一个ApplicationContext
对象,ApplicationContext
对象代表一个Spring
控制反转容器,
ApplicationContext接口
org.springframework.context.ApplicationContext
接口有多个实现,包括ClassPathXmlApplicationContext
和FileSystemXmlApplicationContext
。这两个实现都需要至少一个包含beans
信息的XML
文件。ClassPathXmlApplicationContext
尝试在类加载路径中加载配置文件,而FileSystemXmlApplicationContext
则从文件系统中加载。
下面为从类路径中加载config1.xml
和config2.xml
的ApplicationContext
创建的一个代码示例:
1 | ApplicationContext context = |
可以通过调用ApplicationContext
的getBean
方法获得对象:
1 | Product product = context.getBean("product", Product.class); |
getBean
方法会查询id
为product
且类型为Product
的bean
对象。
注
理想情况下,我们仅需在测试代码中创建一个ApplicationContext
,应用程序本身无须处理。对于Spring MVC
应用,可以通过一个Spring Servlet
来处理ApplicationContext
,而无须直接处理。