让我们尝试通过两个示例来理解它。
例子1
在早期,用于生成命令提示符的应用程序接连接受用户输入。如今,UI框架实例化了各种UI元素,遍历了这些UI元素的各种事件(例如,鼠标悬停,单击等),并且用户/主程序提供了用于监听这些事件的钩子(例如Java中的UI事件监听器)。因此,主要的UI元素流“控件”已从用户程序移至UI框架。在早期,它在用户程序中。
例子2
考虑CustomerProcessor
以下课程:
class CustomerProcessor
{
SqlCustRepo custRepo = new SqlCustRepo();
private void processCustomers()
{
Customers[] custs = custRepo.getAllCusts();
}
}
如果我想processCustomer()
独立于的任何实现getAllCusts()
,而不仅仅是所提供的实现SqlCustRepo
,我将需要摆脱限制:SqlCustRepo custRepo = new SqlCustRepo()
并用更通用的,可以接受各种实现类型的东西来代替它,这样该代码processCustomers()
将仅适用于任何提供的实现。上面的代码(SqlCustRepo
通过主程序逻辑实例化所需的类)是一种传统方法,并且无法实现processCustomers()
与实现分离的目标getAllCusts()
。在控制反转中,容器实例化所需的实现类(如xml配置所指定),将其注入到主程序逻辑中,该逻辑按指定的钩子进行绑定(例如通过spring框架中的@Autowired
注释或 getBean()
方法)。
让我们看看如何做到这一点。考虑下面的代码。
Config.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="custRepo" class="JsonCustRepo" />
</beans>
CustRepo.java
interface ICustRepo
{ ... }
JsonCustRepo.java
class JsonCustRepo implements CustRepo
{ ... }
App.java
class App
{
public static void main(String[] args)
{
ApplicationContext context = new ClassPathXmlApplicationContext("Config.xml");
ICustRepo custRepo = (JsonCustRepo) context.getBean("custRepo");
}
}
我们也可以
class GraphCustRepo implements ICustRepo { ... }
和
<bean id="custRepo" class="GraphCustRepo">
并且我们将不需要更改App.java。
容器(这是Spring框架)上方负责扫描xml文件,实例化特定类型的bean并将其注入用户程序。用户程序无法控制要实例化哪个类。
PS:IoC是通用概念,可以通过多种方式实现。上面的示例通过依赖项注入来实现。
参考: Martin Fowler的文章。