Spring IOC 回顾和拓展

                                                                                                                                                              20190608 田超凡

                                                                                           知网用户_进击的猿宝宝(田超凡)版权所有,转载注明原作者

1.  IOC的基本概念
Spring IOC 回顾和拓展
Spring IOC 回顾和拓展

Spring IOC 回顾和拓展

Spring IOC 回顾和拓展

 (1).依赖:依赖的全称是Dependency,从字面意义上看就很容易理解,表示替代某个事物来完成原本属于它的任务。比如,老板张三需要李四去做一件事,并且全公司的人只有李四具备做这件事的能力,或者说只有李四能胜任这件事,我们就可以认为老板张三依赖于李四。再比如,要开发一个完整的软件工程,需要全公司各个部门员工各尽其能,发挥所长,才能最大程度上推动软件工程进度发展和创造效益。此时,我们也可以认为,公司依赖于各个工作部门,比如开发部、测试部等等。生活中类似的案例还有很多,在这两个例子中,用依赖关系来形容最合适不过了,此种依赖关系记作:

老板张三 ——> 李四,读作:老板张三依赖于李四。

公司 ——> 各个部门,读作:公司依赖于部门。

老板张三、公司称作依赖对象,对应的李四、各个部门称作被依赖对象。哈哈这个例子确实挺生动的阐述了依赖的概念。

为了下文介绍,我们把这两个依赖的例子使用符号标记:

——> C (A为依赖对象,C为被依赖对象)

——> D (B为依赖对象,D为被依赖对象)

 

(2).耦合度:依赖的程度,也叫做耦合度。在上面这两个依赖的例子中存在一个不可忽略的问题:如果被依赖的目标对象C和D换成了E、F等等,导致A和B对象也要转移依赖的目标为对应的E、F。用OOP思想看,A/B/C/D我们都可以抽象为四个实体类。由以上依赖关系,可以得知:A类定义依赖的C类型的属性,B类定义依赖的D类型属性。如果C、D发生改变,很明显A、B类中的代码也要改变,牵一发动全身。如果在一个真实项目环境中,有100个类,每个类都依赖30个类,如果这30个类发生变化,这100个类的改动量就可想而知了。在一个真正的软件项目开发过程中,这种问题肯定会时有发生,比如一个控制器依赖多个业务层,一个业务层依赖多个持久层或其他业务层,依赖的目标一改动,这些原控制器和业务层改动量就会随之改变非常大,降低了开发效率和编写业务代码的效率,不能专注业务逻辑实现,软件开发成本周期也大打折扣。像这类问题,我们叫做耦合度过高。

 

(3).内聚性:刚才说了耦合性也就是依赖程度过高的意思,那么内聚性截然不同,指的是尽量减小多组件依赖关系下,当需求变动或者调整导致替换依赖对象带来的代码改动程度。当然,并不是说内聚性可以完全避免依赖,这个是不可能的,就如同三层架构的依赖关系一样,持久层、业务逻辑层、视图层缺一不可,并且不能跨层调用,下一层调用上一层,依赖关系不能跨层。但是,当程序具有内聚性的时候,代码的扩展性、维护性都会得到质的提高,而不再拘泥于担心某一块改动而导致的代码改动量,可以把精力专心用在编写业务逻辑代码之上。就如同原子弹的原理一样,刚开始零件很多,依赖关系也很复杂,也就是耦合性高的问题,当某一组件出现问题时,都会导致原子弹研发失败。但是现代科学家们通过灵活组装之后形成最终的原子弹,给出多种备选方案,减小了组件之间的依赖关系,每种组件可以用其他具有类似化学成分的组件替代,并不会影响整个原子弹的生产和研发周期,渐渐的也就提高了内聚性,释放出无穷大的热量,产生极大化学效应。

 

(4).依赖注入DI:依赖注入的全称是Dependency Injection,简称DI,也就是将依赖关系通过注入的方式来表达,例如开头提到的两个依赖关系,我们在程序中如何表示这类依赖关系呢?很明显,我们有两种解决方案:第一种方式,就是通过代码根据OOP思想来控制,依赖的对象A、B定义为类,对应的依赖目标实例C、D定义为对应类的属性,使用new关键字构造对象。(需要特别说明的是,在第一种实现依赖的方式中,一般依赖的目标会定义成接口类型的属性,可以用对应的实现类来实例化,一定程度上提高扩展性)。第二种方式,就是通过工厂设计模式或SpringIoC来实现,彻底解决依赖目标对象发生变化时的代码改动问题,最小化目标依赖对象替换时的代码改动程度,对于工厂模式和SpringIoC具体实现方式,稍后会具体介绍。

 

