Spring IOC

一、什么是 IoC?

  • 直白点:原来对象的创建、依赖关系要我们自己 new、自己管理;现在交给 Spring 容器来做。
  • 控制反转:控制权从“开发者”反转到“容器”。

例如没有 Spring 时:

UserService userService = new UserService(new UserRepository());

我们需要手动 new,自己决定依赖怎么创建。

有了 Spring 后:

UserService userService = context.getBean(UserService.class);

Spring 容器帮我们:

  1. 创建对象(Bean)
  2. 解决依赖关系(注入)
  3. 管理生命周期

二、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 {
    @Autowired
    private UserRepository userRepository; // Spring注入
}

通俗理解:

  • 控制反转(IoC) = 你不用自己创建对象,Spring 来管。
  • 依赖注入(DI) = Spring 把你需要的依赖塞给你。

常见方式:

  1. 构造器注入(推荐)

    //构造器注入(Spring Boot 下可以省略 @Autowired)
    @Component
    public class UserService {
        private final UserRepository userRepository;
        @Autowired
        public UserService(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    }
    
  2. Setter 注入

    @Component
    public class UserService {
        private UserRepository userRepository;
        @Autowired
        public void setUserRepository(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    }
    
  3. 字段注入

    @Component
    public class UserService {
        @Autowired
        private UserRepository userRepository;
    }
    

依赖注入的原理(源码角度)

  1. 扫描 & 解析 BeanDefinition
    • @ComponentScan 会找到 @Component@Service@Repository@Controller 标注的类。
    • 这些类会被解析成 BeanDefinition(保存类信息、依赖信息、作用域等)。
  2. 创建 Bean
    • Spring IOC 容器根据 BeanDefinition 通过反射创建对象。
  3. 依赖注入(DI)
    • Spring 调用 AutowiredAnnotationBeanPostProcessor,解析 @Autowired
    • 找到匹配的 Bean(按类型 → 按名称 → @Qualifier → @Primary)。
    • 通过反射赋值(字段/构造器/Setter)。

依赖查找的顺序:

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 也会默认选这个。


依赖注入的细节

  1. 按类型注入(默认)
    • 如果 IOC 容器中有一个对应类型的 Bean,就直接注入。
    • 如果有多个,会报错 → 除非:
      • @Primary:指定默认首选
      • @Qualifier("beanName"):指定注入哪个
  2. 循环依赖问题
    • Spring 通过 三级缓存(singletonObjects、earlySingletonObjects、singletonFactories)解决了大多数循环依赖。
    • 但是 构造器注入无法解决循环依赖。

五、IoC 的好处

  • 解耦:对象只关心“需要什么”,不关心“如何得到”。
  • 统一管理:Spring 负责对象的创建、初始化、销毁。
  • 可扩展:通过配置或注解就能替换实现,而不用改代码。
  • 便于测试:可以轻松替换依赖。

总结一句话:
Spring IoC 是一种设计思想,用容器来管理对象和它们的依赖关系,开发者只需要声明需求,容器负责提供和维护。


六、Spring IOC 的加载过程

1. 资源定位(Resource 定位配置文件/注解)

  • 容器启动时,先要知道 Bean 的配置来源:
    • XML<bean> 标签
    • 注解@ComponentScan@Bean
    • Java 配置类@Configuration
  • 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 就是通过这个阶段注入属性值的。
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