个人总结就是,原本我们的对象需要自己new,现在不需要,但与之替代的是写配置,告诉IOC哪些对象不用new,你给我搞了,我朝你要,朝你要的过程就是创建IOC容器,并调用getBean()方法。
至于DI,个人理解是我造汽车前要轮子,IOC去给我把轮子准备好,这就是IOC创建,然后把轮子放我这来,这就是DI(总不能造好了我自己取吧),和原本相比就是,service里面需要dao,之前dao我们是自己new的,现在不需要,现在我们用构造器,或者setter,又或者是@Autowire (自动注入)
一、什么是控制反转(Inversion of Control)?
控制反转是一种设计思想 ,核心是将对象的创建、管理和依赖关系的控制权从应用程序代码本身转移到外部容器(IOC 容器),实现 "反转" 对象的控制权。
1.1 传统方式 vs IOC 方式:理解 "反转" 的本质
维度 | 传统方式(主动控制) | IOC 方式(被动接收) |
---|---|---|
对象创建 | 开发者在代码中主动通过new 关键字创建对象(如UserService service = new UserService(); ) |
对象由 IOC 容器创建,开发者通过容器 "获取" 对象(如UserService service = container.getBean("userService"); ) |
控制权归属 | 应用程序代码(开发者手动控制对象的生命周期) | 外部 IOC 容器(容器统一控制对象的创建、初始化、销毁) |
依赖管理 | 对象自己负责创建依赖(如UserService 中new UserDao() ) |
依赖由容器自动 "注入" 到对象中,对象无需关心依赖的创建 |
举例说明:
- 传统方式:你(开发者)需要喝奶茶时,亲自去买(
new 奶茶()
),全程自己控制。 - IOC 方式:你告诉奶茶店(IOC 容器)你要喝奶茶,奶茶店做好后直接递给你(容器提供对象),你无需关心制作过程,控制权在奶茶店。
二、IOC 容器:对象的 "管家"
IOC 容器是实现控制反转思想的具体载体,它负责管理应用中所有被纳入管理的对象(称为 "Bean"),并协调对象之间的依赖关系。
2.1 Bean:IOC 容器管理的对象
"Bean" 是 IOC 容器中管理的对象(可以是 Service、Dao、工具类等)。容器对 Bean 的管理包括:
- 创建:根据配置(如 XML、注解)实例化 Bean;
- 初始化 :调用初始化方法(如
init-method
); - 依赖注入:将关联的 Bean 注入到当前 Bean 中;
- 销毁 :容器关闭时调用销毁方法(如
destroy-method
)。
2.2 IOC 容器的核心功能
-
Bean 的生命周期管理
容器从 Bean 的创建到销毁全程托管,开发者无需手动处理对象的创建和销毁,减少资源泄露风险。
-
依赖关系绑定(依赖注入 DI)
当 Bean 之间存在依赖(如
UserService
依赖UserDao
),容器会自动将依赖的 Bean "注入" 到目标 Bean 中,无需开发者在代码中手动创建依赖。 -
配置灵活性
通过配置(注解、XML、配置类)定义 Bean 的创建规则和依赖关系,无需修改代码即可调整对象的创建逻辑。
三、依赖注入(DI):IOC 的实现手段
依赖注入(Dependency Injection)是实现控制反转的具体方式:当一个对象(A)依赖另一个对象(B)时,IOC 容器会主动将 B 实例注入到 A 中,而不是由 A 自己创建 B。
3.1 DI 的核心思想
"谁依赖谁,依赖什么,谁注入谁,注入什么"
- 谁依赖谁:应用程序中的 Bean(如
UserService
)依赖 IOC 容器; - 依赖什么:Bean 依赖容器提供的其他 Bean(如
UserService
依赖UserDao
); - 谁注入谁:IOC 容器向 Bean 注入依赖;
- 注入什么:容器将依赖的 Bean 实例注入到目标 Bean 中。
3.2 依赖注入的常见方式
-
构造器注入
通过 Bean 的构造方法传递依赖,容器在创建 Bean 时调用构造器并传入依赖的实例。
java
运行
javapublic class UserService { private UserDao userDao; // 构造器注入:容器通过此构造器传入UserDao实例 public UserService(UserDao userDao) { this.userDao = userDao; } }
-
setter 方法注入
通过 Bean 的 setter 方法设置依赖,容器在创建 Bean 后调用 setter 传入依赖的实例。
java
运行
javapublic class UserService { private UserDao userDao; // setter注入:容器调用此方法传入UserDao实例 public void setUserDao(UserDao userDao) { this.userDao = userDao; } }
-
字段注入(注解方式)
通过注解(如 Spring 的
@Autowired
)直接在字段上标记依赖,容器自动将依赖注入到字段中。java
运行
javapublic class UserService { // 字段注入:容器自动将UserDao实例注入到该字段 @Autowired private UserDao userDao; }
四、IOC 的优势
-
降低耦合度
对象之间的依赖关系由容器管理,而非硬编码在代码中,减少了对象之间的直接关联(如
new
操作)。 -
提高代码复用性
容器管理的 Bean 可被多个对象共享,无需重复创建。
-
便于测试
依赖由容器注入,测试时可轻松替换依赖为模拟对象(如 Mock),无需修改原代码。
-
简化开发
开发者无需关注对象的创建和依赖管理,专注于业务逻辑实现。
-
增强扩展性
通过修改配置即可替换 Bean 的实现(如将
MySQLDao
替换为OracleDao
),无需修改依赖它的代码。
五、常见的 IOC 容器
- Spring 容器:Java 生态中最流行的 IOC 容器,支持 XML、注解、Java 配置类等多种方式定义 Bean 和依赖。
- Google Guice:轻量级 IOC 容器,基于注解实现依赖注入。
- PicoContainer:轻量级容器,专注于最小化和高性能。
六、总结
-
IOC(控制反转) 是一种思想:将对象的控制权从应用程序转移到外部容器。
-
IOC 容器是实现这一思想的工具:负责 Bean 的创建、生命周期管理和依赖绑定。
-
DI(依赖注入) 是 IOC 的具体实现:容器将依赖的 Bean 注入到目标 Bean 中,解决对象之间的依赖关系。
通过 IOC,应用程序的代码结构更清晰、耦合度更低、可维护性和扩展性更强,是现代框架(如 Spring)的核心思想之一。