我们通过小故事来理解IOC和DI的关系
现实生活中,比如想吃水饺,在没有水饺店的日子里,最直观的做法就是:买面粉、蔬菜、肉等等食材,再去制造也就是去煮熟。这些都是主动创建的过程。也就是说想吃一顿水饺需要自己创造。
然而到了今时今日,水饺店出现,当我们想吃水饺时,只需要知道水饺点的联系方式,通过电话等渠道描述你的需求、地址、联系方式、下订单等待,过一会就有人送来水饺啦。
此时请注意你并没有 “主动” 创造水饺,而是由水饺店为你创造的,然而达到你的要求,甚至创造比你会更好一些。其实我们可以把水饺店理解为Spring框架中的控制反转。
IoC:Inverse of Control(控制反转)
读作“反转控制”,更好理解,不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
简单说就是创建对象由以前的程序员自己new 构造方法来调用,变成了交由Spring创建对象
让我们用代码来说明:
一、pom文件
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<springversion>4.0.0.RELEASE</springversion>
<junitversion>4.9</junitversion>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junitversion}</version>
<scope>test</scope>
</dependency>
<!-- spring核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springversion}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springversion}</version>
</dependency>
</dependencies>
二、创建饺子类
public class Dumplings {
private String pie; // 什么馅
private String size; // 大小份
// 省略setget方法
}
三、创建 applicationContext.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="dumplings" class="com.pojo.Dumplings">
<property name="pie" value="芹菜猪肉馅水饺"/>
<property name="size" value="超大份"/>
</bean>
</beans>
四、测试类
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[]{"applicationContext.xml"}
);
Dumplings dumplings = (Dumplings) context.getBean("dumplings");
System.out.println(dumplings.getPie());
System.out.println(dumplings.getSize());
}
五、测试结果
芹菜猪肉馅水饺
超大份
通过以上的实现我们可以总结出以下2点。
传统方式:
通过new 关键字主动创建一个对象。
IOC方式:
对象的生命周期由Spring来管理。直接从Spring获取一个对象。就像控制权自己手里交给了Spring。
DI:Dependency Injection(依赖注入)
指 Spring 创建对象的过程中,将对象依赖属性(简单值,集合,对象)通过配置设值给该对象
继续上面的例子
新增饭店的类
public class Hotel {
// 饺子类
private Dumplings dumplings;
// 果汁类
private Juice juice;
// 省略setter方法
// 生成订单
public String getOrderInfo() {
return "小明点了:"+dumplings.getSize()+","+dumplings.getPie()+"和"+juice.getName();
}
}
新增果汁类
public class Juice {
// 果汁名称
private String name;
// 省略setget方法
}
修改 applicationContext.xml 文件内容
- 注意:这里要使用 ref 来注入另一个对象
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="dumplings" class="com.pojo.Dumplings">
<property name="pie" value="芹菜猪肉馅水饺"/>
<property name="size" value="超大份"/>
</bean>
<bean name="juice" class="com.pojo.Juice">
<property name="name" value="苹果汁"/>
</bean>
<bean name="hotel" class="com.pojo.Hotel">
<property name="dumplings" ref="dumplings"/>
<property name="juice" ref="juice"/>
</bean>
</beans>
测试方法
@Test
public void saveOrderTest(){
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[]{"applicationContext.xml"}
);
Hotel hotel = (Hotel) context.getBean("hotel");
System.out.println(hotel.getOrderInfo());
}
测试结果
小明点了:超大份,芹菜猪肉馅水饺和苹果汁
总结: 依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。
借鉴于 Martin Fowler 2004年写的<Inversion of Control Containers and the Dependency Injection pattern>
文章地址 http://martinfowler.com/articles/injection.html
2004年,Martin Fowler 探讨了同一个问题,既然IOC是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。他的这个答案,实际上给出了实现IOC的方法:注入。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。
聊聊使用IOC需要注意的要点
软件系统中由于引入了第三方IOC容器,生成对象的步骤变得有些复杂,本来是两者之间的事情,又凭空多出一道手续,所以,我们在刚开始使用IOC框架的时候,会感觉系统变得不太直观。所以,引入了一个全新的框架,就会增加团队成员学习和认识的培训成本,并且在以后的运行维护中,还得让新加入者具备同样的知识体系。
由于IOC容器生成对象是通过反射方式,在运行效率上有一定的损耗。如果你要追求运行效率的话,就必须对此进行权衡。
具体到IOC框架产品(比如:Spring)来讲,需要进行大量的配制工作,比较繁琐,对于一些小的项目而言,客观上也可能加大一些工作成本。
IOC框架产品本身的成熟度需要进行评估,如果引入一个不成熟的IOC框架产品,那么会影响到整个项目,所以这也是一个隐性的风险。
我们大体可以得出这样的结论:一些工作量不大的项目或者产品,不太适合使用IOC框架产品。另外,如果团队成员的知识能力欠缺,对于IOC框架产品缺乏深入的理解,也不要贸然引入。最后,特别强调运行效率的项目或者产品,也不太适合引入IOC框架产品,像WEB2.0网站就是这种情况。
聊聊IOC的优缺点
IOC的优点
实现组件之间的解耦,提高程序的灵活性和可维护性。
IOC的缺点
创建对象的步骤变复杂了,不直观,当然这是对不习惯这种方式的人来说的。
因为使用反射来创建对象,所以在效率上会有些损耗。但相对于程序的灵活性和可维护性来说,这点损耗是微不足道的。
缺少IDE重构的支持,如果修改了类名,还需到XML文件中手动修改,这似乎是所有XML方式的缺憾所在。
总结
这么看的话其实IoC就是一个工厂模式的升级版!当然要做一个成熟的IoC框架,还是非常多细致的工作要做,Spring不仅提供了一个已经成为业界标准的Java IoC框架,还提供了更多强大的功能。