本文共 5073 字,大约阅读时间需要 16 分钟。
由发表在
IoC(Inversion of Control,控制反转)也称为依赖注入(Dependency Injection),作为Spring的一个核心思想,是一种设计对象之间依赖关系的原则及其相关技术,作为Spring的一个关键技术,让我们好好的了解一下吧。
高内聚低耦合可以说是软件技术形态的终极目标。用学术界的话来说,软件的两个本质特性就是构造性和演化性,高内聚低耦合的设计能够让构造和演化都更加高效,比如:
软件设计各种技术的出现,无一不是朝着这个终极目标的努力。面向对象、基于组件(学术界称为构件)的软件开发、面向切面编程(AOP)、Java近些年流行的模块化方法(比如OSGi技术)等等,这些方法和技术的出现,无外乎都是为了让软件更加高内聚低耦合。与此同时,各路大神还提出各种软件设计原则和模式,来规范我们的软件形态。我们今天谈的IoC也是其中的一个大招。IoC(Inversion of Control,控制反转)也称为依赖注入(Dependency Injection),作为Spring的一个核心思想,是一种设计对象之间依赖关系的原则及其相关技术。
先来看看字面上怎么来解释:当一个对象创建时,它所依赖的对象由外部传递给它,而非自己去创建所依赖的对象(比如通过new
操作)。因此,也可以说在对象如何获取它的依赖对象这件事情上,控制权反转了。这便不难理解控制反转和依赖注入这两个名字的由来了。
上面的解释听起来还是有点晦涩,让我们来看看具体的例子吧!
有个土豪老板,我们经常要出差,因此经常要订机票。定机票呢,可以通过去哪儿网订票,也可以通过携程订票。
我们马上可以想到可以通过三个类来表达这个场景,Boss
,QunarBookingService
,CtripBookingService
。当然了,我们还应该提供一个BookingService
接口,作为QunarBookingService
,CtripBookingService
的公共抽象。面向接口编程是面向对象设计的基本原则,如果这都不了解,赶紧先回去看GoF的《设计模式》第一章!
BookingService.java
package com.tianmaying.iocdemo;public interface BookingService { void bookFlight();}
QunarBookingService.java
package com.tianmaying.iocdemo;public class QunarBookingService implements BookingService { public void bookFlight() { System.out.println("book fight by Qunar!"); }}
CtripBookingService.java
package com.tianmaying.iocdemo;public class CtripBookingService implements BookingService { public void bookFlight() { System.out.println("book fight by Ctrip!"); }}
好了,土豪出门谈生意,得订机票了,Boss就琢磨着怎么订票呢,Boss比较了一下价格,这一次决定用去哪儿,对应的Boss
的代码:
Boss.java
package com.tianmaying.iocdemo;public class Boss { private BookingService bookingService; public Boss() { this.bookingService = new QunarBookingService(); } public BookingService getBookingService() { return bookingService; } public void setBookingService(BookingService bookingService) { this.bookingService = bookingService; } public void goSomewhere() { bookingService.bookFlight(); }}
在Boss
的构造函数中,将其orderService
成员变量实例化为QunarOrderService
,goSomewhere()
函数中就可以调用orderService
的bookFlight
方法了!
为了把这个场景Run起来,我们还需要一个main函数:
package com.tianmaying.iocdemo;public class App { public static void main(String[] args) { bossGoSomewhere(); } static void bossGoSomewhere() { Boss boss = new Boss(); boss.goSomewhere(); }}
运行之后可以看到控制中可以打印出"book fight by Qunar!"了。
在这个例子中,我们看到Boss
需要使用OrderService
,于是Boss
自己实例化了一个QunarOrderService
对象。同志们想想,身为土豪Boss,思考的都是公司战略的事儿,定个票还要自己选择通过什么方式来完成,这个Boss是不是当得实在太苦逼。
所以土豪赶紧给自己找了个美女秘书(别想歪!),Boss要出差时,只需要说一声他需要订票服务,至于是哪个服务,让美女秘书选好后告诉他即可(注入啊!注入!)。(别跟我较真说美女秘书直接把票送上就行!)
这样的话,Boss是不是一身轻松了? 而这个美女秘书还是免费包邮的,这正是Spring扮演的角色!来看看使用Spring之后的代码。
我们在pom.xml文件中加入依赖(项目使用Maven作为构建工具):
org.springframework spring-context 4.2.0.RELEASE
QunarBookingService.java
package com.tianmaying.iocdemo;import org.springframework.stereotype.Component;@Componentpublic class QunarBookingService implements BookingService { public void bookFlight() { System.out.println("book fight by Qunar!"); }}
这里我们使用Spring的@Component
标注将QunarBookingService
注册进Spring的Context,这样它就可以被注入到需要它的地方!相应地,创建QunarBookingService
实例的责任也交给了Spring。我们说了,美女秘书帮你搞定嘛!
新建一个SmartBoss
类,聪明的老板知道把选择订机票服务这样的杂事交给秘书来做。
SmartBoss.java
package com.tianmaying.iocdemo;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;@Componentpublic class SmartBoss { private BookingService bookingService; @Autowired public void setBookingService(BookingService bookingService) { this.bookingService = bookingService; } public BookingService getBookingService() { return bookingService; } public void goSomewhere() { bookingService.bookFlight(); }}
在上面的代码中,SmartBoss
不再自己创建BookingService
的实例,只是通过@Autowired
标注告诉Spring小秘我需要一个BookingService
!
调用代码因此也要做一些小修改,需要创建Spring的Context:
static void smartBossGoSomewhere() { AbstractApplicationContext context = new AnnotationConfigApplicationContext( App.class); try { SmartBoss boss = context.getBean(SmartBoss.class); boss.goSomewhere(); } finally { context.close(); }}
回到正题,通过上面的例子,我们来看看IoC到底带来了哪些好处?
Boss
没有和某个具体的BookingService
类耦合到一起了,这样Boss
的维护和演化就更加方便。想象一下,如果Boss
需要改用CtripBookingService
,这时也不需要修改Boss.java
的代码,更换接口的实现非常方便,给Boss注入新的实现即可,轻松惬意。(当然,要做到热插拔还需要进一步的工作,要么得玩转类加载器这玩意,或者借助OSGi这样的神器)。这也是典型的开放-封闭原则的例子,即对现有模块,功能扩展应该是开放的,而对其代码修改应该是封闭的,即能够做到不需要修改已有代码来扩展新的功能。
想象一下,如果Boss
自己直接去实例化QunarBookingService
,而QunarBookingService
在另外一个Package中甚至另外一个Jar包中,你可得import进来才能使用,紧耦合啊!现在好了,Boss
只依赖于抽象接口,测试更方便了吧,Mock一下就轻松搞定!Boss
和QunarBookingService
彼此不知道对方,Spring帮两者粘合在一起。
为什么IoC是个大招,因为它会自然而然得促进你应用一些好的设计原则,会帮助你开发出更加“高内聚低耦合”的软件。
最后我们简单说说IoC是如何实现的。想象一下如果我们自己来实现这个依赖注入的功能,我们怎么来做? 无外乎:
我们发现其实自己来实现也不是很难,Spring实际也就是这么做的。这么看的话其实IoC就是一个工厂模式的升级版!当然要做一个成熟的IoC框架,还是非常多细致的工作要做,Spring不仅提供了一个已经成为业界标准的Java IoC框架,还提供了更多强大的功能,所以大家就别去造轮子啦!希望了解IoC更多实现细节不妨通过学习Spring的源码来加深理解!
更多文章请访问