控制反转与依赖注入的概念解析

控制反转(Inversion of Control)与依赖注入(Dependency Injection)应该是我们在学习Spring时最早也最常接触的两个概念。在Spring的官方文档中,对这两个概念的解释是:控制反转约等于依赖注入。注意看加粗部分:

This chapter covers the Spring Framework implementation of the Inversion of Control (IoC) principle. IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies (that is, the other objects they work with) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes or a mechanism such as the Service Locator pattern.

不过文档中的描述还是有些抽象,下面我用我的理解来描述一下:

控制反转是一种【思想】
我们知道,一个对象A需要实现某个完整的功能,往往需要依赖于别的对象为其提供某些基础功能,那些被依赖的对象便是对象A的依赖。

class A{
	private B b;
	public void doSomething(){
		b.doSomething;
		// A继续实现后续逻辑
	}	
}

在上面的例子中,B是A的依赖。但显然,这样的代码是不能运作的,因为B并没有实例化。在最传统的面向对象编程方式中,A要获得依赖对象B,需要自己手动实现。比如在代码中new出一个B的实例。

class A{
	private B b;
	public void doSomething(){
		// 手动new出B的实例
		b = new B;
		b.doSomething;
		// A继续实现后续逻辑
	}
}

但是在真实的企业开发中,这种由使用者管理控制依赖的创建的方式会面临各种问题。
比如B需要是一个单例,而在A以外的地方也需要使用B;比如在多人开发中,B还只是一个接口,并没有实现类,根本new不出来;比如B本身也有依赖的对象需要先实例化;比如在A中硬编码的方式造成了过高的耦合,给后续的……
面对实际的问题,就需要我们通过某种方式由调用者控制依赖对象实例化转变为别的方式,也就是**“控制反转”**。由于A不再负责B的实例化,只需要在A使用B的时候,可以有某种方式获得B的实例即可,A中少了实例化B相关的代码,也就降低了A、B之间的耦合。
前面我们说了“某种方式”,说明实现控制反转的方式不止一种——依赖注入、依赖查找(Dependency Lookup)都是控制反转思想的具体实现

Spring采用依赖注入实现了控制反转
所谓依赖注入,就是将被依赖的对象传入到依赖对象中。再看一遍Spring对IOC和依赖注入的说明:

This chapter covers the Spring Framework implementation of the Inversion of Control (IoC) principle. IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies (that is, the other objects they work with) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes or a mechanism such as the Service Locator pattern.

注意看加粗的部分,Spring是通过构造方法参数、工厂方法参数、对象属性的方式,寻找注入点,将依赖注入到对象中的。由于Spring是用DI的方式实践了IOC的思想,所以才会说“IoC is also known as dependency injection (DI)”,这句话在Spring的语境下是没有错的。

控制反转还有依赖查找这一实现方式
依赖查找关键在“查找”二字上。也就是说,虽然依赖者不再由自己管理依赖对象的创建,但是它需要通过某种方式从外部获得它所需要的依赖。通过Spring其实也可以实现“依赖查找”,下面是Stack Overflow上一个回答里举的例子:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/application-context.xml");
MyBean bean = applicationContext.getBean("myBean")

如果把整段代码看做依赖者,myBean看做依赖项,上面就是一个依赖者通过Spring容器实现了IOC的例子。

JNDI也是依赖查找的一个具体实现
什么是JNDI,可以参考这篇文章,我摘抄一段:

JNDI中的命名(Naming),就是将Java对象以某个名称的形式绑定(binding)到一个容器环境(Context)中,以后调用容器环境(Context)的查找(lookup)方法又可以查找出某个名称所绑定的Java对象。读者也许会感到奇怪:自己创建一个Java对象,将其绑定到JNDI容器环境中后又查询出来,这有什么意思?在真实的项目应用中,通常是由系统程序或框加程序先将资源对象绑定到JNDI环境中,以后在该系统或框架中运行的模块程序就可以从JNDI环境中查找这些资源对象了。例如,Tomcat服务器在启动时可以创建一个连接到某种数据库系统的数据源(DataSource)对象,并将该数据源(DataSource)对象绑定到JNDI环境中,以后在这个Tomcat服务器中运行的Servlet和JSP程序就可以从JNDI环境中查询出这个数据源(DataSource)对象进行使用,而不用关心数据源(DataSource)对象是如何创建出来的,这种方式极大地增强了系统的可维护性,当数据库系统的连接参数发生变更时,这只是Tomcat系统管理员一个人要关心的事情,而与所有的应用程序开发人员无关。

使用JNDI配置数据源,是JNDI的一个常用场景。从JNDI容器中获取对象的方式,便是典型的依赖查找。

总结:
控制反转是将被依赖对象实例化(或者说获取)的过程从依赖对象之中抽离,以降低代码耦合度,便利开发的一种设计思想。Spring使用了依赖注入这一实现方式来达成控制反转的目的,具体来说,就是由Spring的IOC容器负责对象的创建,各个对象通过配置、注解的方式向Spring声明自己需要什么依赖,由Spring通过构造方法、工厂方法、set方法、反射等方式将被依赖对象注入到依赖对象中。
虽然控制反转和依赖注入不能等价,但是在Spring中,可以说依赖注入就约等于控制反转,才会有Spring官方文档中的那句“IoC is also known as dependency injection (DI)”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值