大白话说Spring:IOC控制反转和DI依赖注入
我们都知道,Spring两大核心思想为IOC和AOP。
而IOC的中文名是控制反转,同时我们要注意IOC是一种思想,并不是一种实现。
现在我们将通过尽可能通俗的方式去讲解IOC。
IOC诞生的历史背景
我们首先要知道,一个技术诞生,都是为了解决一个实际的生产问题。
在IOC诞生之前,我们创建对象的方式,是通过new关键词进行创建,而如果要进行对象与对象的关联,也是要在对象内部再进行new操作。
比如我们现在有一个A类和B类,如下:
class A{
public void write(){
B b = new B();
b.say();
}
}
class B{
public void say(){
System.out.println("Hellow,World!");
}
}
A a = new A(); //实例化一个对象a
a.write();
结果:
Hellow,World!
如上所看到的例子,A类的write方法实现,依赖于B类,所以要在A类里面通过关键词new创建B的实力,进行方法调用。
这种方法的弊端?
(1)需要由A类去管理B类。比如A类通过new关键词创B类的实例。
(2)导致A类与B类耦合。也就意味着,代码固定了say方法只能通过B类获取,而如果B类被修改了,实现say方法的类改为C类的话,则要手动修改B为C。
引入IOC后的改变
而采用了IOC控制反转之后,上面的示例代码就可以变为如下:
@Component
public class B{
public void say(){
System.out.println("Hellow,World!");
}
}
public class A{
//通过@Resource,获取bean,然后注入到b中
@Resource
private B b;
public void write(){
//这里不用在通过关键词new进行创建示例b,而是可以直接调用say方法。
b.say();
}
}
我们查看上面的代码,并没有通过new关键字创建B的实例对象。
可能有人说,private B b
这句代码不就是创建一个B的实例吗?难道不还是说明是由A控制B的实例?
这样理解就错误了,private B b
只是声明一个实例b而已,并没对其进行赋值,其实就是一个空的对象。也就相当于private B b = null
。
而通过@Resource注解,是获取IOC容器创建的B的实例,然后赋值给A里面的b而已。
所以这里对B实例的创建就交给了IOC容器。
所以IOC解决了什么问题?
“解决了对象的过分耦合”
那什么又是DI依赖注入?
“DI就是IOC,只是从不同的角度说明同一个事情。”
IOC指的是控制反转,也就是把原本要开发人员通过new关键词等方式对对象进行管理,现在交给IOC容器进行管理。
DI指的是依赖注入,就是把对象之间的依赖关系的实现交给了容器。
我们再看一下上面那个例子
class A{
public void write(){
B b = new B();
b.say();
}
}
class B{
public void say(){
System.out.println("Hellow,World!");
}
}
A a = new A(); //实例化一个对象a
a.write();
结果:
Hellow,World!
A的write方法实现,需要依赖于B,在传统方法中,我们直接通过New关键字实例化,然后调用b的方法。这个给A加入B的实例对象是我们自己开发人员代码实现。
而使用了DI依赖注入之后,就变成如下
@Component
public class B{
public void say(){
System.out.println("Hellow,World!");
}
}
public class A{
//通过@Resource,获取bean,然后注入到b中
@Resource
private B b;
public void write(){
//这里不用在通过关键词new进行创建示例b,而是可以直接调用say方法。
b.say();
}
}
我们可以看到,在A类中虽然声明了B的实例b,但是b只是空壳,其值为null。
而真正进行赋值操作的是IOC容器,IOC容器扫描到A类中需要获取一个B的实例,然后在程序运转的某个时刻,给A类中的private B b
赋值,这之后A类中的b不再是null,而是有真实的值。
这种把实例的赋值操作交给IOC容器的方式,就是DI依赖注入。