控制反转(Inversion of Control,IoC)是 Spring 框架用来降低程序耦合度的一种解决方案。依赖注入(Dependency Injection,DI)是 IoC 的另一种说法。控制反转的核心思想是将对象的创建和管理权从程序本身转移给外部容器。进一步地,这个外部容器不仅负责创建对象,还会处理它们之间的依赖关系,这个建立依赖关系的过程称为依赖注入。
以吃面包为例,假设没有面包店时,你需要自己制作每种口味的面包,这意味着你必须掌握各种制作方法。每当有新口味出现,你都要学习新的制作技巧。然而,有了面包店后,你只需告诉店员你想要的口味,他们会为你提供现成的面包,无论口味如何新颖,对你都没有影响。在这个比喻中,Spring 容器就像面包店,而你则是程序本身。程序中的对象就像面包,其创建和管理由 Spring 容器负责,这就是控制反转的概念。这样,当类的实现发生变化时,程序本身无需修改(即你不受影响)。在 Spring 中,容器会将依赖的对象注入到调用者的成员变量中,并且建立对象与对象之间的依赖关系,从而实现依赖注入。
一、需求引入
这里以面包为实例,假设:业务层存在 BreadService 接口和 BreadServiceImpl 实现类,数据层存在 BreadDao 接口和 BreadDaoImpl 实现类。在BreadServiceImpl 实现类中,存在指向 BreadDao 具体实现类 BreadDaoImpl 的属性 breadDao,代码如下所示。
// 业务层
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 的实现对象,代码如下所示。
// 业务层
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 对应版本的包下载情况,确保下载完成。
<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 容器分配,从而实现控制反转。
// 业务层
public class BreadServiceImpl implements BreadService {
private BreadDao breadDao;
@Override
public void save() {
breadDao.save();
}
public void setBreadDao(BreadDao breadDao) {
this.breadDao = breadDao;
}
}
// 数据层
public class BreadDaoImpl implements BreadDao {
@Override
public void save() {
System.out.println("hello");
}
}
- 创建配置文件
在 resources 文件夹下面创建一个名为 applicationContext.xml 的 Spring 配置文件。具体地,右键 resources 文件夹,从 New 中选择 XML Configuration File 中的 Spring Config(若没有该选项,说明步骤1的依赖没下载成功)。在 applicationContext.xml 文件的 <beans> 中配置对应的 bean 对象。
<?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 类构造方法传递需要的配置文件。
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
- 获取
bean对象
在得到 IoC 容器后,通过 getBean 方法获取需要的 bean 对象。这里需要向 getBean 方法传递 bean 对象对应的 id 属性。
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();
}
}
6485

被折叠的 条评论
为什么被折叠?



