Spring Ioc源码引入:什么是IoC,IoC解决了什么问题

什么是IoC

用一个故事举例🌰:

小陈想开一家咖啡店,于是独自创业。找咖啡豆供应商、买咖啡机、招员工,样样都要自己来。开店成本很高。后来,小陈选择加盟连锁咖啡店。总部帮他对接供应商、提供咖啡机,还负责员工培训。小陈只需按流程做好咖啡服务顾客,经营轻松许多。

独立开店时,小陈事事操心,就像没有 IOC,系统耦合度高,难以维护与扩展。加盟后,总部统一管理资源,小陈专注核心业务,如同引入 IOC,降低耦合度,经营变得高效。

上面的故事你可能还无法对应到程序开发场景中,下面我们进行Java代码模拟

用Java代码模拟:

  1. 没有IOC

如下可以看到构造一个咖啡店,需要依赖服务员、清洁工、咖啡豆供应商、以及咖啡机

所以,当我们需要开一个咖啡店的时候,需要自己处理好这些依赖,对应到下面代码,我们自己找到合适的服务员、清洁工、咖啡豆供应商、以及咖啡机,然后开店:

Java 复制代码
public class CoffeeShop {
    // 服务员
    private Waiter waiter;
    // 清洁工
    private Cleaner cleaner;
    // 咖啡豆供应商
    private CoffeeBeansSupplier coffeeBeansSupplier;
    // 咖啡机
    private CoffeeMachine coffeeMachine;
    public CoffeeShop(Waiter waiter,
                      Cleaner cleaner,
                      CoffeeBeansSupplier coffeeBeansSupplier,
                      CoffeeMachine coffeeMachine) {
        this.waiter = waiter;
        this.cleaner = cleaner;
        this.coffeeBeansSupplier = coffeeBeansSupplier;
        this.coffeeMachine = coffeeMachine;
    }
    public void saleCoffee() {

    }
}
Java 复制代码
public class BootStrap {

    public static void main(String[] args) {

        // 我们自己找到合适的服务员
        Waiter waiter = null;
        // 我们自己找到合适的清洁工
        Cleaner cleaner = null;
        // 我们自己找到合适的咖啡供应商
        CoffeeBeansSupplier coffeeBeansSupplier = null;
        // 我们自己找到合适的咖啡机
        CoffeeMachine coffeeMachine = null;
        // 开店,构造自己的咖啡店
        CoffeeShop cacheShop = new CoffeeShop(waiter, cleaner, coffeeBeansSupplier, coffeeMachine);

        // 开始售卖咖啡
        cacheShop.saleCoffee();
    }
}
  1. 具备IOC(以SpringBoot 为例)

首先我们使用Autowired注解来描述CoffeeShop依赖服务员、清洁工、咖啡豆供应商、以及咖啡机

Java 复制代码
public class CoffeeShop {

    // 表明我们需要服务员
    @Autowired
    private Waiter waiter;
    // 表明我们需要清洁工
    @Autowired
    private Cleaner cleaner;
    // 表明我们需要咖啡供应
    @Autowired
    private CoffeeBeansSupplier coffeeBeansSupplier;
    // 表明我们需要咖啡机器
    @Autowired
    private CoffeeMachine coffeeMachine;

    public void saleCoffee() {

    }
}
Java 复制代码
@SpringBootApplication
public class BootStrap {

    public static void main(String[] args) {
        // ctx就如同总部
        ConfigurableApplicationContext ctx = SpringApplication.run(BootStrap.class, args);
        
        // ctx.getBean就如同总部处理好各种依赖(服务员、清洁工、咖啡豆供应商、以及咖啡机)
       
        CoffeeShop shop = ctx.getBean(CoffeeShop.class);
        
         // 我们可以直接进行开店
        shop.saleCoffee();
    }
}

再理解IoC:

IOC的全称是Inversion of Control,即控制反转。控制反转,从字面理解,就是控制权的反转。

  • 没有IOC:传统的程序流程是由开发者自己控制的,比如对象A需要对象B,那么A会直接创建B或者通过工厂类获取B的实例。这种情况下,控制权在A手里

  • 有了IOC:而IoC则是将这种控制权交给外部容器或框架,由外部来管理对象的创建和依赖关系。比如,通过依赖注入,对象A不需要自己创建B,而是由外部容器将B注入到A中。这样,控制权就从A转移到了容器,这就是所谓的反转。

不同的理解:有的人也认为是从程序员手里,转移到IOC容器

