控制反转(Inversion of Control,IoC
)是 Spring
框架用来降低程序耦合度的一种解决方案。依赖注入(Dependency Injection,DI
)是 IoC
的另一种说法。控制反转的核心思想是将对象的创建和管理权从程序本身转移给外部容器。进一步地,这个外部容器不仅负责创建对象,还会处理它们之间的依赖关系,这个建立依赖关系的过程称为依赖注入。
以吃面包为例,假设没有面包店时,你需要自己制作每种口味的面包,这意味着你必须掌握各种制作方法。每当有新口味出现,你都要学习新的制作技巧。然而,有了面包店后,你只需告诉店员你想要的口味,他们会为你提供现成的面包,无论口味如何新颖,对你都没有影响。在这个比喻中,Spring
容器就像面包店,而你则是程序本身。程序中的对象就像面包,其创建和管理由 Spring
容器负责,这就是控制反转的概念。这样,当类的实现发生变化时,程序本身无需修改(即你不受影响)。在 Spring
中,容器会将依赖的对象注入到调用者的成员变量中,并且建立对象与对象之间的依赖关系,从而实现依赖注入。
一、需求引入
这里以面包为实例,假设:业务层存在 BreadService
接口和 BreadServiceImpl
实现类,数据层存在 BreadDao
接口和 BreadDaoImpl
实现类。在BreadServiceImpl
实现类中,存在指向 BreadDao
具体实现类 BreadDaoImpl
的属性 breadDao
,代码如下所示。
java">// 业务层
public class BreadServiceImpl implements BreadService {private BreadDao breadDao = new BreadDaoImpl();public void save() {breadDao.save();}
}// 数据层
public class BreadDaoImpl implements BreadDao {public void save() {}
}
现在由于出现了一种新口味的面包,引入了新的数据层代码实现类 NewBreadDaoImpl
。如果此时希望获得新口味的面包,那么这个时候业务层的代码就会受到影响,需要重新指定属性 breadDao
的实现对象,代码如下所示。
java">// 业务层
public class BreadServiceImpl implements BreadService {private BreadDao breadDao = new NewBreadDaoImpl();public void save() {breadDao.save();}
}// 数据层
public class NewBreadDaoImpl implements BreadDao {public void save() {}
}
那么当这个项目部署上线后,每当出现一个新的面包口味,业务层的代码就要修改,项目需要重新测试部署。因此,为了解决这种耦合度高带来的不便,一种直接的思想是在使用对象时不主动在程序中使用 new
创建对象,而是转为由外部提供对象。这里的外部实际上就是 Spring
中的 IoC
容器。
二、技术实现
Spring
技术对 IoC
思想进行了实现,Spring
提供了一个容器,称为 IoC
容器,用来充当 IoC
思想中的“外部”。Spring
通过 IoC
容器来管理需要创建的对象。IoC
容器负责这些对象的创建、初始化等一系列工作,被创建或被管理的对象在 IoC
容器中统称为 Bean
,比如面包案例中的service
和 dao
对象实例就可以称为 bean
对象。
三、具体使用
在使用 Spring
实现 IoC
的过程中,我们需要思考四个问题:
(1)利用 IoC
容器管理哪些 Bean
对象?
(2)如何将 Bean
对象告知 IoC
容器?
(3)如何获取 IoC
容器?
(4)如何获取由 IoC
容器管理的 Bean
对象?
在面包案例中,我们需要管理的 Bean
对象包括 Service
和 Dao
。而 Service
和 Dao
信息则可通过配置告知 IoC
容器。至于 IoC
容器和 Bean
对象的获取则可以通过 Spring
提供的接口获得。这里通过 Idea
创建一个 Maven
项目,并展示控制反转和依赖注入的编码过程,项目代码目录如下所示。
- 导入
Spring
坐标
在 pom.xml
文件中编写 Spring
对应坐标,导入 Spring
包。这里的 Spring
版本使用的是 5.2.10.RELEASE
。编写后等待 Spring
包下载,点击 Idea
右侧的 Maven
导航查看 Dependencies
中 Spring
对应版本的包下载情况,确保下载完成。
java"><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.10.RELEASE</version></dependency>
</dependencies>
- 定义
Bean
对象
这里同样以面包购买作为实例,在对应的 dao
和 service
包中定义需要的实现类。需要注意的是,由于 dao
和 service
之间存在依赖,因此,在 service
实现类中提供了一个 Setter
方法,用于帮助 IoC
容器实现依赖注入。另外,为了降低耦合,这里的 breadDao
属性指向的对象实例由 IoC
容器分配,从而实现控制反转。
java">// 业务层
public class BreadServiceImpl implements BreadService {private BreadDao breadDao;@Overridepublic void save() {breadDao.save();}public void setBreadDao(BreadDao breadDao) {this.breadDao = breadDao;}
}
java">// 数据层
public class BreadDaoImpl implements BreadDao {@Overridepublic void save() {System.out.println("hello");}
}
- 创建配置文件
在 resources
文件夹下面创建一个名为 applicationContext.xml
的 Spring
配置文件。具体地,右键 resources
文件夹,从 New
中选择 XML Configuration File
中的 Spring Config
(若没有该选项,说明步骤1的依赖没下载成功)。在 applicationContext.xml
文件的 <beans>
中配置对应的 bean
对象。
java"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="breadDao" class="com.dao.impl.BreadDaoImpl"/><bean id="breadService" class="com.service.impl.BreadServiceImpl"><!-- 配置 dao 和 service 的依赖关系 --><property name="breadDao" ref="breadDao"></property></bean>
</beans>
注意:
bean
标签表示配置的bean
对象;id
用于唯一标识bean
对象,该属性在同一个上下文中不能重复;class
属性用于指定bean
对象的类型。另外,property
标签表示配置当前bean
对象的属性;name
表示属性名;ref
表示指向的bean
对象id
。
- 获取
IoC
容器
在 App
类的 main
方法中,通过 ClassPathXmlApplicationContext
类获取 IoC
容器。向ClassPathXmlApplicationContext
类构造方法传递需要的配置文件。
java">ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
- 获取
bean
对象
在得到 IoC
容器后,通过 getBean
方法获取需要的 bean
对象。这里需要向 getBean
方法传递 bean
对象对应的 id
属性。
java">public class App {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");BreadService breadService = (BreadService) ctx.getBean("breadService");breadService.save();}
}
ntext("applicationContext.xml");BreadService breadService = (BreadService) ctx.getBean("breadService");breadService.save();}
}