一、依赖注入(DI)简介
依赖注入背后的基本原理是对象之间的依赖关系,可以通过以下几种方式来实现:构造器的参数、工厂方法的参数,或给由构造函数或者工厂方法创建的对象设置属性。因此,容器的工作就是创建bean时注入那些依赖关系。相对于由bean自己来控制其实例化、直接在构造器中指定依赖关系或者类似服务定位器模式这3种自主控制依赖关系注入的方法来说,控制从根本上发生了倒转,这也正是控制反转名字的由来。
依赖注入主要有两种注入方式,即构造器注入和Setter注入。
二、构造器注入
基于构造器的依赖注入是通过调用带参数的构造器来实现,每个参数代表着一个依赖。下面展示了用构造器参数来注入依赖关系的例子。
先创建一个对象(bean)
Java代码
1. public class HelloWorld {
2. private String msg;
3.
4. //需要一个默认无参构造器
5. public HelloWorld(){}
6.
7. public HelloWorld(String msg){
8. this.msg = msg;
9. }
10.
11. public String getMsg() {
12. return msg;
13. }
14. public void setMsg(String msg) {
15. this.msg = msg;
16. }
17. }
public class HelloWorld {
private String msg;
//需要一个默认无参构造器
public HelloWorld(){}
public HelloWorld(String msg){
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
再修改配置文件applicationContext.xml,实例化bean
Java代码
1. <bean id="hello" class="com.spring.demo.HelloWorld">
2. <constructor-arg index="0">
3. <value>HelloWorld!</value>
4. </constructor-arg>
5. </bean>
<bean id="hello" class="com.spring.demo.HelloWorld">
<constructor-arg index="0">
<value>HelloWorld!</value>
</constructor-arg>
</bean>
最后测试是否能够得到注入的bean,并打印出对象的属性。
Java代码
1. public static void main(String[] args){
2. //读取配置文件,获得BeanFactory
3. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
4. BeanFactory factory = context;
5.
6. HelloWorld hello = (HelloWorld)factory.getBean("hello");
7.
8. System.out.println(hello.getMsg());
9. }
public static void main(String[] args){
//读取配置文件,获得BeanFactory
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BeanFactory factory = context;
HelloWorld hello = (HelloWorld)factory.getBean("hello");
System.out.println(hello.getMsg());
}
三、Setter注入
通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法,即可实现基于setter的依赖注入。
创建一个对象
Java代码
1. public class HelloWorld {
2. private String msg;
3.
4. public String getMsg() {
5. return msg;
6. }
7. public void setMsg(String msg) {
8. this.msg = msg;
9. }
10. }
public class HelloWorld {
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
修改配置文件applicationContext.xml,实例化bean
Java代码
1. <bean id="helloBean" class="com.spring.demo.HelloWorld">
2. <property name="msg" value="Hello World!"/>
3. </bean>
<bean id="helloBean" class="com.spring.demo.HelloWorld">
<property name="msg" value="Hello World!"/>
</bean>
测试类,获得注入的bean
Java代码
1. public static void main(String[] args){
2. //读取配置文件,实例化IOC容器
3. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
4. BeanFactory factory = context;
5.
6. HelloWorld hello = (HelloWorld)factory.getBean("helloBean");
7.
8. System.out.println(hello.getMsg());
9. }
public static void main(String[] args){
//读取配置文件,实例化IOC容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BeanFactory factory = context;
HelloWorld hello = (HelloWorld)factory.getBean("helloBean");
System.out.println(hello.getMsg());
}
处理bean依赖关系通常按以下步骤进行:
1.根据定义bean的配置创建并初始化BeanFactory实例
2.每个bean的依赖将以属性、构造器参数、或静态工厂方法参数的形式出现。当这些bean被实际创建时,这些依赖也将会提供给该bean。
3.每个属性或构造器参数既可以是一个实际的值,也可以是对该容器中另一个bean的引用。
4.每个指定的属性或构造器参数值必须能够被转换成特定的格式或构造参数所需的类型。
Spring会在容器被创建时验证容器中每个bean的配置,包括验证那些bean所引用的属性是否指向一个有效的bean。在bean被实际创建之前,bean的属性并不会被设置。伴随着bean被实际创建,作为该bean的依赖bean以及依赖bean的依赖bean也将被创建和分配。
依赖注入的3种实现方式分别是:接口注入(interface injection)、Set注入(setter injection)和构造注入(constructor injection)。接下来笔者还将主要通过举例的方式,把依赖注入的3种实现方式介绍给读者。
3.2.1 接口注入(interface injection)
接口注入指的就是在接口中定义要注入的信息,并通过接口完成注入。结合前面的示例,其具体步骤如下。
(1)编写一个接口IBusiness,各种数据库的注入将通过这个接口进行。IBusiness.java的示例代码如下:
//******* IBusiness.java**************
public interface IBusiness {
public void createDI(DataBase db);
}
(2)任何想要使用数据库实例的类都必须实现这个接口,业务逻辑类Business实现这个接口IBusiness。Business.java的示例代码如下:
//******* Business.java**************
public class Business implement IBusiness {
private DataBase db;
public void createDI (DataBase db) {
this.db = db;
}
……
//根据注入的数据库类,从×××数据库中获取数据
public void getData() {
……
db.getData();
……
}
}
(3)编写测试类TestBusiness。TestBusiness.java的示例代码如下:
//******* TestBusiness.java**************
public class TestBusiness {
private Business business = new Business();
……
//根据注入的数据库类,从Oracle数据库中获取数据
public void getData() {
……
business. createDI (new OracleDataBase());
business.getData();
……
}
}
如果要完成依赖关系注入的对象,必须实现IBusiness接口。
3.2.2 Set注入(setter injection)
Set注入指的就是在接受注入的类中定义一个Set方法,并在参数中定义需要注入的元素。为了让类Business接受DataBase的注入,需要为它定义一个Set方法来接受DataBase的注入。Business.java的示例代码如下:
//******* Business.java**************
public class Business {
private DataBase db;
public void setDataBase(DataBase db) {
this.db = db;
}
……
//根据注入的数据库类,从×××数据库中获取数据
public void getData() {
……
db.getData();
……
}
}
更详细的代码,可以参看3.1节的第二个例子,采用的就是Set注入的方式。
3.2.3 构造注入(constructor injection)
构造注入指的就是在接受注入的类中定义一个构造方法,并在参数中定义需要注入的元素。为了让类Business接受DataBase的注入,需要为它定义一个构造方法,来接受DataBase的注入。Business.java的示例代码如下:
//******* Business.java**************
public class Business {
private DataBase db;
public Business (DataBase db) {
this.db = db;
}
……
//根据注入的数据库类,从×××数据库中获取数据
public void getData() {
……
db.getData();
……
}
}