结合上面的例子:

  • 没有IOC:咖啡店需要的各种依赖,需要小陈自己处理,自己找咖啡豆供应商等等,然后开店

  • 有了IOC:咖啡店的所有配置,由总部这个容器来统一安排

IoC解决了什么问题

IOC(控制反转)主要解决对象间耦合度过高的问题。在传统编程中,对象直接通过new关键字创建依赖对象,导致代码高度耦合,难以维护和扩展。IOC将对象的创建、依赖管理和生命周期交给外部容器,从而解耦组件。

在上面场景中,总部就是我们的IOC容器,它管理了众多不同的咖啡供应商,咖啡机,可以根据你开店的需求,为你的咖啡店进行装配。

  1. 那么现在思考一下如何实现IOC呢?

IOC容器需要给CoffeeShop自动的填充依赖------依赖注入(Dependency Injection, DI)

  1. 何为依赖:

    如果一个对象A缺少另外一个对象B那么将无法工作(方法不可用)那么我们可以说,A依赖B

那么如何实现依赖注入呢?

如何实现IoC or DI

3.1 如何描述依赖

首先需要清楚如何描述依赖

在Java中一般有三种方式:

  1. 字段+注解

    如下面代码中的waiter,标注@Autowired说明这个字段需要进行依赖注入

  2. 构造器

    如下面的构造方法,每一个方法参数都可以视为CoffeeShop在描述自己依赖哪些外部组件

  3. setter方法

如下面的setCleaner,同样是实验@Autowired来说明自己依赖Cleaner

Java 复制代码
public class CoffeeShop {
    @Autowired //字段+注解
    private Waiter waiter;

    private Cleaner cleaner;
    private CoffeeBeansSupplier coffeeBeansSupplier;
    private CoffeeMachine coffeeMachine;
    
    // 构造器
    public CoffeeShop(Waiter waiter,
                      Cleaner cleaner,
                      CoffeeBeansSupplier coffeeBeansSupplier,
                      CoffeeMachine coffeeMachine) {
        this.waiter = waiter;
        this.cleaner = cleaner;
        this.coffeeBeansSupplier = coffeeBeansSupplier;
        this.coffeeMachine = coffeeMachine;
    }

    @Autowired // setter方法
    public void setCleaner(Cleaner cleaner) {
        this.cleaner = cleaner;
    }
}

当然Spring还可以使用注解和工厂方法,这里为了方便粉丝理解,不做过多扩展。

3.2 如何进行依赖注入

如上,我们完成了描述依赖的过程,那么如何进行依赖注入呢?

在java一般来说有两种方式:反射和生成代码

  1. 反射:

    java提供的反射,允许程序在 运行时 动态地获取类的信息(如类名、方法、字段、注解等),并能直接操作类或对象(如创建实例、调用方法、访问私有字段)

  2. 反射调用方法:可以用于实现基于构造器和setter方法的依赖注入,将依赖项作为参数进行传入,然后反射调用方法即可

  3. 反射访问字段:可以用于实现基于字段的依赖注入,找到匹配要求的对象,反射为字段赋值即可

  4. 生成代码:

    例如 Google 维护Dagger 2 ,会在编译时依赖注入框架,直接通过代码生成实现依赖注入,无需反射或动态代理,启动性能更佳。生成代码的方式在Go语言中运用广泛,主要是Go提供的反射能力没有java那么强大,加上Go强调云原生,对部署速度有较高要求

其中Spring Ioc使用的是反射

Spring Ioc源码学习引入

每个 Spring 开发者都踩过的「坑」


为什么要啃 Spring IoC 源码?

  1. 面试「灵魂拷问」高频区
  • "Spring 如何解决循环依赖?"
  • "BeanFactory 和 ApplicationContext 的区别?"
  • "@Autowired 和 @Resource 注入原理有何不同?"
    绝大部分 Java 高级岗位面试会深挖 Spring 源码实现。 仅靠八股文背诵,难以应对灵活追问。
  1. 日常开发中的「未解之谜」
  • 为什么 @Transactional 注解有时失效?
  • 如何定制 Bean 的生命周期回调?
  • 配置文件加载的优先级到底怎么定?
    源码能让你从「玄学调试」进阶到「精准打击」。
  1. 架构思维跃迁的关键阶梯
    Spring 的设计融合了工厂模式、模板方法、策略模式等经典设计模式,其代码是「教科书级」的架构范本。
    读源码 = 站在巨人肩上,学习如何设计高扩展、低耦合的系统。

>>>点击去关注<<<