Spring IoC 超清晰讲解:Bean、容器、依赖注入全流程
目录
- Spring IOC
- 一、什么是 IoC?
- 二、IoC 的核心:IoC 容器
- 三、IoC 和 Bean 的关系
- 四、IoC 的依赖注入(DI)
- 常见方式:
- 依赖注入的原理(源码角度)
- 依赖查找的顺序:
- 依赖注入的细节
- 五、IoC 的好处
- 六、Spring IOC 的加载过程
- 1. 资源定位(Resource 定位配置文件/注解)
- 2. 解析配置,生成 **BeanDefinition**
- 3. BeanFactoryPostProcessor 阶段(修改 BeanDefinition)
Spring IOC
一、什么是 IoC?
- 直白点:原来对象的创建、依赖关系要我们自己 new、自己管理;现在交给 Spring 容器来做。
- 控制反转:控制权从“开发者”反转到“容器”。
例如没有 Spring 时:
UserService userService = new UserService(new UserRepository());
我们需要手动 new,自己决定依赖怎么创建。
有了 Spring 后:
UserService userService = context.getBean(UserService.class);
Spring 容器帮我们:
- 创建对象(Bean)
- 解决依赖关系(注入)
- 管理生命周期
二、IoC 的核心:IoC 容器
IoC 容器就是一个“大工厂”,里面存放着各种 Bean,**负责整个 Bean 的生命周期管理。**常见实现:
- BeanFactory:IoC 的最基本容器,按需加载(懒加载)。
- ApplicationContext:BeanFactory 的子接口,功能更强大,支持事件发布、国际化、AOP 等,几乎开发中都是用它。
三、IoC 和 Bean 的关系
- Bean:容器管理的对象(Java 类实例)。
- BeanDefinition:Bean 的“元数据”(配方/蓝图),描述如何创建这个 Bean。
- 类的全限定名
- 作用域(singleton、prototype…)
- 初始化方法、销毁方法
- 依赖关系
容器根据 BeanDefinition 来生产 Bean,就像工厂根据图纸来生产零件。
四、IoC 的依赖注入(DI)
Dependency Injection:对象的依赖不是自己去 new,而是由 Spring 注入。
依赖:一个类需要另外一个类来完成工作,比如:
class UserService {private UserRepository userRepository; // 依赖
}
UserService
想操作用户,就必须依赖 UserRepository
。
注入:这个依赖不是 UserService
自己去 new,而是 Spring 帮你塞进去。
@Service
class UserService {@Autowiredprivate UserRepository userRepository; // Spring注入
}
通俗理解:
- 控制反转(IoC) = 你不用自己创建对象,Spring 来管。
- 依赖注入(DI) = Spring 把你需要的依赖塞给你。
常见方式:
-
构造器注入(推荐)
//构造器注入(Spring Boot 下可以省略 @Autowired) @Component public class UserService {private final UserRepository userRepository;@Autowiredpublic UserService(UserRepository userRepository) {this.userRepository = userRepository;} }
-
Setter 注入
@Component public class UserService {private UserRepository userRepository;@Autowiredpublic void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;} }
-
字段注入
@Component public class UserService {@Autowiredprivate UserRepository userRepository; }
依赖注入的原理(源码角度)
- 扫描 & 解析 BeanDefinition
@ComponentScan
会找到@Component
、@Service
、@Repository
、@Controller
标注的类。- 这些类会被解析成 BeanDefinition(保存类信息、依赖信息、作用域等)。
- 创建 Bean
- Spring IOC 容器根据 BeanDefinition 通过反射创建对象。
- 依赖注入(DI)
- Spring 调用
AutowiredAnnotationBeanPostProcessor
,解析@Autowired
。 - 找到匹配的 Bean(按类型 → 按名称 → @Qualifier → @Primary)。
- 通过反射赋值(字段/构造器/Setter)。
- Spring 调用
依赖查找的顺序:
1. 按类型找(byType)
-
比如你写了:
@Autowired private UserRepository userRepository;
Spring 会去 IOC 容器里找 UserRepository 类型的 Bean。
-
如果只有一个,就直接注入。
2. 多个 Bean 时 → 按名称(byName)
-
如果 IOC 里有两个
UserRepository
实现,比如:@Repository("userRepo1") class UserRepositoryImpl1 implements UserRepository {}@Repository("userRepo2") class UserRepositoryImpl2 implements UserRepository {}
那么 Spring 就会看看 变量名
userRepository
,有没有和某个 Bean 的名字匹配。- 如果匹配成功 → 注入对应的 Bean。
- 如果不匹配 → 报错(
NoUniqueBeanDefinitionException
)。
3. @Qualifier 指定
-
如果你明确指定了:
@Autowired @Qualifier("userRepo1") private UserRepository userRepository;
Spring 就会按照
userRepo1
这个名字去注入。
4. @Primary 标记默认
-
你也可以在某个 Bean 上标记:
@Repository @Primary class UserRepositoryImpl1 implements UserRepository {}
那么即使有多个同类型的 Bean,Spring 也会默认选这个。
依赖注入的细节
- 按类型注入(默认)
- 如果 IOC 容器中有一个对应类型的 Bean,就直接注入。
- 如果有多个,会报错 → 除非:
@Primary
:指定默认首选@Qualifier("beanName")
:指定注入哪个
- 循环依赖问题
- Spring 通过 三级缓存(singletonObjects、earlySingletonObjects、singletonFactories)解决了大多数循环依赖。
- 但是 构造器注入无法解决循环依赖。
五、IoC 的好处
- 解耦:对象只关心“需要什么”,不关心“如何得到”。
- 统一管理:Spring 负责对象的创建、初始化、销毁。
- 可扩展:通过配置或注解就能替换实现,而不用改代码。
- 便于测试:可以轻松替换依赖。
总结一句话:
Spring IoC 是一种设计思想,用容器来管理对象和它们的依赖关系,开发者只需要声明需求,容器负责提供和维护。
六、Spring IOC 的加载过程
1. 资源定位(Resource 定位配置文件/注解)
- 容器启动时,先要知道 Bean 的配置来源:
- XML:
<bean>
标签 - 注解:
@ComponentScan
、@Bean
- Java 配置类:
@Configuration
- XML:
- Spring 通过
ResourceLoader
找到这些配置文件/类。
2. 解析配置,生成 BeanDefinition
- Spring 使用 BeanDefinitionReader 来解析配置。
- 每一个
<bean>
或@Component
→ 转换成一个 BeanDefinition 对象。 - BeanDefinition 就是 Bean 的“元信息”(类似于建模),里面存放:
- Bean 的 class 类型
- 作用域(scope)
- 是否懒加载(lazy-init)
- 属性值、依赖信息
- Spring 把这些 BeanDefinition 存到一个 BeanDefinitionMap(ConcurrentHashMap) 里。
- Key = Bean 名称
- Value = BeanDefinition
3. BeanFactoryPostProcessor 阶段(修改 BeanDefinition)
在 BeanDefinition
和 完整BeanDefinition
中间通过一个后置增强器,可以对bean的定义信息进行统一修改,只需要实现 BeanFactoryPostProcessor
接口即可,这个后置增强器是可以有多个的,你只要在不同的类实现多个 BeanFactoryPostProcessor
接口就会执行多次
- 比如:
@ConfigurationProperties
就是通过这个阶段注入属性值的。