Spring学习系列(二)彻底理解:控制反转(IOC)和依赖注入(DI)
学习Sping首先要了解的就是Spring的核心思想之一:控制反转。
1.HelloWorld与模块化思想
在团队开发中,我们有这样一个需求【输入信息】:实现一些输出功能,【功能A】输入一个HelloWorld(这里用功能A表示,当然功能A也可以是更复杂的功能)。
然而在一个系统应用中肯定不止一个单一的功能A,往往还有其他的功能B,功能C...等一系列的功能,这些不同的功能组成一个完成的业务系统。
例如在HelloWorld程序中,我们不单单要输出HelloWorld,我们还想要输出HelloJava,HelloSpring等信息。
在这种单一的程序中,我们采用这种集中式开发模式是没有什么问题的。但是在团队开发中,一个复杂的系统采用这种方式将是一件非常糟糕的事情(不灵活,耦合严重,协同开发效率低下等等问题)。
这时候就要祭出Java的封装神器了(封装是面向对象语言共有的特性),对HelloWorld进行封装升级。(将功能A,功能B,功能C等封装成一个独立的模块)
这样你和同事就可以分工合作了,由你完成功能A‘HelloWorld’,由你的同事完成功能B‘HelloJava’。而且在模块化之后,如果单个的功能变更将不在主程序中修改,你只需要在对应的功能模块修改就可以。
而我们的主程序Hello就成为了组装师,这样写代码也可以流水化生产了。
既然我们知道了封装,不妨对上述程序再进行改造一下。
当然这么改造是因为功能A,功能B要做的事情是一样,我们又进行了一次封装。如果说模块化是针对不同功能的封装(提高代码的隐蔽性和独立性),而这次是针对相似功能的封装(提高代码的复用性)。
小结:面对一直在变的需求,程序员要学会一系列的思想:封装,模块化,流水线等应对始终会变的需求,增强代码的健壮性和扩展性。
2.类与类之间的关系
万物皆对象,在Java中类是对象的描述(定义),类中的属性和方法就是对象的状态和行为的描述。
一个Java系统应用是由许多类组成的,那么这些类之间有什么样的关系呢?
类与类之间大体上有三种关系:
- 继承关系(泛化)针对类或接口的子类的extends,针对抽象类或接口的实现类的implements。
- 关联关系(依赖)依赖分为弱依赖(在方法/行为中发生依赖)和强依赖(在属性/状态中发生依赖),在依赖关系中我们更多的关注强依赖关系:包括聚合(has a,)和组合(contains a)两种形式(两者简单的区别是:聚合的依赖可以独立存在,而组合的依赖不能独立存在)。在开发中我们更多的使用聚合关系,例如JavaBean。
- 独立关系(无关)两个类之间不发生交互。
更详细的资料可以查阅UML(统一建模语言)的相关知识,其中会对类图以及类关系作详细描述,也可参考《UML精粹》等书籍,这些对面向对象设计(OOD)很有帮助。
我们在面向对象编程(OOP)中关注的问题是,如何处理类与类之间复杂的依赖关系。
3.控制反转和依赖注入
在学习控制反转和依赖注入的过程中Martin Fowler的文章是必读的,描述的非常详细。这里是学习后的一个总结。
- 对比模块化中的功能A和功能B,这些功能可以是远程的服务也可以是本地化的组件(介绍模块化的必要)。
- 而文章中主要讨论的问题就是如何在Hello中调用这些功能HelloWorld,HelloJava(介绍类与类关系的必要)。
- 在应用程序中控制的就是在当前的应用程序中new来控制插件的实例化。现在应用程序不在关注插件的实例化,交由框架(装配器)进行实例化(把控制权交出去了)这就是所说的控制反转。
- 而框架在实例化的时候通常采用两种方式:依赖注入(将实例化的插件放入IOC容器中)和服务定位(通过服务定位器来获取实例化的插件,由内部的装配器负责实例化,服务定位器只负责获取),两者更清晰的区别可以对比原文的两种方式的图例。
- 依赖注入的三种方式:接口方式,Setter方式,构造器方式。都是通过方法来注入实例化的依赖,不同的是方法的类别不同:接口方式的方法是实现接口中定义的方法来注入,Setter方式是通过属性的setter方法注入,构造器方式则是通过构造方法来注入。
- 服务定位器的两种方式:静态的是将依赖定义为属性,动态的是将依赖放入定义的容器对象中。
- 这些技术都能够达到控制反转的目的,根据不同的需要选择不同的方式实现即可。
简单来说,控制反转和依赖注入就是在描述一件事。
控制反转提出问题:我是应用程序,我不想关注我所依赖的对象是怎么创建的了。我把创建对象的控制权交出去了,具体怎么实现我不管,我只负责拿来用就可以了。
依赖注入解决问题:我是装配器(容器),我来负责创建对象,这件事我比较擅长(当然别人也可以做到比如说服务定位器,Service Locator)。而且我可以通过三种方式创建:接口,Setter,构造器。
4、总结
如果对一个问题表达的不清楚,只能说明理解的不够深入和透彻。当能够用自己的方式表达出正确的意思,那么才算得上真正掌握。