Spring IOC 容器和依賴注入(DI)
1、什么是 IOC?
IOC(Inversion of Control)控制反轉,IOC的核心是將對象的創建和依賴關系的組裝控制權從程序內部反轉到外部容器。容器管理的是Bean的生命周期和依賴關系,而“對象之間的調用過程”通常是由業務邏輯本身決定的,容器并不管理“調用過程”
我們使用IOC的原因就是為了降低類之間的耦合度
1.1 Spring 實現 IOC 思想
- Spring 提供了一個容器,稱為
IOC容器,用來充當IOC思想中的外部 - IOC 容器負責對象的創建、初始化等一系列工作,被創建或被管理的對象在 IOC 容器中稱為
Bean
1.2 為什么要有 IOC?
目的:充分解耦
- 使用 IOC 容器管理 bean(IOC)
- 在 IOC 容器內將有依賴關系的 bean 進行關系綁定(DI)
最終效果:
- 使用對象時不僅可以直接從 IOC 容器中獲取,并且獲取到的 bean 已經綁定了所有的依賴關系
2、IOC 底層原理
最主要的目的就是降低代碼的耦合度,盡量地降到最低限度
- xml 解析
- 工廠模式
- 反射
2.1 工廠模式和原始模式
原始模式
弊端:代碼之間的耦合度高,UserService直接依賴于UserDao的具體實現,每一次調用UserService.execute()都要創建一次UserDao對象,這樣會非常浪費jvm堆內存資源,
class UserService {
execute() {
UserDao userDao = new UserDao();
userDao.add();
}
}
class UserDao {
add() {
// CODE...
}
}
2.1.1 工廠模式
相比于原始模式的優點:代碼耦合度降低,在啟動項目的時候,UserFactory會先創建一個UserDao對象存放到堆中,每次UserService需要調用UserDao.add()的時候,只需要在堆直接調用即可,可以集中管理對象的生命周期,避免重復創建對象
class UserService {
execute() {
UserDao userDao = UserFactory.getDao();
userDao.add();
}
}
class UserFactory {
private static UserDao userDao = new UserDao();
public static UserDao getDao(){
return userDao; // 返回同一個實例
}
}
class UserDao {
add() {
// CODE...
}
}
2.1.2 xml 解析
ioc 過程
第一步:xml 配置文件,配置創建的對象
<bean id="UserDao" class="com.lantz.UserDao"></bean>
第二步:假設現在有了UserService和UserDao類,創建工廠類
public class UserFactory {
public static UserDao getDao(){
String classValue = class屬性值;
Class clazz = Class.forName(classValue);
return (UserDao)clazz.newInstance();
}
}
至此,進一步降低耦合度
因此,IOC的底層基于 IOC容器完成,IOC 的底層思想就是創建工廠,利用工廠模式降低耦合度
3、Spring 提供給 IOC 的兩個接口
BeanFactory
- IOC容器基本實現,Spring 內部實現的接口,不提供給開發人員使用
- 加載配置文件的時候不會創建對象,只有獲取對象的時候(使用)才會創建對象
ApplicationContext(推薦)
BeanFactory接口的子接口,提供了更多的更強大的功能,一般由開發人員直接使用- 加載配置文件的時候就會把配置文件對象進行創建
4、DI 依賴注入
依賴注入的概念
DI(Dependency Injection)依賴注入:
- 在容器中建立 bean 與 bean 之間的依賴關系的整個過程,即依賴注入
依賴注入指的是:當一個對象依賴于其他對象(或數據)時,由Spring 容器負責在運行的時候將這些依賴注入進去,而不是由對象本身去查找或創建
一句話:我要什么就由容器給,不用自己弄(new)
代碼演示
數據層實現
public class BookDaoImpl implements BookDao{
@Override
public void save() {
System.out.println("book dao impl ...");
}
}
業務層實現
public class BookServiceImpl implements BookService{
private BookDao bookDao;
@Override
public void save() {
bookDao.save();
}
}
圖示:
整體圖示:

IOC容器內

