# 基本概念
IoC(Inversion of Control),直观地讲,就是对象创建或查找对象依赖的控制权由应用代码转到了外部容器,控制权的转移是所谓反转。使用Ioc,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。我们可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它
IoC还有另外一个名字——“依赖注入DI(Dependency Injection)”。从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,形象地说,即由容器动态地将某种依赖关系注入到组件之中。
# IoC场景
上面说的可能有点晕,来一个实际点的例子。
丽萨已经老大不小了,一直没有男朋友,看着别人恩恩爱爱的,也不禁想找个BoyFriend。摆在她面前的有3种方案:主动“邂逅” Or 同事介绍 Or 父母包办。她会选择哪种呢?
![](https://box.kancloud.cn/2016-02-18_56c53c1177842.jpg)
主动“邂逅”方式,如图所示:
![](https://box.kancloud.cn/2016-02-18_56c53c118447f.jpg)
~~~
public class Girl {
public void kiss(){
Boy boy = new Boy();
}
}
~~~
不过这种美好的纯洁的爱情,一般只会发生在校园里,对于已经是工薪阶层的丽萨显然不太适合。
第二方案,同事介绍,
![](https://box.kancloud.cn/2016-02-18_56c53c1195e9f.jpg)
~~~
public class Girl {
void kiss(){
Boy boy = BoyFactory.createBoy();
}
}
~~~
很多人都是这样找到了自己的另一半。丽萨以前也试着去跟同事介绍的handsome man接触过,但是真人与介绍的出入太大,最起码handsome这条就不太符合,而且有他许多缺点。觉得他不适合自己,所以最后也就不了了之。
所以无奈之下,她的难题丢给了父母。父母给她物色了一个“绝世好男人”——曾小贤(这娃有句经典台词:“好男人就是我,我就是....曾小贤”),终于算是遂了她的心愿了。
![](https://box.kancloud.cn/2016-02-18_56c53c11a5158.jpg)
~~~
public class Girl {
void kiss(Boy boy){
// kiss boy
boy.kiss();
}
}
~~~
虽然在现实生活中我们都希望与自己的另一半来场完美的邂逅,但在Spring世界里,跟丽萨一样,选择的却是父母包办,它就是控制反转,而这里具有控制力的父母,就是Spring所谓的容器概念。
典型的IoC可以如图所示。
![](https://box.kancloud.cn/2016-02-18_56c53c11c6fa9.jpg)
# 实例说明IoC注入方式
IoC有3种注入方式:接口注入、Setter方法注入、构造器注入。由于接口注入不推荐使用,所以只介绍setter方法注入和构造器注入。
用代码来说明一切吧:
【Girl.java】
~~~
package com.tgb;
/**
* 期待找BF的Girl
* @author Admin
*
*/
public class Girl {
private Boy bf;
public Girl(){}
public Girl(Boy bf){
System.out.print("使用构造方法方式注入:");
this.bf = bf;
}
public void setBf(Boy bf) {
System.out.print("使用Setter方式注入:");
this.bf = bf;
}
public void kissYourBF() {
bf.kiss();
}
}
~~~
【Boy.java】
~~~
package com.tgb;
/**
* 合格的BF
* @author Admin
*
*/
public class Boy {
public void kiss(){
System.out.println("My boy friend,I'll kiss you!");
}
}
~~~
【Client客户端】
~~~
package com.tgb;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Client {
public static void main(String[] agrs){
ApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml");
Girl lisa = (Girl)factory.getBean("girl");
lisa.kissYourBF();
}
}
~~~
【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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="boy" class="com.tgb.Boy" />
<bean id="girl" class="com.tgb.Girl">
<!--构造器注入-->
<constructor-arg ref="boy"></constructor-arg>
<!--Setter方法注入-->
<!--<property name="bf" ref="boy"></property>-->
</bean>
</beans>
~~~
选择Setter方法注入和构造器注入的控制是在配置文件中完成的。结果如下:
![](https://box.kancloud.cn/2016-02-18_56c53c11d4a12.jpg)
![](https://box.kancloud.cn/2016-02-18_56c53c120194b.jpg)
Setter方法注入时,有2种装载方式需要注意,byName和byType。当我在配置文件中,把2种注入方式都注释掉,同时添加了default-autowire="byType",
~~~
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"
default-autowire="byType"
>
<bean id="boy" class="com.tgb.Boy" />
<bean id="girl" class="com.tgb.Girl">
<!--构造器注入-->
<!--<constructor-arg ref="boy"></constructor-arg>-->
<!--Setter方法注入-->
<!--<property name="bf" ref="boy"></property>-->
</bean>
</beans>
~~~
执行结果如图:
![](https://box.kancloud.cn/2016-02-18_56c53c11d4a12.jpg)
但是换成default-autowire="byName",则会报如下错误:
![](https://box.kancloud.cn/2016-02-18_56c53c1212527.jpg)
这是为什么呢?原因在于,当使用byType方式装载时,Spring是根据classType来确定要实例化的类。所以就算bean的id是boy,跟Girl中bf的Setter名字不一致,依旧可以实例化。但是使用byName时,则是根据id来实例化类的。所以只要把Boy类对应的bean id跟Girl中的setter方法名一致才行,即修改id="boy"为id="bf",即可正常显示:
~~~
<bean id="bf" class="com.tgb.Boy" />
<bean id="girl" class="com.tgb.Girl">
<!--构造器注入-->
<!--<constructor-arg ref="boy"></constructor-arg>-->
<!--Setter方法注入-->
<!--<property name="bf" ref="boy"></property>-->
</bean>
~~~
另一种修改方式就是用显示的方式来设定Setter方法所注入的是哪个类的对象:
~~~
<bean id="boy" class="com.tgb.Boy" />
<bean id="girl" class="com.tgb.Girl">
<!--构造器注入-->
<!--<constructor-arg ref="boy"></constructor-arg>-->
<!--Setter方法注入-->
<property name="bf" ref="boy"></property>
</bean>
~~~
执行结果如图:
![](https://box.kancloud.cn/2016-02-18_56c53c11d4a12.jpg)
### **2种注入方式的比较**
Setter 注入:
- 对于习惯了传统 javabean 开发的程序员,通过 setter 方法设定依赖关系更加直观。
- 如果依赖关系较为复杂,那么构造子注入模式的构造函数也会相当庞大,而此时设值注入模式则更为简洁。
- 如果用到了第三方类库,可能要求我们的组件提供一个默认的构造函数,此时构造子注入模式也不适用。
构造器注入:
- 在构造期间完成一个完整的、合法的对象。
- 所有依赖关系在构造函数中集中呈现。
- 依赖关系在构造时由容器一次性设定,组件被创建之后一直处于相对“不变”的稳定状态。
- 只有组件的创建者关心其内部依赖关系,对调用者而言,该依赖关系处于“黑盒”之中。
- 前言
- Struts 简单小结
- 深入浅出了解Struts的处理流程(有图有真相)
- struts标签+jstl标签之国际化实例
- 一口一口吃掉Hibernate(二)——别被世俗蒙蔽了双眼:Hibernate中Session之get和load方法的真正区别
- 一口一口吃掉Hibernate(一)——使用SchemaExport生成数据表
- 一口一口吃掉Hibernate(三)——Hibernate给表和字段设置前后缀及分隔符
- 一口一口吃掉Hibernate(四)——多对一单向关联映射
- 一口一口吃掉Hibernate(五)——一对多单向关联映射
- 一口一口吃掉Hibernate(六)——多对多关联映射
- 一口一口吃掉Hibernate(七)——继承映射
- 一口一口吃掉Hibernate(八)——Hibernate中inverse的用法
- 一览Spring全貌
- 包办婚姻的Spring IoC
- 3幅图让你了解Spring AOP
- Spring Aop实例之xml配置
- Spring Aop实例之AspectJ注解配置