Skip to content

IoC 和 DI 是众多框架的通用设计思想——Spring、Google Guice、Java CDI、.NET Core 都用它,只是 Spring 应用得最广。平时天天用 @Autowired@Bean,但"控制反转"到底反转了什么、IoC 和 DI 是不是一回事、三种注入方式怎么选——这些基础概念值得系统梳理一遍。这篇把 IoC 从头讲清楚,是理解《AOP 面向切面编程》和《Spring Boot 自动配置原理》的前提。

一、解决什么问题:对象之间的耦合

先看不用 IoC 的写法:

java
public class UserService {
    // 写死了具体实现,耦合死了
    private UserDao userDao = new UserDaoImpl();

    public User getUser(int id) {
        return userDao.selectById(id);
    }
}

UserService 直接 new UserDaoImpl(),带来了三个问题:

  1. 强耦合:想换成 UserDaoMyBatisImpl,就得改 UserService 的代码
  2. 难测试:单测 UserService 时,没法用一个"假的 UserDao"替换掉真的
  3. 重复创建:每个用到 UserDao 的地方都要自己 new,创建逻辑散落各处

核心矛盾一句话:对象自己掌控了"创建依赖对象"的权力,导致它和具体实现绑死

二、IoC 是什么:把"创建权"交出去

IoC(Inversion of Control,控制反转)是一种思想:把"创建对象、管理依赖"的控制权,从对象自己手里,反转给一个外部容器

java
public class UserService {
    private UserDao userDao;

    // 依赖不再自己 new,而是由外部(容器)注入进来
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}

现在 UserService 只依赖 UserDao 接口,不关心具体实现。具体注入哪个实现,由容器决定——想换实现,改容器配置就行,UserService 一行不用动。

IoC 控制反转:自己 new 的耦合 vs 容器注入的解耦

三、IoC 和 DI 是不是一回事

不是一回事,是"思想"和"实现"的关系

概念定位说明
IoC(控制反转)思想/原则"创建对象的控制权从自己反转到容器"
DI(依赖注入)实现手段容器把依赖"注入"进对象(构造器/setter)

IoC 是目标,DI 是实现这个目标最主流的方式。除了 DI,还有一种叫 Service Locator(服务定位器),但因为 DI 更简单、依赖更明确,所以 Spring 主推 DI。

一句话:IoC 是"反转控制权"这个思想,DI 是"怎么把依赖送进去"这个动作

四、三种注入方式怎么选

java
// ① 构造器注入(推荐):依赖不可变、必填、便于测试
@Service
public class UserService {
    private final UserDao userDao;          // final,一旦注入不可改
    public UserService(UserDao userDao) {   // Spring 自动按类型注入
        this.userDao = userDao;
    }
}

// ② Setter 注入:适合可选依赖
@Service
public class UserService {
    private UserDao userDao;
    @Autowired
    public void setUserDao(UserDao userDao) { this.userDao = userDao; }
}

// ③ 字段注入(不推荐):@Autowired 直接打在字段上
@Service
public class UserService {
    @Autowired
    private UserDao userDao;   // 隐藏依赖、不能 final、难测试
}
方式优点缺点场景
构造器注入依赖不可变、必填清晰、易测试参数多时构造器变长首选
Setter 注入可选依赖灵活可能漏注入、可变可选依赖
字段注入代码最简洁隐藏依赖、难单测不推荐(老代码常见)

五、Spring 容器:IoC 思想的落地

前面几节说的"把创建权交给外部容器",那个"容器"在 Spring 里是真实存在的类,叫 Spring 容器(IoC 容器)。它是 IoC 思想的落地实现——一个负责"创建对象、管理对象、注入依赖"的东西,可以理解成对象的管家

先认识一个词:Bean。容器里管理的对象,统称 Bean(豆子,比喻容器是个装豆子的罐子)。前面代码里 @Service@Component 标注的类,容器启动时会创建它的实例、放进容器管理,这个实例就是一个 Bean。

Spring 容器有两个层次,日常用增强版:

容器定位特点
BeanFactory最底层的接口懒加载:用到哪个才创建哪个,一般不直接用
ApplicationContext继承 BeanFactory 的增强版启动时提前创建好所有 Bean,还带事件、国际化等,日常都用它
java
// 从容器拿对象,而不是自己 new
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = ctx.getBean(UserService.class);   // 容器已注入好依赖

启动时,容器扫描配置、创建所有 Bean、把依赖一个个注入进去——前面说的"创建 + 注入 + 管理"三步,就在这一步自动完成,这就是"反转"的落地。

一句话串起来:IoC 是思想(把创建权交出去),Spring 容器是实现这个思想的"对象管家",DI 是它注入依赖的方式

小结

  • IoC 解决"对象自己 new 导致强耦合"的问题,把创建权反转给容器
  • IoC 是思想,DI 是实现(构造器/setter 注入依赖)
  • 注入方式首选构造器注入(不可变、必填清晰、易测试),字段注入不推荐
  • Spring 容器 = ApplicationContext,从容器拿对象而不是自己 new

IoC 管"对象怎么来、依赖怎么注入",AOP 管"横切逻辑怎么统一织入"——接着看《AOP 面向切面编程》,它解决事务、日志这类散落在各处的代码。