4.1 依賴注入方式
我們在向一個類傳遞數據的方式主要是兩種:set方法、構造方法。依賴注入描述了在容器中建立了bean與 bean之間依賴關系的過程,bean運行需要的類型有:引用類型,簡單類型(基本數據類型與string)
setter 注入
通過類的setter方法完成依賴注入
簡單類型
<bean id="student" class="com.example.Student">
<property name="name" value="Tom"/>
<property name="age" value="20"/>
</bean>
public class Student {
private String name;
private int age;
// setter方法
public void setName(String name) { this.name = name; }
public void setAge(int age) { this.age = age; }
}
引用類型
<bean id="school" class="com.example.School">
<property name="name" value="清華大學"/>
</bean>
<bean id="student" class="com.example.Student">
<property name="name" value="Tom"/>
<property name="school" ref="school"/>
</bean>
ref用于注入引用類型(bean)
public class Student {
private String name;
private School school;
public void setName(String name) { this.name = name; }
public void setSchool(School school) { this.school = school; }
}
構造器注入(推薦)
通過類的構造方法來完成依賴項注入(推薦)
簡單類型
<bean id="student" class="com.example.Student">
<constructor-arg name="name" value="Tom"/>
<constructor-arg name="age" value="20"/>
</bean>
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
引用類型
<bean id="school" class="com.example.School">
<constructor-arg name="name" value="清華大學"/>
</bean>
<bean id="student" class="com.example.Student">
<constructor-arg ref="school"/>
<constructor-arg value="Tom"/>
</bean>
public class Student {
private School school;
private String name;
public Student(School school, String name) {
this.school = school;
this.name = name;
}
}
5、依賴自動裝配
5.1 什么自動裝配
自動裝配是 Spring 用來自動滿足 bean 之間依賴關系的一種機制
5.2 自動裝配的幾種方式
| 值 | 含義 | 裝配方式 | 說明 | 適用場景 |
|---|---|---|---|---|
no(默認) |
不自動裝配 | 無 | 所有依賴都需要手動使用 <property> 或 <constructor-arg> 指定 |
控制最嚴格,適合顯式配置 |
byName(不推薦) |
按 屬性名 自動裝配 | Setter 方法 | 容器中存在 與屬性名相同 ID 的 bean 時自動注入 | 當 bean 命名規范統一時很方便 |
byType(推薦) |
按 類型 自動裝配 | Setter 方法 | 容器中存在 與屬性類型匹配的 bean 時自動注入;若有多個同類型 bean 會報錯 | 當同類型 bean 唯一時最常用 |
constructor |
按 構造方法參數類型 自動裝配 | 構造器 | 容器會根據構造函數參數類型自動選擇匹配的 bean 進行注入 | 構造器注入場景 |
default |
使用上級 <beans> 元素的默認設置 |
依賴 <beans default-autowire=""> |
若 <beans> 設置了默認裝配方式,則該 bean 自動繼承該策略 |
批量統一配置 bean 的裝配策略 |
代碼模擬
xml 配置
<bean id="bookDao" class="com.springbean.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.springbean.service.impl.BookServiceImpl" autowire="byType"/>
bookDao 實現類
public class BookDaoImpl implements BookDao {
private int bookName;
public void setBookName(int bookName) {
this.bookName = bookName;
}
@Override
public void save() {
System.out.println("保存書籍...");
}
}
bookService 實現類
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public BookServiceImpl() {
System.out.println("service constructor...");
}
@Override
public void schedule() {
System.out.println("預定了一本書...");
bookDao.save();
}
public void setBookDao(BookDao bookDao) {
System.out.println("service set ...");
this.bookDao = bookDao;
}
}
測試函數
public class BookMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
applicationContext.registerShutdownHook();
BookService bookService = (BookService) applicationContext.getBean("bookService");
bookService.schedule();
}
}
BookServiceImpl 里有一個 BookDao 類型的屬性
容器中存在一個 BookDaoImpl(類型匹配):
? 所以自動把 bookDao 注入到 bookService 里,就不要在測試函數添加一下這句了
BookDao bookDao = (BookDao) applicationContext.getBean("bookDao");

浙公網安備 33010602011771號