学习过Spring的都知道Spring有三大核心的概念,分别是控制反转(IOC)、依赖注入(DI)和面向切面编程(AOP)。今天就来聊聊我对控制反转和依赖注入的了解,至于面向切面编程放到后面讲解。
一、控制反转(Ioc)
控制反转(Inversion of Control)简称Ioc,他是一种设计思想,目的是指导我们设计出更加松耦合的程序。(解耦)。
要想了解控制反转,我们就需要了解以下几个问题:
- 控制反转出现之前是怎么做的?
- 控制反转出现之后解决了什么问题?
- 为什么要叫他控制反转?
1.1、Ioc出现之前
场景一:现在有两个类A,B,其中各有一个方法a,b,a中方法的执行需要依赖于b方法,
通常我们会把代码写成下面格式:
public class A {
private B b;
public A() {
this.b = new B();
}
public void a(){
b.b();
System.out.println("A中的a方法执行了");
}
}
public class B {
public void b(){
System.out.println("B中的b方法执行了");
}
}
测试类
public class IocTest {
public static void main(String[] args) {
A a = new A();
a.a();
}
}
结果
B中的b方法执行了
A中的a方法执行了
从上面的过程中我们可以看出,A中的a方法执行需要依赖于B中的b方法的执行。因此我们需要首先创建出一个A的对象,用这个对象去调用a方法,但是a方法又依赖于b方法,所以需要先创建出一个B对象,再用B对象去调用B中的b方法。
但是上面的代码又存在这一个问题,由于B在A中是写死的,每次调用a方法都需要创建出一个B对象去调用b方法,那么这个过程能不能由开发者自己操作呢?
于是我们将A改造成如下所示
public class A {
private B b;
public A(B b) {
this.b = b;
}
public void a(){
b.b();
System.out.println("A中的a方法执行了");
}
}
我们B改成由开发者自己控制,然后在创建A对象时传进入,这个在一定层度上可以使得我们的代码更加灵活,降低了耦合性。
注意:在控制反转出现之前我们都是通过new的方式进行注入,耦合性较强。
但是上面的代码又出现了新的问题,如果a方法中需要依赖C、D、E、F。。。中的方法呢?我们每次new出来一个A对象都需要创建出对应的C、D、E、F对象,不仅繁琐,代码感官、后期维护等都十分不易,那么有没有什么方法可以解决这种问题呢?
1.2、spring的容器
我们可以考虑在编译之后,创建出一个map容器,把所有的对象都放到这个容器中,如果需要某个对象直接可以通过map的唯一key去获取相应的对象,这样就不需要我们自己一步步去new对象了,这个就是Spring所做的事情,将所有的对象都放到ApplicationContext容器中,使用的时候直接从容器中获取即可。
引入Spring容器后需要进行相应的改造如下:
public class A {
public void a(){
System.out.println("A中的a方法执行了");
}
}
public class B {
public void b(){
System.out.println("B中的b方法执行了");
}
}
<bean id="a" class="com.sxx.service.entity.A"/>
<bean id="b" class="com.sxx.service.entity.B"/>
public class IocTest {
public static void main(String[] args) {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("application.xml");
A a = (A) applicationContext.getBean("a");
a.a();
B b = (B) applicationContext.getBean("b");
b.b();
}
}
执行结果如下:
A中的a方法执行了
B中的b方法执行了
1.3、控制反转
在Spring引入ApplicationContext容器之后,创建对象不再需要我们自己手动new出来一个对象,直接从Ioc容器中获取即可,这便是控制反转。
控制:指的是控制权,现在可以简单理解为对象的创建权限(new)
反转:指的对象的控制权由程序员在类中主动控制(new)反转到由Spring容器来控制。
再具体些即原来我们需要一个对象,只需要自己在类中 new 就行了
现在呢,使用了 Spring 之后,我们不再主动new对象,而是当需要对象的时候直接问spring要(Spring 会利用反射技术提前将所有的对象都创建好,等待我们调用)
由原来new 转而向容器要的过程即时控制反转。
二、依赖注入(DI)
依赖注入(Dependency injection)一种设计模式,即为了解决应用程序和Ioc容器之间的依赖关系,想要理解依赖注入,需要弄清楚以下问题:
1、谁依赖于谁?当然是应用程序依赖于Ioc容器;
2、为什么需要依赖?应用程序需要Ioc容器来提供对象需要的外部资源;
3、谁注入谁?很明显是Ioc容器注入应用程序某个对象,应用程序依赖的对象;
4、注入了什么?就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)
有了依赖注入,上面的需求就可以改造成下面的代码了
public class A {
private B b;
public void a(){
b.b();
System.out.println("A中的a方法执行了");
}
//提供set方法
public void setB(B b) {
this.b = b;
}
}
public class B {
public void b(){
System.out.println("B中的b方法执行了");
}
}
测试类
public class IocTest {
public static void main(String[] args) {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("application.xml");
A a = (A) applicationContext.getBean("a");
a.a();
}
}
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-3.0.xsd">
<bean id="a" class="com.sxx.service.entity.A">
<!--注入b-->
<property name="b" ref="b"/>
</bean>
<bean id="b" class="com.sxx.service.entity.B"/>
</beans>
测试结果如下
B中的b方法执行了
A中的a方法执行了
三、总结
在控制反转(Ioc)和依赖注入(DI)未出现之前,我们要处理AB之间的依赖关系需要我们做如下步骤:
- 手动创建被依赖的对象B;
- 将被创建的的对象B注入到A中;
- 填装完A所需要的属性之后再手动创建A对象;
- 使用A对象。
这样做不仅繁琐,而且不宜维护,扩展性不强。在Spring推出之后,就给这个问题提出了一个新的解决思路,即创建出一个新的Ioc容器,在系统启动时,将所需要的属性(对象也是一种属性)加载到Ioc容器中,我们再想去创建A对象便可以直接创建,不需要考虑到依赖关系,创建的A对象所需要的B对象会被Spring感知并从Ioc容器中找到B对象并创建出一个新的B对象填充进去,这些步骤统统由Spring完成,我们只需要创建出自己所需要的对象即可。
其实对于Spring来说,控制反转(Ioc)和依赖注入(DI)都是对于同一种事务的不同角度的解释而已。举个例子,假如我们想自己组装个汽车,那就需要轮子、方向盘、玻璃、座椅这些属性,早期我们自己想组装的时候需要自己一个个去买,然后自己组装起来就成了一辆汽车,我们可以用它去旅游,后来在提出控制反转(Ioc)和依赖注入(DI)后,我们想到自己一个个买这些属性太麻烦了,就找个工厂(Ioc容器),这个工厂不仅有各种各样的轮胎、方向盘、玻璃、座椅等属性,甚至还有成型的汽车供我们挑选,我们需要什么东西,工厂可以自己感知并装配好提供给我们,我们直接拿来用即可。
在这个过程中,我们从原来自己需要一个个去买属性到后面直接问工厂要的过程就是控制反转,工厂感知并自动注入构建汽车所需要的属性并装配好的过程就是依赖注入。
俗话说一千个读者有一千个哈姆莱特,每个人对于Spring的控制反转(Ioc)和依赖注入(DI)理解都不一样,如果有感觉我理解不对的地方,欢迎大家指正