1.概念
控制反转(IoC,Inversion of Control)是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。
以前我们在代码中,使用new 构造方法创建对象,现在不用了, 由容器代替开发人员管理对象。
2.传统方式和ioc方式的区别
下面用制造汽车需要引擎、轮胎等零件的场景解释含义
-
传统方式
我们要实现某一个功能或者说是完成某个业务逻辑时至少需要两个或以上的对象来协作完成,每个对象在需要使用他的合作对象时,自己均要使用像new object() 这样的语法来将合作对象创建出来,这个合作对象是由自己主动创建出来的,创建合作对象的主动权在自己手上,自己需要哪个合作对象,就主动去创建,创建合作对象的主动权和创建时机是由自己把控的,而这样就会使得对象间的耦合度高了,A对象需要使用合作对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是紧密耦合在一起。
下面用制造汽车需要引擎、轮胎等零件的场景解释含义
-
ioc
创建合作对象B的工作是由Spring来做的,Spring创建好B对象,然后存储到一个容器里面,当A对象需要使用B对象时,Spring就从存放对象的那个容器里面取出A要使用的那个B对象,然后交给A对象使用,至于Spring是如何创建那个对象,以及什么时候创建好对象的,A对象不需要关心这些细节问题(你是什么时候生的,怎么生出来的我可不关心,能帮我干活就行),A得到Spring给我们的对象之后,两个人一起协作完成要完成的工作即可。
下面用制造汽车需要引擎、轮胎等零件的场景解释含义
- 假设现在要生产汽车、货车、客车、出租车,它们都是使用【1代引擎】
代码如下:
public class EngineV1 {
private String name = "1代引擎";
public String getName() {
return name;
}
}
/**
* 汽车
*/
public class Car {
private String carName = "小汽车";
private EngineV1 engineV1;
public Car(EngineV1 engineV1) {
this.engineV1 = engineV1;
}
public void info() {
System.out.println(carName + "使用的是" + engineV1.getName());
}
}
/**
* 货车
*/
public class Van {
private String carName = "货车";
private EngineV1 engineV1;
public Van(EngineV1 engineV1) {
this.engineV1 = engineV1;
}
public void info() {
System.out.println(carName + "使用的是" + engineV1.getName());
}
}
代码测试:
Car car = new Car(new EngineV1());
car.info();
Van van = new Van(new EngineV1());
van.info();
// 结果
// 小汽车使用的是1代引擎
// 货车使用的是1代引擎
总结:现在可以看出不管是汽车还是货车,都是依赖于【1代引擎】的,耦合度很高,没有【1代引擎】就没有这些车。假如我们还有很多其他类型的车都是依赖于【1代引擎】,有一天我想把所有的【1代引擎】换成 【2代引擎】我该怎么做?如果手动将所有的【1代引擎】通过全局搜索修改为【2代引擎】,想想就非常麻烦。
-
我们需要换一种思路来实现,解决上述问题。
/** * 引擎接口 */ public interface IEngine { String getName(); }
public class EngineV1 implements IEngine{ private String name = "1代引擎"; public String getName() { return name; } }
public class EngineV2 implements IEngine{ private String name = "2代引擎"; public String getName() { return name; } }
/** * 汽车 */ public class Car { private String carName = "小汽车"; // 现在不依赖于具体哪个引擎,直接对接引擎接口 private IEngine engine; public Car(IEngine engine) { this.engine = engine; } public void info() { System.out.println(carName + "使用的是" + engine.getName()); } }
/** * 货车 */ public class Van { private String carName = "货车"; private IEngine engine; public Van(IEngine engine) { this.engine = engine; } public void info() { System.out.println(carName + "使用的是" + engine.getName()); } }
代码测试:
Car car = new Car(new EngineV1()); car.info(); Van van = new Van(new EngineV1()); van.info(); // 结果 // 小汽车使用的是1代引擎 // 货车使用的是1代引擎
Car car = new Car(new EngineV2()); car.info(); Van van = new Van(new EngineV2()); van.info(); // 结果 // 小汽车使用的是2代引擎 // 货车使用的是2代引擎
总结:代码中不再依赖于具体,而是依赖于抽象容器,即要针对接口编程,不针对实现编程。过去思维中想要什么依赖,需要自己去 “拉” 改为抽象容器主动 “推” 给你,你只管使用实体就可以了。这是依赖倒转 (DIP) 的一种表现形式。由原来的汽车依赖引擎转化成了引擎依赖引擎接口了,进行了反转。现在生产汽车的时候,给汽车什么引擎它就用什么引擎,汽车就脱离了只能用【1代引擎】绝对绑定形式了。
我们还可以接着优化,现在是使用引擎的时候我们手动去new的。假如项目中有很多的地方使用了【1代引擎】,要换成【2代引擎】,即把全部的
new EngineV1()
改成new EngineV2()
。 -
针对上面的问题进行优化。我们可以先创建一个仓库出来,把引擎先创建好放在容器里面。车辆要用引擎的时候直接从仓库里面获取。
/**
* 容器
*/
public class Container {
private Map<String, Object> map = new HashMap<String, Object>();
public Container() {
// 此处利用可以利用读取配置的方式,利用反射动态创建出对象
map.put("engine", new EngineV1());
}
public Object getBean(String name) {
return map.get(name);
}
}
代码测试:
// 创建容器
Container container = new Container();
Car car = new Car((IEngine) container.getBean("engine"));
car.info();
Van van = new Van((IEngine) container.getBean("engine"));
van.info();
// 结果
// 小汽车使用的是1代引擎
// 货车使用的是1代引擎
现在要把【1代引擎】化成【2代引擎】,我们只需要修改容器里面的引擎即可,只要修改一处地方。
map.put("engine", new EngineV2());
上面的测试结果就变成了
// 创建容器
Container container = new Container();
Car car = new Car((IEngine) container.getBean("engine"));
car.info();
Van van = new Van((IEngine) container.getBean("engine"));
van.info();
// 结果
// 小汽车使用的是2代引擎
// 货车使用的是2代引擎
总结:这就是IOC要做的事情。把创建对象的过程交给spring管理,我们需要用的时候直接拿就行。