什么是IOC?
IOC:控制反转,通俗点讲,将对象的创建权交给spring,我们需要new对象,则由spring帮我们创建,然后供我们使用。
1.控制反转的目的是什么?
控制反转的目的是——解耦
2.控制反转是如何实现的解耦?
没有控制反转之前我们实现解耦的一个重要手段是使用工厂模式。
先来看一下工厂模式,我们假设用户向工厂购买不同的产品,工厂负责生产给用户,用工厂模式实现的话,代码如下:
产品:
// 产品接口
public interface IProduct {
}
// 产品A
public class ProductA implements IProduct {
ProductA() {
System.out.println("产品A");
}
}
// 产品B
public class ProductB implements IProduct {
ProductB(){
System.out.println("产品B");
}
}
工厂:
//工厂接口
public interface IFactory {
public IProduct production();
}
// 工厂A
public class FactoryA implements IFactory {
// 多态:工厂A只能生产产品A
private IProduct product = new ProductA();
@Override
public IProduct production() {
return product;
}
}
//工厂B
public class FactoryB implements IFactory {
// 多态:工厂B只能生产产品B
private IProduct product = new ProductB();
@Override
public IProduct production() {
return product;
}
}
用户:
public class User {
public static void main(String[] args) {
// 购买产品A
FactoryA fa = new FactoryA();
fa.production();
// 购买产品B
FactoryB fb = new FactoryB();
fb.production();
}
}
应该说工厂模式已经很好的实现了解耦,当用户需要新产品时,无需再修改原有的任何代码,只需要新增一个产品实现类和一个工厂实现类就可以轻松的实现扩展。
但是我们可以发现,工厂模式有一个缺陷:随着产品种类的增加,我们需要增加同样多种类的工厂
原因就在于这一段代码的耦合造成的:
// 工厂A只能生产产品A
private IProduct product = new ProductA();
扫描二维码关注公众号,回复: 3721173 查看本文章// 工厂B只能生产产品B
private IProduct product = new ProductB();
我们来设想一下,目前的情况是,工厂的功能已经事先定义好了,用户需要产品A,只能向A工厂要,需要产品B只能向B工厂要。
那么能不能由用户决定工厂的功能,当用户需要产品A时,就要求工厂生产产品A,当用户需要产品B时,就要求工厂生产产品B。
这样我们就只需要一个工厂实现类,就可以获得不同种类的产品,而且也无需更改现有代码,需要新产品时,只需要写新的产品实现类就可以了。
也就是说:我们就要求程序实现这样一种功能
这里我们不再规定某个工厂生产某个固定产品,而是在用户程序模块中,根据需要改变工厂能够生产的产品种类
3.Spring如何实现控制反转?
经常的,我们把控制反转又叫做依赖注入,在我看来,应该是为了实现控制反转,而采用了依赖注入的方式。
而Spring的核心功能之一就是依赖注入。Spring实现依赖注入的方式之一是:在工厂和用户之间加入一层代码(即Spring容器),用来读取外部的XML文件中配置好的工厂类型的名字,并且实例化这个工厂类,存入Map中(Map<Sting,Object>),当用户需要调用哪个工厂时,就去这个Map中取得对应名称的哪个工厂实例。
所以本质上,针对不同产品的工厂,还是被实例化了,但是这不需要我们去写一个个不同的工厂对象,而是通过外部XML数据注入数据,交由Spring容器来实现的。
所以本质上,针对不同产品的工厂,还是被实例化了,但是这不需要我们去写一个个不同的工厂对象,而是通过外部XML数据注入数据,交由Spring容器来实现的。
代码解析:
产品:
// 产品接口
public interface IProduct {
}
// 产品A
public class ProductA implements IProduct {
ProductA() {
System.out.println("产品A");
}
}
// 产品B
public class ProductB implements IProduct {
ProductB(){
System.out.println("产品B");
}
}
工厂:
// 工厂接口
public interface IFactory {
public IProduct production();
}
// 工厂实例:针对不同的产品,不再新增不同的工厂
public class Factory implements IFactory {
// 不再指定具体产品
private IProduct product;
@Override
public IProduct production() {
return product;
}
}
XML文件:
<beans>
<bean id="pa" class="com.****.ProducA" />
<bean id="pb" class="com.****.ProducB" />
</beans>
模拟Spring容器:
//bean工厂接口:用来承载不同的工厂类型
public interface IBeanFactory {
public Object getBean(String name);
}
// bean工厂实例:将所有工厂类型全部实例化到一个Map中
public class BeanFactory implements IBeanFactory {
private Map<String, Object> beans = new HashMap<String, Object>();
// 构造函数读取XML文件中的配置项,并实例化XML中规定的类,存入Map中
public BeanFactory () {
/** 为避免知识点过多造成理解困难,本段代码不展示,其具体功能是:
1.通过JDOM或者DOM4J读取XML文件(需导入jar包),获得对应的工厂类型名称(id)和class(不同工厂实现类的类名)
2.通过Object o = Class.forName(factoryClass).newInstance();的方式构造不同的工厂类实例
3.将工厂类型名称(id)和工厂类实例(Object)存入Map<String,Object>,即代码中的beans中
**/
}
@Override
public Map<String,Object> getBean(String name) {
// 拿到Map,Map中经过以上的调用,已经生成了不同的bean对象和该对象的名称
return beans;
}
}
当我们需要新产品的时候,不需要再新增新的工厂类型实例,只需要实现新的产品实例,然后在XML文档中添加一条产品的配置即可。
这样的情况可以被描述为:工厂对产品的依赖,不是一开始就写好的代码,而是依靠外部XML中的配置项来动态注入的,这可以被称为依赖注入