Spring(5)——IoC DI
初步理解IoC & DI
什么是Spring?
用一句话概括就是:Spring是包含了众多工具方法的IoC容器
那么问题又来了,什么是容器?什么是IoC容器?
1.0 什么是容器
容器是⽤来容纳某种物品的(基本)装置。⸺来自:百度百科
在开发中: List/Map是数据存储容器,Tomcat是Web容器。
1.1 什么是IoC
在早期的开发中和对于初学者的学习过程中,整个项目中对象的数量不多,对象的属性不多,对象之间的依赖不强。
但是随着技术的发展,一个项目中对象数量越来越多,属性越来越多,对象之间的依赖也越来越复杂。甚至于多个对象构成一个组件,组件之间的依赖也很复杂。
此时我们就需要一个平台,平台负责对象的生成和装配。
作为开发者我们只需要将对象的生产方法告诉平台,即更多的关注功能的实现。
而这个平台就是Spring的IoC容器,平台所提供的装配好依赖关系的对象,在Spring中叫做Bean。
再通过一个场景去理解:在一个项目中两个服务都需要连接数据库,我们需要分别创建连接数据库的对象,同时还需要创建一些依赖对象,但是有了IoC容器后,容器会创建好一个连接对象,当我们的服务中需要数据库连接对象的时候,只需要进行相应注解的标注,容器就会自动将这个依赖对象注入。
IoC是Spring的核⼼思想,本质是 “将程序的控制权交给框架”,开发者不再需要手动管理对象和依赖,而是由容器统一调度。
IoC:Inversion of Control(控制反转),也就是说Spring是⼀个"控制反转"的容器。
这个反转主要体现在以下几个方面:
- 对象创建权的反转
-
传统方式:开发者需要手动通过
new
关键字创建对象,并显式管理对象之间的依赖关系(例如通过构造函数或setter
方法传递依赖)。// 传统方式:开发者手动创建对象和依赖 UserService userService = new UserService(); UserDao userDao = new UserDao(); userService.setUserDao(userDao);
-
IoC 方式:对象的创建、依赖注入、生命周期管理均由容器(如 Spring)负责。开发者只需通过配置(XML/注解)或代码定义对象的依赖关系,容器会自动完成对象的实例化和依赖装配。
// Spring IoC:容器自动注入依赖 @Service public class UserService { @Autowired // 容器自动注入 UserDao private UserDao userDao; }
其实IoC我们在前⾯已经使⽤了,我们在前⾯讲到,在类上⾯添加 @RestController 和@Controller 注解,就是把这个对象的创建方法交给Spring管理,Spring框架启动时就会加载该类,在容器中创建该类的对象。把对象交给Spring管理,就是IoC思想。
- 依赖管理权的反转
-
传统方式:对象需要主动获取依赖(例如从工厂类或全局单例中获取),导致代码与具体实现强耦合。
// 传统方式:对象主动获取依赖(紧耦合) public class UserService { private UserDao userDao = DaoFactory.getUserDao(); }
-
IoC 方式:依赖由容器主动“注入”到对象中,对象是被动接受依赖。这种方式解耦了对象和依赖的具体实现,依赖关系由外部(容器)定义。
// Spring IoC:依赖由容器注入(松耦合) public class UserService { private UserDao userDao; // 依赖由容器注入 }
- 控制流的反转
-
传统程序:代码逻辑由开发者编写的代码直接控制,主流程由开发者显式调用。
public static void main(String[] args) { UserService userService = new UserService(); userService.doSomething(); }
-
IoC 程序:程序的主流程由框架(如 Spring)控制,开发者只需通过配置或注解定义组件,框架负责调度和执行。
@SpringBootApplication public class MyApp { public static void main(String[] args) { SpringApplication.run(MyApp.class, args); // 控制权交给 Spring } }
可以理解为传统程序中,开发者要显式的点击开始,然后程序就开始按照提前写好的顺序去执行了,在IoC程序中,点击开始,程序不会按照某个顺序去执行,流程由框架驱动,开发者只需定义组件和处理逻辑,框架决定何时调用它们。
关键总结
- 反转的核心:将对象的创建、依赖管理、程序流程的控制权从开发者手中转移到容器或框架。
- 解决的问题:解耦组件之间的依赖关系,使代码更灵活、可维护性更高(例如通过依赖注入实现面向接口编程)。
- 实际体现:开发者不再需要编写
new
、不再需要手动管理依赖关系、不再需要控制程序的主流程。
通过 IoC,开发者可以更专注于业务逻辑的实现,而非底层对象的创建和管理。
1.2 什么是DI
在 Spring 框架中,依赖注入(Dependency Injection) 是实现控制反转(IoC)的核心技术,它通过容器自动管理对象之间的依赖关系。
下面通过一个小案例来理解依赖注入。
这是原本的代码:
//用来提供数据
public class BookDao {
public List<BookInfo> mockData() {
List<BookInfo> books = new ArrayList<>();
for (int i = 0; i < 5; i++) {
BookInfo book = new BookInfo();
book.setId(i);
book.setBookName("书籍" + i);
book.setAuthor("作者" + i);
book.setCount(i * 5 + 3);
book.setPrice(new BigDecimal(new Random().nextInt(100)));
book.setPublish("出版社" + i);
book.setStatus(1);
books.add(book);
}
return books;
}
}
//使用数据
public class BookService {
private BookDao bookDao = new BookDao();
public List<BookInfo> getBookList() {
List<BookInfo> books = bookDao.mockData();
for (BookInfo book : books) {
if (book.getStatus() == 1) {
book.setStatusCN("可借阅");
} else {
book.setStatusCN("不可借阅");
}
}
return books;
}
}
可以看到在BookService中我们主动new了一个BookDao对象。这里需要介绍两个注解
@Component & @Autowired
1. @Component
:标记类为 Spring Bean
- 作用:告诉 Spring 容器,“这个类需要被管理,请把它创建成一个 Bean”。
- 使用场景:标记任意需要被 Spring 管理的类(如工具类、服务类、数据访问类)。
2. @Autowired
:自动注入依赖
- 作用:告诉 Spring 容器,“请把匹配的 Bean 自动注入到这个字段/构造器/方法中”。
- 使用场景:在需要依赖其他 Bean 的地方(如 Service 依赖 DAO)。
将这两个注解运用到上面的案例中:
//告诉 Spring 容器,“这个类需要被管理,请把它创建成一个 Bean”。
@Component
public class BookService {
//告诉 Spring 容器,“请把匹配的 Bean 自动注入到这个字段/构造器/方法中”。
@Autowired
private BookDao bookDao;
public List<BookInfo> getList(){
List<BookInfo> books = bookDao.mockData();
//处理⻚⾯展⽰
for (BookInfo book:books){
if (book.getStatus()==1){
book.setStatusCN("可借阅");
}else {
book.setStatusCN("不可借阅");
}
}
return books;
}
}
//告诉 Spring 容器,“这个类需要被管理,请把它创建成一个 Bean”。
@Component
public class BookDao {
public List<BookInfo> mockData() {
List<BookInfo> books = new ArrayList<>();
for (int i = 0; i < 5; i++) {
BookInfo book = new BookInfo();
book.setId(i);
book.setBookName("书籍" + i);
book.setAuthor("作者" + i);
book.setCount(i * 5 + 3);
book.setPrice(new BigDecimal(new Random().nextInt(100)));
book.setPublish("出版社" + i);
book.setStatus(1);
books.add(book);
}
return books;
}
}
先给类做**@Component**注解告诉Spring这两个类需要被管理,把他们创建成Bean,然后在BookService中需要new BooKDao()的地方做
@Autowired注解。做了这两步之后,每当需要BookService的getList()服务的时候,Spring会自动把BookDao的Bean注入到@Autowired标记过的地方。
上面这个过程就体现了依赖注入(Dependency Injection)。
1.3 Bean的声明
1.4 Bean的注入
当一个Bean有多个实现类的时候,并且他们都有@Component或者其衍生注解的时候,Spring就不知道注入哪个了。
此时有三个注解可以用来解决这个问题:
-
**@Primary:**当一个类标记了@Component或者其衍生注解的类添加@Primary注解的时候,Spring在注入Bean时会优先注入该类实现的Bean。
-
@Qualifier: 使用该注解配合@Autowired,可以指定当前注入哪个Bean。
-
**@Resource:**直接使用该注解标记注入处,不使用@Autowired。
@Resource与@Autowired的区别:
- @Autowired默认按照类型进行注入,而@Resource按照名称进行注入。
- @Autowired是Spring框架提供的注解,而@Resource是JDK提供的注解。