一、什么是 Spring IoC?
1. 先搞懂:IoC是什么
IoC全称 Inversion of Control (控制反转),咱先不说Spring IOC概念,我们首先按照字面意思给你举个生活的例子来对比一下
假设你要装一套新房:
方式1在没有IOC下:你就是业主,要亲手搞定所有事情,做的事情包括不限于:自己找工人,自己买材料,自己管流程,自己扛风险
方式2在IOC下:你只需要做 2 件事,告诉装修公司需求:比如说我要简约风格、预算 30 万、3 个月完工。最后验收房子:装修公司把所有细节搞定,你直接拎包入住,剩下的所有事,全由装修公司负责,找工人、买材料等等
这就是控制权反转—— 原本由业主掌控的工人招聘、材料采购、流程管理的权力,反转给了装修公司(Spring IoC 容器)。你不再管细节,只关心最终需求和结果验收
所以控制反转就是控制权反转,而 Spring IoC 的核心就是反转控制权:将对象的创建、依赖(DI)的注入、生命周期的管理全部交给 Spring 容器负责,开发者不再手动控制
2.Spring IoC 容器:IoC 的 “执行者”
Spring IoC 容器是实现 IoC 思想的核心载体,如果 Spring IOC是个思想,那Spring IOC容器就是这个思想的具体实现工具或载体,当需要某个对象时,创建对象的任务交给容器, 程序中只需要依赖注⼊(DependencyInjection,DI)就可以了,其实笼统的来说Spring是⼀个IoC容器,所以有时Spring也称为Spring容器
二、Bean 的存储
Bean 的存储核心是 Spring 容器(IoC 容器) 对对象的 “注册 - 实例化 - 管理” 过程 ,本质是将 Bean 的元数据(类信息、依赖、配置)注册到容器,再由容器根据规则创建实例并存储,供后续依赖注入或直接获取,简单的说将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对象就是Bean的存储
1. 注解驱动
要让 Spring 容器存储 Bean,首先需要通过某种方式定义 Bean(即告诉 Spring “哪个类需要被管理”),这种方式之一就是注解,Spring框架为此提供了更丰富的注解
共有两类注解类型可以实现:
- 类注解:@Controller、@Service、@Repository、@***ponent、@Configuration
- 方法注解:@Bean
@Controller(控制器存储)
从Spring容器中获取对象
获得对象后,调用print方法,测试结果为HelloController对象中的print方法的打印结果
那如果把@Controller删掉呢?
结合上述结果可以得知: @Controller 所标识的类会被 Spring 容器注册为 Bean,也就是将控制权反转给了Spring管理 ,从而可以能从Spring中获取到Controller对象来使用
获取bean对象的其他⽅式
主要为三次方式:
- 根据类型获得Bean
- 根据Bean名称和类型获得Bean
- 根据Bean名称获得Bean
什么是bean名?
Spring bean是Spring框架在运⾏时管理的对象,Spring会给管理的对象起⼀个名字.⽐如学校管理学⽣,会给每个学⽣分配⼀个学号,根据学号,就可以找到对应的学⽣.Spring也是如此,给每个对象起⼀个名字,根据Bean的名称(BeanId)就可以获取到对应的对象
怎么知道bean名为什么?
如何知道bean名为什么,这一点在spring 官方文档中有详细说明
程序开发⼈员不需要为bean指定名称(BeanId),如果没有显式的提供名称(BeanId),Spring容器将为该bean⽣成唯⼀的名称.
命名约定使⽤Java标准约定作为实例字段名.bean名称以⼩写字⺟开头(当第⼀个和第⼆个字符都是⼤写时,将保留原始的⼤⼩写),然后使⽤驼峰式大小写,也就是说在此处HelloerController的bean名为helloController,但是注意此处的getBean为object类性,需要强制转换到对应的bean对象类型
2. 类注解总结
其实这些注解本质都是 @***ponent 的 “派生注解”,这些类注解的存在是为了明确类的职责分层,让代码更具可读性,Spring 扫描时会将它们统一识别为 Bean 并纳入容器管理,只是在 “语义” 和 “附加功能” 上做了区分。
源码:
从源码中可知并无太大区别
“语义” 和 “附加功能”区别
| 注解 | “语义” 和 “附加功能” |
|---|---|
| @***ponent | 通用组件 |
| @Controller | 控制层,接收请求,对请求进⾏处理,并进⾏响应 |
| @Servie | 业务逻辑层,处理具体的业务逻辑 |
| @Repository | 数据访问层,也称为持久层.负责数据访问操作 |
| @Configuration | 配置层.处理项⽬中的⼀些配置信息. |
注意:
这5个注解, @Controler不可以和其他注解替换,在控制层中必须要使用@Controller
在不固定场景下,控制层使用@Service也可能能访问成功
3. ⽅法注解@Bean
上述所说的注解是添加到某个类上的类注解,但是存在两个问题:
- 外部依赖包中的类,无法手动为其添加类注解
- 同一个类需要创建多个实例(比如多个不同配置的数据源)
这种场景, 我们就需要使⽤⽅法注解@Bean
⽅法注解@Bean的使⽤
测试结果:
@Bean 必须搭配五大注解类使用,否则无法被 Spring 扫描识别,提示错误
定义多个对象
一个 @Bean 方法对应一个 Bean 实例,在同一个类中,通过编写多个 @Bean 方法,即可注册多个对象到 Spring 容器
测试结果:
为什么这里我直接使用BeanName而不直接Student类呢,核心原因就是:同一类型有多个 Bean时,按类型获取会报错(Spring 无法确定选定的是s1,还是s2),而容器中 Bean 名称是唯一的
@Bean注解的bean,bean的名称就是它的⽅法名
当@Bean方法中传参
String name="hr";
@Bean
public Student s1(String name){
return new Student(name,1);
}
Spring 会从Spring容器当中, 查找String类型的对象,赋值给name
@Bean的重命名
@Bean 的重命名本质是通过注解的 name 或 value 属性 自定义 Bean 的名称,替代默认的方法名作为 Bean 在 Spring 容器中的标识。
name 和 value 属性完全等价
测试结果:
这里就是通过重命名student1,student2来代替s1方法名
三、扫描路径
在刚刚开始搭建spring boot的时候,我们有学到过默认扫描的范围是SpringBoot启动类所在包及其⼦包的@controller,但是随着我们深入学习,难道这个范围就是固定的吗,显然不是,这就引入了 @***ponentScan注解
@***ponentScan
@***ponentScan就是告诉 Spring:“从这些包路径下,查找并加载所有带候选注解的类,将它们注册为 Bean”
操作@***ponentScan来更改扫描的范围
测试结果:
四、最后再说一下
总结一下,本篇IOC内容就一句话 “注解定标识,扫描定范围,@Bean 补灵活”,还有本篇文章没有怎么提及依赖(DI)的内容,在Spring IOC中IOC和DI是 “目标 - 手段” 的统一关系,对应DI的理解也同样很重要,下篇文章将重点讲解