让我用一个非常通俗易懂的方式来解
<p>这是一个非常经典的Spring面试题,也是理解Spring核心思想的关键。很多人会把它们混为一谈,但实际上它们描述的是同一个概念的两个不同侧面。</p>
<p>让我用一个非常通俗易懂的方式来解释。</p>
<h4><a name="t0"></a>核心结论</h4>
<ul><li>
<p><strong>控制反转(IoC)是一种设计原则或思想。</strong></p>
</li><li>
<p><strong>依赖注入(DI)是实现控制反转这种思想的一种具体技术模式。</strong></p>
</li></ul>
<p>可以把它们的关系理解为:<br>
<strong>“战略” vs “战术”</strong></p>
<ul><li>
<p><strong>IoC是战略目标</strong>:我们要将程序的控制权反转过来。</p>
</li><li>
<p><strong>DI是战术手段</strong>:我们通过“注入”依赖的方式来实现这个目标。</p>
</li></ul>
<hr>
<h4><a name="t1"></a>1. 控制反转(Inversion of Control - IoC)</h4>
<p><strong>核心思想:</strong> 将对象的创建、组装、生命周期的控制权,从应用程序代码中“反转”给一个外部容器(在Spring中就是IoC容器)。</p>
<p><strong>传统开发模式(正转):</strong></p>
<ul><li>
<p>你需要一个对象(比如<code>Car</code>),你会在代码里直接<code>new Car()</code>。</p>
</li><li>
<p>你的代码<strong>主动</strong>去创建和管理它所依赖的对象。</p>
</li><li>
<p><strong>控制权在程序本身。</strong></p>
</li></ul>
<p>java</p>
<pre data-index="0" class="new-version hljs set-code-show" name="code">// 传统方式:程序员自己控制
public class Car {
private Engine engine;
public Car() {
// 程序员主动创建依赖
this.engine = new Engine(); // 控制权在Car类内部
}
}</pre>
<p><strong>控制反转模式(反转):</strong></p>
<ul><li>
<p>你不再自己<code>new</code>对象,而是由一个“容器”来帮你创建好、组装好,然后“送”给你。</p>
</li><li>
<p>你的代码是<strong>被动</strong>接收它所需要的依赖。</p>
</li><li>
<p><strong>控制权转移到了外部容器。</strong></p>
</li></ul>
<p>java</p>
<pre data-index="1" class="new-version hljs set-code-show" name="code">// IoC方式:容器控制
public class Car {
private Engine engine;
// 容器会负责把创建好的Engine对象“注入”到这里
public Car(Engine engine) { // 控制权在容器
this.engine = engine;
}
}</pre>
<p><strong>IoC容器的职责:</strong></p>
<ul><li>
<p>创建对象</p>
</li><li>
<p>弄清楚对象之间的依赖关系(比如<code>Car</code>依赖<code>Engine</code>)</p>
</li><li>
<p>将依赖的对象装配到一起</p>
</li><li>
<p>管理对象的生命周期(何时创建,何时销毁)</p>
</li></ul>
<hr>
<h4><a name="t2"></a>2. 依赖注入(Dependency Injection - DI)</h4>
<p><strong>核心思想:</strong> 是实现IoC的具体技术。它描述的是“如何”将依赖关系传递给一个组件。具体来说,就是<strong>容器通过构造函数、Setter方法或接口等方式,将依赖对象“注入”到需要它的组件中。</strong></p>
<p>依赖注入主要有三种方式:</p>
<p><strong>1. 构造器注入(推荐)</strong><br>
容器通过调用类的构造函数来注入依赖。</p>
<p>java</p>
<pre data-index="2" class="new-version hljs set-code-show" name="code">@Component
public class Car {
private final Engine engine;
// 容器会在这里注入一个Engine实例
@Autowired // Spring 4.3后,如果只有一个构造器,可省略此注解
public Car(Engine engine) {
this.engine = engine;
}
}</pre>
<p><strong>2. Setter方法注入</strong><br>
容器通过调用类的Setter方法来注入依赖。</p>
<p>java</p>
<pre data-index="3" class="new-version hljs set-code-show" name="code">@Component
public class Car {
private Engine engine;
// 容器会调用这个setter方法来注入Engine
@Autowired
public void setEngine(Engine engine) {
this.engine = engine;
}
}</pre>
<p><strong>3. 字段注入(现在不推荐)</strong><br>
容器通过反射直接给字段赋值。</p>
<p>java</p>
<pre data-index="4" class="new-version hljs set-code-show" name="code">@Component
public class Car {
@Autowired // 直接注入到字段
private Engine engine;
}</pre>
<hr>
<h4><a name="t3"></a>用一个生活中的比喻来总结</h4>
<p>假设你想<strong>喝一杯咖啡</strong>。</p>
<p><strong>传统模式(正转):</strong></p>
<ol><li>
<p>你自己去种咖啡豆。</p>
</li><li>
<p>你自己去烘焙、研磨。</p>
</li><li>
<p>你自己用咖啡机冲泡。</p>
</li><li>
<p>你喝到咖啡。<br>
-> <strong>你控制了所有环节,非常繁琐,高度耦合。</strong></p>
</li></ol>
<p><strong>控制反转模式(IoC):</strong></p>
<ul><li>
<p>你走进一家<strong>咖啡馆(IoC容器)</strong>。</p>
</li><li>
<p>你直接向<strong>服务员</strong>点单:“我要一杯拿铁”。</p>
</li><li>
<p>服务员(容器)去后厨(创建并组装对象)让咖啡师(Bean)用咖啡机(另一个Bean)和牛奶(另一个Bean)做好一杯拿铁。</p>
</li><li>
<p>服务员把这杯做好的拿铁(组装好的对象)递给你。<br>
-> <strong>你不再关心制作过程,只关心最终结果。控制权反转给了咖啡馆。</strong></p>
</li></ul>
<p><strong>依赖注入(DI)在这个比喻中:</strong></p>
<ul><li>
<p>就是服务员<strong>把做好的拿铁“递给你”的这个“递”的动作</strong>。</p>
</li><li>
<p>它是实现“咖啡馆为你服务”这个“控制反转”思想的具体方式。</p>
</li></ul>
<hr>
<h4><a name="t4"></a>总结对比表</h4>
<div class="table-box"><table><thead><tr><th>特性</th><th>控制反转(IoC)</th><th>依赖注入(DI)</th></tr></thead><tbody><tr><td><strong>本质</strong></td><td><strong>设计原则、思想</strong></td><td><strong>设计模式、具体技术</strong></td></tr><tr><td><strong>关系</strong></td><td><strong>目标</strong></td><td><strong>实现手段</strong></td></tr><tr><td><strong>关注点</strong></td><td>控制权的转移(从程序转移到容器)</td><td>对象依赖关系的满足方式(通过注入)</td></tr><tr><td><strong>范围</strong></td><td>一个更广泛的概念,DI是它的子集</td><td>是实现IoC的特定方式,还有其他方式如服务定位器</td></tr><tr><td><strong>类比</strong></td><td><strong>去咖啡馆喝咖啡</strong>的理念</td><td><strong>服务员把咖啡递给你</strong>的这个具体动作</td></tr></tbody></table></div>
<p>在Spring框架中,我们通过使用<strong>依赖注入</strong>这种最主流的方式,来实现<strong>控制反转</strong>这个崇高的设计目标,最终达到代码解耦、更易测试、更灵活的目的。所以当我们在谈论Spring的IoC时,通常指的就是它的依赖注入功能。<br>
</p>
