Spring的IoC(控制反转) 、DI(依赖注入)的理解
分享Bromon的blog上对IoC与DI浅显易懂的讲解
1.1、IoC(控制反转)
首先想说说IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。
那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
1.2、DI(依赖注入)
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。
理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在spring的框架中堆积木而已。
用图例说明一下,传统程序设计如图1-1,都是主动去创建相关对象然后再组合起来:

图1-1 传统应用程序示意图
当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了,如图1-2所示:
图1-2有IoC/DI容器后程序结构示意图
我的理解,运用拍电影的例子
拍电影大家都知道,需要编剧,导演,演员,摄影师。就拿《大话西游》这部电影来举例把。
编剧(写代码的程序猿):编剧在编写剧本的时候,是不知道剧本中的至尊宝和紫霞仙子的演员是谁的。也就是说编剧只关注我的剧本中的角色以及剧情,不关心这些角色由谁来扮演。
导演(spring容器):导演需要负责演员的挑选以及其他工作。
演员(被管理的对象): 演员有很多,刘德华啊,张学友啊,周星驰什么的。
摄影师(模拟程序运行):按照剧本和选择的演员,拍摄电影。
spring的控制反转:导演,就充当着spring容器的作用,前来应聘的演员都在导演这里登记,由导演来管理(如spring管理对象的生命周期,对象的创建和销毁)。假如现在刘德华,张学友,周星驰都想应聘《大话西游》,导演会录入他们的个人信息并登记。也就是说,电影中角色扮演者是由导演一手控制。编剧和摄像师不需要考虑谁来演这个问题,只需要管好自己的事情。(解耦)
spring的依赖注入: 编剧方面,在演员没有确定好之前,剧本中的人物都是虚幻的,没有实例化的,也就是还无法开拍(程序无法运行,因为有些依赖没有到位),但这并不影响编剧写剧本。spring的依赖注入,在电影这个例子中可以解释为-在导演确定好演员,编剧写好剧本后,就该正式开拍了。拍摄是按照剧本来的,剧本中的人物又需要现实的明星来表演,这里就要用到依赖注入了。比如剧本中至尊宝带金刚圈的剧情,拍摄时需要人来演至尊宝,就找导演要人(找spring要合适的对象),导演已经确定了周星驰比较适合演至尊宝,就直接把周星驰扔给他们。
下面用实例来说明一下
目录结构:

Person类,表示人(演员都是人)。有get,set方法。
person类有一个say(String str)的方法,就是人说话。(模拟演员说台词)
package com.sicau.test;
/**
* Person
* create by chenshihang on 2018/10/26
*/
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void say(String str){
System.out.println(this.getName()+"口音:"+str);
}
}
TestController类。用来测试
这个类可以看作是编剧,写剧本。可以看到,这段剧本需要两个Person,一个角色是自尊包,一个角色是紫霞。(但编剧并不关心具体演员是谁)
package com.sicau.controller;
import com.sicau.test.A;
import com.sicau.test.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
/**
* TestController
* create by chenshihang on 2018/10/24
*/
@Controller
public class TestController {
@Autowired//默认是通过type匹配
@Qualifier("zhizunbao")//结合该注解,可以通过name去匹配
private Person zhizunbao;
@Autowired
@Qualifier("zixia")
private Person zixia;
@RequestMapping("/toTest1.form")
public String Test1(){
System.out.println("toTest1");
zhizunbao.say("hehe");
zixia.say("haha");
return "test";
}
}
spring容器核心配置文件 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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.sicau.*"/>
<bean id="zhizunbao" class="com.sicau.test.Person">
<property name="age" value="1"/>
<property name="name" value="z周星驰"/>
</bean>
<bean id="zixia" class="com.sicau.test.Person">
<property name="age" value="12"/>
<property name="name" value="朱茵"/>
</bean>
</beans>
当程序运行时(拍摄时)
浏览器访问:

进入编剧写好的剧本Test1

导演将周星驰和朱茵扔给摄影师,周星驰和朱茵就充当了至尊宝和紫霞
正式表演,剧本中至尊宝说hehe,紫霞说haha(程序运行)
控制台打印出信息:


浙公网安备 33010602011771号