基本概念
IoC(Inversion of Control),即,创建对象实例的控制权从程序员的代码控制剥离到IOC容器控制,由xml等文件控制,语义侧重于原理。使用Ioc,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。
IoC还可以以“依赖注入DI(Dependency Injection)”解释,即,组件之间的依赖关系由容器在运行期决定,即,意味着由容器动态地将某种依赖关系注入到组件之中,创建对象实例时,为这个对象注入属性值或其他对象实例,语义侧重于实现。
IoC场景
下面以找恋爱对象为例。
Lily大学毕业后直接去工作,然而一直没有男朋友,看着同学们结婚的结婚,谈恋爱的谈恋爱,就想找个男朋友。 Lily想找到对象可以有3种方案:主动寻找Or 同事介绍 Or 父母安排。如何选择?
主动寻找方式,new一个对象:
public class Girl {
public void love(){
Boy boy = new Boy();
}
同事介绍(男孩工厂):
public class Girl {
void love(){
Boy boy = BoyFactory.createBoy();
}
}
父母安排:
public class Girl {
void love(Boy boy){
boy.love();
}
}
在Spring世界里,跟Lily最后的选择一样,由父母安排恋爱对象,这就是控制反转,而这里具有控制力的父母,即是Spring容器。
IoC注入方式
IoC主要有2种注入方式:接口注入、Setter方法注入、构造器注入。
虽然接口注入模式在很多容器中都已经得到应用,但由于其在灵活性、易用性上不如其他两种注入模式,因而在IOC中已经被废弃。
Spring的5.1.5.RELEASE中的文档上说:
DI exists in two major variants:
Constructor-based dependency injection and Setter-based dependency injection
接口注入其实也是通过Setter注入来实现:
interface BoyInterface{
public void injectHere(Boy bf);
}
class Company implements BoyInterface {
Boy BoyInterface;;
public void injectHere(Boy bf) {
this.BoyInterface = bf;
}
}
Girl类:
package ioc;
public class Girl {
private Boy bf;
public Girl () {
}
public Girl(Boy bf) {
System.out.println("构造方法注入:");
this.bf=bf;
}
public void setBf(Boy bf) {
System.out.println("Setter注入:");
this.bf=bf;
}
public void loveBoy() {
bf.love();
}
}
Boy类:
package ioc;
public class Boy {
public void love() {
System.out.println("Love me!");
}
}
Main方法:
package ioc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext factory=new ClassPathXmlApplicationContext("applicationContext.xml");
Girl lili=(Girl)factory.getBean("girl");
lili.loveBoy();
}
}
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">
<!--default-autowire="byName"-->
<!-->-->
<bean id="boy" class="ioc.Boy" />
<bean id="girl" class="ioc.Girl">
<!--构造器注入-->
<constructor-arg ref="boy"></constructor-arg>
<!--Setter方法注入-->
<!-- property name="bf" ref="boy"></property> -->
</bean>
</beans>
applicationContext.xml配置文件(Setter byName):
<?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="byName"
>
<bean id="boy" class="ioc.Boy" />
<bean id="girl" class="ioc.Girl">
<!--构造器注入-->
<!--<constructor-arg ref="boy"></constructor-arg>-->
<!--Setter方法注入-->
<property name="bf" ref="boy"></property>
</bean>
</beans>
Setter方法注入时,有2种装载方式——byName和byType。当使用byType方式装载时,Spring是根据classType来确定要实例化的类,无需关注bean的id是什么,即使和Girl中bf的Setter方法名不一致,依旧可以实例化。但是使用byName时,则是根据id来实例化类,所以需要把Boy类对应的bean id跟Girl中的Setter方法名一致才行,即修改bean id="boy"为id="bf"或者注入属性值。
applicationContext.xml配置文件(Setter 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="ioc.Boy" />
<bean id="girl" class="ioc.Girl">
<!--构造器注入-->
<!--<constructor-arg ref="boy"></constructor-arg>-->
<!--Setter方法注入-->
<!--<property name="bf" ref="boy"></property>-->
</bean>
</beans>
注入方式比较
Setter 注入:
1、通过 setter 方法设定依赖关系更加直观。
2、如果依赖关系较为复杂,那么构造子注入模式的构造函数也会相当庞大,而此时设值注入模式则更为简洁。
3、如果用到了第三方类库,需要组件提供一个默认的构造函数时,构造子注入模式也不适用。
构造器注入:
1、在构造期间完成一个完整的、合法的对象。
2、所有依赖关系在构造函数中集中呈现。
3、依赖关系在构造时由容器一次性设定,组件被创建之后一直处于相对“不变”的稳定状态。
4、只有组件的创建者关心其内部依赖关系,对调用者而言,该依赖关系处于“黑盒”之中,无需知晓。