Ioc—Inversion of Control,即“控制反转”。在传统面向对象编程中,我们往往会在客户类里主动去创建、配置我们需要用到的依赖。比如,我们有一个客户类userDao需要完成数据库的增删改查操作,我们需要先在客户类对象中创建我们需要的依赖(数据库连接对象conn):
Class.forName("com.mysql.jdbc.Driver"); //加载mysql驱动
url = "jdbc:myqsl://localhost/database";
user = "root";
password = "root";
conn = DriverManager.getConnection(url,user,password);
....
....
conn.close();
从上面我们可以看到,数据库连接对象的参数配置、初始化以及最终的销毁,都是在我们客户类的控制下完成的,这显然会造成两个类的严重耦合,如果有新的需求,需要去更改数据库的参数配置,或者数据库的连接方式等,我都要去修改我们的客户类userDao,但它所完成的功能没有变化(依然是对数据库的增删改查操作)。
事实上,一个良好的设计,核心原则之一就是将变化隔离,使得变化部分发生变化时,不变部分不受影响。对此,我们往往利用面向对象的多态性,使客户类不再直接依赖服务类,而是依赖于一个抽象的接口,这样,客户类就不能在内部直接实例化具体的服务类。但是,客户类在运作中又客观需要具体的服务类提供服务,因为接口是不能实例化去提供服务的。于是就产生了“客户类不准实例化具体服务类”——“客户类需要具体服务类”的矛盾。
于是IOC容器就出现了,我们可以把客户类所需要的依赖(dataSource),交给容器进行管理(在spring中,容器的配置往往在xml文件中写入)。
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="test"/>
</bean>
这样,客户类只需要定义一个注入点,在需要用到依赖的时候,由IOC容器注入进来
public void setDataSource(DataSource dataSource){
this.dataSource = dataSource;
}
使用完毕后,只需交还给IOC容器进行销毁。也就是说,客户类(userDao)只专注于自己的职责(对数据库的增删改查),依赖(数据库连接对象)的参数配置、初始化、销毁工作统统交给IOC容器管理。这样,面对新的需求,无论我们如何更改依赖(如更换数据库等),只要我们的客户类需求不变(userDao依然专注于对数据库的增删改查操作),我们就无须对客户类作任何变更。
这就是IOC的核心思想。本来,是由客户类来控制依赖的生命周期,主动创建获取依赖,现在依赖由容器创建,客户类需要用到依赖的时候被动注入来获取,即依赖的获取方式被反转了。
很多时候,我们往往会听到另一个概念DI—Dependency Injection,即“依赖注入”。它的本质和IOC其实是一样。通过依赖注入,我们只需要通过简单地配置(如上面的xml文件配置),客户类即可轻松获得它所需要的资源,来完成自身的业务逻辑,而无需关心这些资源从哪里来,到哪里去。