
2.1 准备知识:什么是控制反转
在传统的项目中,需要在类中主动定义所需的依赖对象,比如需要在计算机类中定义所需依赖的鼠标键盘等类。这种做法相关类的耦合度很高,这样会导致在修改其中一个类的代码时引发其他类的修改,从而提升项目的维护难度。
对此,在Spring的控制反转的开发模式中,会由Spring容器来管理类之间的依赖关系,即类之间依赖关系的控制权由“类”反转到了“容器”,这样就能很好地降低类之间的耦合度,从而降低维护项目的工作量。
2.1.1 以实例了解控制反转的做法
在如下的IocDemo范例中,将以控制反转的方式管理类之间的耦合度,从中读者能直观地看到类之间依赖关系的“反转”,以及这种“反转”对代码维护的帮助。
按第1章给出的详细步骤,通过IDEA集成开发工具创建名为IocDemo的Maven类型的项目,并在pom.xml文件中加入如下关键代码:

通过第1~3行代码指定本项目用JDK1.11版本,通过第4~10行代码引入了使用IoC所必需的依赖包。
编写提供服务的Tool.java类,代码如下:

在该类第2行的print的方法中封装了提供打印服务的代码,这里简化成输出一句话。
编写实现配置管理Bean类的Config.java,代码如下:

在Spring IoC开发模式中,类依赖关系的控制器被“反转”到了Spring容器中,在该Config.java类中,定义了通过Spring容器管理Tool类的方式。
具体的做法是,首先通过第3行的注解说明Config.java类将承担“向Spring容器配置类”的角色,其次通过第5行的@Bean注解说明第6行的init方法将返回名为tool的Bean,再结合第7行代码,可以看到名为tool的Bean其实是Tool类型的。
编写调用Tool对象的TestClass.java类,具体代码如下:

在main函数的第5行,先根据Config类中的定义初始化了ApplicationContext对象,这个对象可以理解成Spring管理Bean的容器。由于在Config配置类中定义了名为tool的Bean其实是Tool类型的对象,因此在第6行中能通过applicationContext.getBean方法得到Tool类型的对象,随后在第7行中通过tool对象调用其中的print方法。
运行TestClass类的main函数,能看到如下结果,这是由第7行的tool.print方法输出而成的。
Use tool to Coding
根据Java传统定义类和使用类的做法,在main函数中,应该通过如下代码创建并使用tool对象,即先通过第1行的new方法创建对象,再通过第2行的代码使用tool对象。

在传统做法中,TestClass类依赖Tool类,并调用了其中的print方法,这里对Tool类的依赖关系定义在类中。而在本范例中,TestClass类在要用到Tool类的时候才从Spring容器ApplicationContext对象中得到Tool类,也就是说,TestClass类对Tool类依赖关系的控制权“反转”到了Spring容器中。
由于TestClass类对Tool类的依赖关系被“反转“到了Spring容器中,因此从代码层面来看,两者的耦合度很低,因此哪天要修改Tool类中print方法的调用参数,也不会对TestClass类造成任何影响。
2.1.2 Bean与Spring容器
从上文的范例中,读者能感受到控制反转的做法,这里来说明一下与之相关的两个名词Bean和Spring容器。
在Spring的开发和运行环境中,读者可以把Bean理解成一个个具体的类,比如上文的范例中,Tool类就是一个Bean,而在控制反转的开发模式中,Bean之间(也就是类之间)的依赖关系是由Spring容器来管理的,而不是直接定义在类的内部。
而Spring容器则是Spring管理Bean的工具,在上文的范例中,ApplicationContext对象就是具象化的Spring容器。在Spring项目启动时,Spring容器能从配置文件或配置类中读取各种Bean的依赖关系,并在运行时在必要时根据预先的配置创建对应的Bean类。
在开发Spring乃至Spring Boot的项目时,Bean和Spring容器这两个名词经常会被提起,但经过上文的解释,其实它们也是比较好理解的,并没有什么神秘的地方。
2.1.3 控制反转和依赖注入是一回事
在Spring语境中,和控制反转意思相同的一个名词叫依赖注入(Dependency Injection,DI),其实它们是一回事,是对同一个事物从不同角度的解释。通过表2.1的对比,读者能清晰地看到这一点。
表2.1 IoC和DI概念的对比描述

从表2.1可以看到,依赖注入强调类的注入是由Spring容器在代码的运行时完成的,而控制反转则强调类之间的依赖关系是由Spring容器控制的。但无论怎样,通过它们所描述的编程模式,程序员能大大降低类之间的依赖关系,这样就能把修改一个类的影响范围降低到最小的程度。