1. IoC 的基本概念
IoC(Inversion of Control,控制反转)是一个比较抽象的概念,是Spring框架的核心,用来消减程序的耦合问题。DI(Dependency Injection,依赖注入)是IoC的另外一种说法,只是从不同的角度描述相同的概念。
想吃面包,你可以自己做。也可以选择在面包店下单,告诉店家你的需求,然后等着吃就行。
想买汽车,直接给工厂下单付款即可。
上面的例子包含了控制反转的思想:把制作面包的主动权交给面包店。
当某个Java对象(调用者,例如我),需要调用另一个Java对象(被调用者,即被依赖对象,例如面包)时,以前我们通常会“new 被调用者”来创建对象(例如我们自己做面包)。这种方式会增加调用者与被调用者之间的耦合性,不利于后期代码的升级和维护。
Spring出现后,对象的实例由Spring容器(例如面包店)来创建。Spring容器会负责控制程序之间的关系(例如面包店负责控制我们与面包的关系)。这样控制权就由调用者转移到Spring容器,控制权发生了反转。
依赖注入:Spring容器负责将依赖对象赋值给调用者的成员变量,相当于为调用者注入它所依赖的实例。这就是依赖注入。
综上所述,控制反转是一种通过描述(XML或者注解)并通过第三方去产生或获取特定对象的方式。实现控制反转的是IoC容器,其实现方式是依赖注入。
2.IoC 容器
前面我们知道,实现控制反转的是IoC容器。IoC容器的设计主要是基于BeanFactory 和 ApplicationContext 两个接口。
2.1 BeanFactory 接口
BeanFactory 由org.springframework.beans.factory.BeanFactory接口定义,提供了完整的IoC服务支持,是一个管理Bean的工厂,主要负责初始化各种Bean。
BeanFactory接口有很多实现类,常用的是XmlBeanFactory,根据XML配置文件中的定义来配置Bean。
使用BeanFactory实例加载Spring配置文件实际并不多见,仅作了解。
2.2 ApplicationContext 接口
ApplicationContext 是BeanFactory的子接口,也称应用上下文。除了包含BeanFactory的所有功能以外,还添加了对国际化、资源访问、事件传播等内容的支持。
创建ApplicationContext接口实例通常有以下三种方法:
2.2.1 通过ClassPathXmlApplicationContext
public static void main(String[] args) {
// 初始化Spring容器ApplicationContext,加载指定的XML配置文件
ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过容器获取test实例
TestDao tt = (TestDao)appCon.getBean("test");
tt.sayHello();
}
2.2.2通过FileSystemXmlApplicationContext创建
// 仅需要修改这一行代码
ApplicationContext appCon = new FileSystemXmlApplicationContext("D:\test\applicationContext.xml");
FileSystemXmlApplicationContext 将从指定文件的绝对路径中寻找XML配置文件,但是绝对路径会导致程序的灵活性变差,不推荐使用。通常Spring的Java应用采用ClassPathXmlApplicationContext类来实例化ApplicationContext容器,而Web应用中,将交给Web服务器完成。
2.2.3通过Web服务器实例化ApplicationContext容器
Web服务器实例化ApplicationContext容器时,一般使用基于org.springframework.web.context.ContextLoaderListener的实现方式。
<context-param>
<!-- 加载src目录下的applicationContext.xml文件 -->
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext.xml
</param-value>
</context-param>
<!-- 指定以ContextLoaderListener方式启动Spring容器 -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
3.依赖注入
Spring中实现IoC容器的方法是依赖注入。依赖注入的作用是在使用Spring创建对象时,动态地将其所依赖的对象(例如属性值)注入Bean组件中。
3.1使用属性的setter方法注入
依赖注入通常有两种实现方式:构造方法注入、属性的setter方法注入。(这两种方式都是基于Java的反射机制)。
使用setter方法注入是Spring中最主流的注入方式,利用Java Bean规范所定义的setter方法来完成注入,灵活且可读性高。
下面将两者放在一起进行,方便对比。
3.1.1创建dao包
在dao包创建TestDIDao接口及其实现类TestDIDaoImpl
package dao;
public interface TestDIDao {
public void sayHello();
}
package dao;
public class TestDIDaoImpl implements TestDIDao {
@Override
public void sayHello() {
System.out.println("TestDIDao say: Hello");
}
}
3.1.2创建service包
service包中创建TestDIService接口及其实现类TestDIServiceImpl
package service;
public interface TestDIService {
public void sayHello();
}
package service;
import dao.TestDIDao;
public class TestDIServiceImpl implements TestDIService {
private TestDIDao testDIDao;
// 添加testDIDao属性的setter方法,用于实现依赖注入
public void setTestDIDao(TestDIDao testDIDao) {
this.testDIDao = testDIDao;
}
/* 构造方法,用于实现依赖注入接口对象testDIDao
public TestDIServiceImpl(TestDIDao testDIDao) {
super();
this.testDIDao = testDIDao;
}
*/
@Override
public void sayHello() {
// 调用testDIDao中的sayHelllo方法
testDIDao.sayHello();
System.out.println("TestDIService setter方法注入 say: Hello");
}
}
3.1.3编写配置文件
在src根目录下创建Spring配置文件 applicationContext.xml。将TestDIServiceImpl类托管给Spring,让Spring创建其对象,同时调用其setter方法完成依赖注入。
<!-- 将TestDIDaoImpl类配置给Spring,让Spring创建其实例 -->
<bean id="myTestDIDao" class="dao.TestDIDaoImpl"/>
<!-- 使用setter方法注入 -->
<bean id="testDIService" class="service.TestDIServiceImpl">
<!-- 调用TestDIServiceImpl类的setter方法,将myTestDIDao注入到TestDIServiceImpl类的属性testDIDao上-->
<property name="testDIDao" ref="myTestDIDao"/>
</bean>
/*
<!-- 使用构造方法注入 -->
<bean id="testDIService" class="service.TestDIServiceImpl">
<!-- 将myTestDIDao注入到TestDIServiceImpl类的属性testDIDao上-->
<!-- constructor-arg元素用于定义类构造方法的参数,index定义参数的位置-->
<!-- ref指定某个实例的引用,如果参数是常量值,ref由value代替-->
<constructor-arg index="o" ref="myTestDIDao"/>
</bean>
*/
3.1.4测试
创建test包,并创建TestDI测试类
package test;
public class TestDI {
public static void main(String[] args) {
ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml")
TestDIservice ts = (TestDIService)appCon.getBean("testDIService");
ts.sayHello();
}
}