(5).控制反转IOC:控制反转全称是Inversion Of Control,简称IOC,简单来说就是控制权发生转移,控制反转的实现离不开之前提到的依赖注入(DI),原因是,刚才说过控制反转主要指的是控制权转移,简单理解就是,还是以开头的两个依赖关系举例。既然提到控制权这个关键词,肯定会有控制对象和被控制对象(也就是前面说的依赖对象和被依赖对象),但是在依赖注入思想中,是尽量杜绝直接在代码中使用new实例化对象的,因为即使可以把依赖目标对象定义为接口类型属性,直接按需实例化对应的接口实现类即可。但是这样也还存在一个问题,那就是如果依赖的目标对象较多,按这个逻辑理解肯定要定义多个接口属性,但是如果依赖100、1000个接口,则都需要new 构建100个、1000个对应接口的实现类实例实现依赖注入,这样避免代码改动折损也还是比较大的。控制反转主要就是为了避免这种类型问题的代码改动折损。Spring IOC主要可以基于配置式、注解驱动式实现。实际上IOC的设计思想来自于稍后介绍的工厂设计模式。总之,IOC的主要目标是增强程序各组件直接的内聚性,尽可能降低耦合和依赖程度。即:高内聚,低耦合。

 

2. IOC的起源——工厂设计模式

工厂设计模式,顾名思义要用到工厂来构建接口对应类型的实现类实例,减小每次更换接口导致的代码改动折损。工厂设计模式是提高代码扩展性的一种基础实现方式,如同硬件工厂生产零件,农场生产水果蔬菜,养鸡场饲养小鸡一样,工厂设计模式也是为了构建一个类似以上这种生产者-消费者的模式来尽可能做到各层之间职责单一,高效协同。举一个例子,现在有一个农场,需要根据每周老板给定的要求生产对应类型的水果蔬菜。第一周,老板要求大量生产西红柿,第二周老板要求大量生产苹果,第三周,老板要求大量……。众所周知,在这种模式下,老板为了确保农场的效益肯定会不定时根据当时的市场实际需求来制定生产计划,是不可能在一开始就全部规划好并且一尘不变的。我们仔细想这个问题,不就像我们开发出的项目客户一样,客户的需求变化,我们就需要做出对应调整,不可能要求用户一次需求确认就不能再次更改的,客户也是根据使用过程中其他的原因肯定是会对需求做出变动和调整的。在农场这个问题中,如果用传统的OOP设计思想来表示依赖关系,会有什么后果呢?我们来细理一下就明白了:

刚才说了,老板制定的生产需求也是根据实际市场需求会不定时调整的。如果我们一开始就把老板初定的需求全部理好,定义出来对应的接口和实现类,但是当老板需求变动的时候,就需要大量新增和修改对应的接口实现类;如果老板需求变动很大,比如突然有一天农场要大规模转型,不再主要种植蔬菜和水果,开始饲养动物和牲畜,这些定义的接口和实现类又需要全部重新定义,如果农场逐渐壮大,代码的改动量就可想而知了。但是如果使用工厂设计模式呢?我们可以统一定义一个农场的工厂,并通过反射机制注入T类型的实例,这样不管下面的接口和实现类怎么变动和调整,获取对应类型实例的时候只需要都通过这个工厂类来构建对应接口实现类类型的实例即可,以上这种问题得到了很好的一种解决方式,减轻了依赖关系下的代码改动量,并且能够更加灵活的构建各种类型的实例,对于频繁变动的需求也不再需要大量的重构和反复修改,这也就是工厂设计模式的思想和核心所在。

 

3. Spring IOC的实现方式

(1).XML配置式:定义组件(Define)、注入组件(Inject)

在传统Spring的applicationContext-mybatis.xml核心配置文件中实现Bean组件的定义和注入,常用注入方式如下:

1.1设值注入

设置注入也就是把依赖的属性全部定义为private,并通过封装生成的对应属性set方法来实现注入的一种方式。

1.2构造注入

构造注入,顾名思义就是通过带参构造函数的方式给类中的属性注入Bean

1.3 p命名空间注入

P命名控件主要也是依赖于set方法设值注入,是设值注入的一种简化配置方式

1.4注入常用类型的元素

对于不同类型的属性,SpringIoC配置式注入都提供了大量的标签元素来实现不同类型属性值的灵活注入。

 

(2).注解驱动式:创建组件(Build)、自动装配(Autowired)

注解驱动主要基于Spring内置的注解实现,对传统xml配置方式中标签的属性进行了封装,作为注解的属性,按需设置即可,比传统xml配置更加灵活快捷。针对持久层、业务逻辑层、控制器都提供了对应的注解来注册对应类型的组件到Spring容器中,并交由Spring容器统一管理。

2.1创建组件:将组件创建出来并放入Spring容器中,交由Spring容器统一管理。

2.2自动装配:从Spring容器中根据指定的匹配规则去找到对应的组件,并直接取出引用到需要引用的类中。

 

小结:不论是配置式还是注解驱动式实现SpringIOC,总的方式都是先创建组件放入容器,需要的时候直接从容器中取出引用。Spring容器创建组件默认采用的是单例设计模式,即创建组件时,创建的组件实例默认在Spring生命周期中只存在一个,但是可以在需要引用实例的地方多次引用。

 

参考文献:

CSDN 《依赖注入详解》

开源中国《Spring IoC的实现原理》

SourceForge 《Building Build With Spring IoC》

云教程中心《深入SpringIOC》

 

相关文章: