Ⅰ IOC是什么
我简单总结一下 IOC 是什么:
- 控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理;
- 使用 IOC 的目的:降低耦合度。
我第一篇文章写了一个 Spring 的第一个case,实现的就是 IOC 的功能。
【Java Web】-> Spring 5 -> Spring 的下载 & 第一个样例
其实 IOC 也好, 里氏替换也好,包括24种设计模式,主要的目的都是一件事:解耦。
当我们在一个对象里生成另一个对象时,这个过程耦合度是很高的,所以我们把这个过程交给 Spring,它来负责创建对象,生成对象,我们只需要用就好了,IOC 大概就是这样。
Ⅱ IOC 底层原理
IOC 底层主要用到了三个技术: XML 文件解析, 工厂模式,反射。
我从一个case说起,简单介绍一下程序解耦的一个演化过程。
比如我现在有两个类。UserService和UserDao,它们分别有一个方法 execute() 和 add()
现在 UserSeivice 的 execute() 方法要调用 UserDao 中的 add() 方法,我们先来用传统的方法做一下。
这样需要在 execute() 方法中先创建 UserDao 的对象,再进行调用。这样耦合度就很高。
这时候有一个模式就出现了,它就是工厂模式。
我们建立一个工厂,让它来生成 UserDao 的对象,然后 UserService 要使用的时候调用 UserFactory 就可以了,这样就使得 UserDao 和 UserService 解耦了。
这里有同学可能会疑惑,那工厂不是就和这两个类都耦合了吗,哪里降低耦合度了,本来只有两个类有耦合,现在搅进来个第三者,不就更麻烦了吗?
这位同学提了个好问题。
我们一般用工厂,也不会就生成一个对象,而且一个对象也不一定就是只会被一个类来调用,如果很多类都要调用 UserDao,那它们都要去和 UserDao 耦合,但是有了工程,所有使用 UserDao 的类都可以直接通过工厂这个中间人来取得,就不需要再去自己和 UserDao 耦合了。
就像金融领域里的银行一样,银行中间商是为了降低信息的不确定性的,有中间商总比没有中间商方便很多,不然你每次贷款都要去自己找愿意给你放贷的人,就很耦合很麻烦嘛。
要知道,对象和对象之间是不可能完全没有耦合度的,我们要做的是尽可能降低耦合度,但是这里降到最低了吗?显然也是没有的,我们再继续看。
再进一步,我们使用 xml 文件解析和反射机制。我描述一下 Spring IOC 的过程。
这样如果你的 UserDao 的地址改了,其他的地方都不需要动,什么包都不需要改,只改一个配置文件的 class 属性里的地址就好了,这样耦合性是不是就非常低了?
绿色的第二步 return 的对象去哪里了呢?当然就是放在工厂里的,所以说 Spring IOC 的本质就是一个容器。IOC 的底层容器就是一个对象工厂。
Ⅲ IOC 容器的实现方式
Spring 提供 IOC 容器实现的两种方式(两个接口):
- BeanFactory
- ApplicationContext
这两个接口其实是可以互相替换的。我用我第一个case来举例。
这里我将 ApplicationContext
更改成 BeanFactory
,一样没有错误。那我简单介绍一下它们俩的区别是什么。
① BeanFactory
BeanFactory 是 IOC 容器的基本实现,是 Spring 内部使用的一个接口,不提供给开发人员(也就是我们)使用。
当然你要使用,就像我上面一样,也没有人拦你。
BeanFactory 使用的懒汉模式,也就是当你获取对应的对象的时候,对象并不存在,BeanFactory这时才进行创建,然后返回给你。
② ApplicationContext
ApplicationContext 是 BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员使用。
和 BeanFactory 不同的是,ApplicationContext 实现的是饿汉模式,也就是在第一步就已经完成了对象的生成,当用户从工厂种取的时候,对象已经存在了。
那 BeanFactory 的懒汉模式和 ApplicationContext 的饿汉模式哪个更好一点呢?可能有人会想,那还是 BeanFactory 的懒汉模式 好一点,这样就可以节省很多资源,用的时候再生成就好了。但是要知道,Spring 是结合我们的 web 项目一起使用的,我们最好把耗时耗资源的准备工作留给自己,也就是在服务器启动的时候,就把一切资源都创建好,对象都生成好放在工厂中,这样在服务器启动之后,用户直接使用就可以了。
我们来看一下 ApplicationContext 的实现类。我圈起来的两个是 ApplicationContext 最主要的两个实现类。
我还是用我的case来说明这两个的区别。
ClassPathXmlApplicationContext()
实现的是类路径,比如我的配置文件 bean1.xml 是写在 src 目录下的,所以直接写名字就好了。
对比的,FileSystemXmlApplicationContext()
需要写的是配置文件在系统中的绝对路径。