Spring IoC 的基本概念
管制反转(Inversion of Control,IoC)是一个比拟形象的概念,是 Spring 框架的外围,用来消减计算机程序的耦合问题。依赖注入(Dependency Injection,DI)是 IoC 的另外一种说法,只是从不同的角度,形容雷同的概念。
比方咱们想吃面包了:咱们以前只能是须要本人去买面粉本人做进去吃。
然而当初都有实体店或者网店了,齐全能够把本人想要的口味通知店家,让店家来制作。
此时,咱们本人并没有动手做面包,而是由店家制作,然而这个面包完全符合咱们的口味。
这个例子十分活泼的解说了管制反转的思维,即把制作面包的主动权交给店家。当某个 Java 对象(调用者,比方您)须要调用另一个 Java 对象(被调用者,即被依赖对象,比方面包)时,在传统编程模式下,调用者通java 培训常会采纳“new 被调用者”的代码形式来创建对象(比方您本人制作面包)。这种形式会减少调用者与被调用者之间的耦合性,不利于前期代码的降级与保护。
当 Spring 框架呈现后,对象的实例不再由调用者来创立,而是由 Spring 容器(比方面包店)来创立。Spring 容器会负责控制程序之间的关系(比方面包店负责管制您与面包的关系),而不是由调用者的程序代码间接管制。这样,控制权由调用者转移到 Spring 容器,控制权产生了反转,这就是 Spring 的管制反转。
从 Spring 容器角度来看,Spring 容器负责将被依赖对象赋值给调用者的成员变量,相当于为调用者注入它所依赖的实例,这就是 Spring 的依赖注入。
管制反转是一种通过形容(在 Spring 中能够是 XML 或注解)并通过第三方去产生或获取特定对象的形式。在 Spring 中实现管制反转的是 IoC 容器,其实现办法是依赖注入。
如果大家不太了解,还能够看看这种解释:
IOC(管制反转):全称为:Inverse of Control。从字面上了解就是管制反转了,将对在本身对象中的一个内置对象的管制反转,反转后不再由本人自身的对象进行管制这个内置对象的创立,而是由第三方零碎去管制这个内置对象的创立。
DI(依赖注入):全称为 Dependency Injection,意思本身对象中的内置对象是通过注入的形式进行创立。
那么 IOC 和 DI 这两者又是什么关系呢?
IOC 就是一种软件设计思维,DI 是这种软件设计思维的一个实现。而 Spring 中的外围机制就是 DI。
Spring IoC 容器
Spring IoC 容器的设计次要是基于 BeanFactory 和 ApplicationContext 两个接口。
1. BeanFactory
BeanFactory 提供了残缺的 IOC 服务反对,是一个治理 Bean 的工厂,次要负责初始化各种 Bean。(Bean 相干常识将在下一章讲述)
创立 BeanFactory 实例时,须要提供 XML 文件的绝对路径。例如,能够将第一章 ch1 利用中 main 办法的代码批改如下:
// 初始化 Spring 容器,加载配置文件
BeanFactory beanFac = new XmlBeanFactory(
new FileSystemResource(“D:\eclipse-workspace\ch1\src\applicationContext.xml”)
);
// 通过容器获取 test 实例
TestDao tt = (TestDao)beanFac.getBean(“test”);
tt.sayHello(); 应用 BeanFactory 实例加载 Spring 配置文件在理论开发中不多见,咱们理解以下即可。
2. ApplicationContext
ApplicationContext 是 BeanFactory 的子接口,也称为利用上下文,它除了蕴含 BeanFactory 的所有性能以外,还增加了对国际化、资源化、事件流传等内容的反对。
创立 ApplicationContext 接口实例通常有三种办法:
1.通过 ClassPathXmlApplicationContext 创立
ClassPathXmlApplicationContext 将从类门路 classPath 目录(src 根目录)寻找指定的 XML 配置文件,例如:
ApplicationContext appCon = new ClassPathXmlApplicationContext(“applicationContext.xml”);
2.通过 FileSystemXmlApplicationContext 创立
FileSystemXmlApplicationContext 将从指定文件的绝对路径中寻找 XML 配置文件(不罕用),找到并装载实现 ApplicationContext 的实例化工作。例如:
ApplicationContext appCon =
new FileSystemXmlApplicationContext(“D:\eclipse-workspace\ch1\src\applicationContext.xml”);
3.通过 Web 服务器实例化 ApplicationContext 容器
Web 服务器实例化 ApplicationContext 容器时,个别应用基于 org.springframework.web.context.ContextLoaderListener 的实现形式(须要将 spring-web-5.0.2.RELEASE.jar 复制到 WEB-INF/lib 目录中),此办法只需在 web.xml 中增加如下代码:
<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>
依赖注入的类型
在 Spring 中实现 IoC 容器的办法是依赖注入,依赖注入的作用是在应用 Spring 框架创建对象时,动静地将其所依赖的对象(如属性值)注入 Bean 组件中。Spring 框架的依赖注入通常有两种实现形式:一种是构造方法注入,另一种是属性 setter 办法注入。
1. 构造方法注入
Spring 框架能够采纳 Java 的反射机制,通过构造方法实现依赖注入。Java 反射相干常识文章:浅谈 Java 的反射机制。
在 ch2 利用中,创立 dao 包,并在该包中创立 TestDIDao 接口和接口实现类 TestDIDaoImpl。创立 dao 的目标是在 service 中应用构造方法依赖注入 TestDIDao 接口对象。
package dao;
import org.springframework.stereotype.Service;
@Service
public class TestDIDaoImpl implements TestDIDao{
@Override
public void sayHello() {
System.out.println("TestDIDao say: Hello, Study hard!");
}
}package dao;
public interface TestDIDao {
public void sayHello();
}
在 ch2 利用中,创立 service 包,并在该包中创立 TestDIService 接口和接口实现类 TestDIServiceImpl。在 TestDIServiceImpl 中应用构造方法依赖注入 TestDIDao 接口对象。
package service;
import dao.TestDIDao;
public class TestDIServiceImpl implements TestDIService{
private TestDIDao testDIDao;
// 构造方法,用于实现依赖注入
public TestDIServiceImpl(TestDIDao testDIDao) {
super();
this.testDIDao = testDIDao;
}
@Override
public void sayHello() {
// 调用 testDIDao 中的 sayHello 办法
testDIDao.sayHello();
System.out.println("TestDIService 构造方法 注入 say: Hello, Study hard!");
}
}
在 src 根目录下,创立 Spring 配置文件 applicationContext.xml。在配置文件中,首先,将 dao.TestDIDaoImpl 类托管给 Spring,让 Spring 创立其对象。其次,将 service.TestDIServiceImpl 类托管给 Spring,让 Spring 创立其对象,同时给构造方法传递实参。
<?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.xsd">
<!– 将指定类 TestDIDaoImpl 配置给 Spring,让 Spring 创立其实例 –>
<bean id=”myTestDIDao” class=”dao.TestDIDaoImpl” />
<!– 应用构造方法注入 –>
<bean id=”testDIService” class=”service.TestDIServiceImpl”>
<!-- 将 myTestDIDao 注入到 TestDIServiceImpl 类的属性 testDIDao 上 -->
<constructor-arg index="0" ref="myTestDIDao"/>
</bean>
<!– 应用 setter 办法注入 –>
<bean id=”testDIService1″ class=”service.TestDIServiceImpl1″>
<!-- 调用 TestDIServiceImpl1 类的 setter 办法,将 myTestDIDao 注入到 TestDIServiceImpl1 类的属性 testDIDao 上 -->
<property name="testDIDao" ref="myTestDIDao"/>
</bean>
</beans> 在 ch2 利用中,创立 test 包,并在该包中创立测试类 TestDI,具体代码如下:
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.TestDIService;
public class TestDI {
public static void main(String[] args) {
// 初始化 Spring 容器 ApplicationContext,加载配置文件
ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过容器获取 testDIService 实例,测试构造方法 注入
TestDIService ts = (TestDIService)appCon.getBean("testDIService");
ts.sayHello();
// 通过容器获取 testDIService 实例,测试 setter 办法注入
TestDIService ts1 = (TestDIService)appCon.getBean("testDIService1");
ts1.sayHello();
}
}
2. 属性 setter 办法注入
setter 办法注入是 Spring 框架中最支流的注入形式,它利用 Java Bean 标准所定义的 setter 办法来实现注入,灵便且可读性高。setter 办法注入,Spring 框架也是应用 Java 的反射机制实现的。
在 service 包中,创立接口实现类 TestDIServiceImpl1,在 TestDIServiceImpl1 中应用属性 setter 办法依赖注入 TestDIDao 接口对象。
package service;
import dao.TestDIDao;
public class TestDIServiceImpl1 implements TestDIService{
private TestDIDao testDIDao;
// 增加 testDIDao 属性的 setter 办法,用于实现依赖注入
public void setTestDIDao(TestDIDao testDIDao) {
this.testDIDao = testDIDao;
}
@Override
public void sayHello() {
// 调用 testDIDao 中的 sayHello 办法
testDIDao.sayHello();
System.out.println("TestDIService setter 办法注入 say: Hello, Study hard!");
}
}将 service.TestDIServiceImpl1 类托管给 Spring,让 Spring 创立其对象。同时,调用 TestDIServiceImpl1 类的 setter 办法实现依赖注入。在配置文件增加如下代码:
<!– 应用 setter 办法注入 –>
<bean id=”testDIService1″ class=”service.TestDIServiceImpl1″>
<!– 调用 TestDIServiceImpl1 类的 setter 办法,将 myTestDIDao 注入到 TestDIServiceImpl1 类的属性 testDIDao 上 –>
<property name=”testDIDao” ref=”myTestDIDao”/>
</bean> 在主类中,增加如下代码测试 setter 办法注入:
// 通过容器获取 testDIService 实例,测试 setter 办法注入
TestDIService ts1 = (TestDIService)appCon.getBean("testDIService1");
ts1.sayHello();
最初,测试截图:
总结
一 为什么要应用 Spring:
- 应用 Spring 框架次要是为了简化 Java 开发(大多数框架都是为了简化开发),它帮咱们封装好了很多欠缺的性能,而且 Spring 的生态圈也十分宏大。
- 基于 XML 的配置是 Spring 提供的最原始的依赖注入配置形式,从 Spring 诞生之时就有了,性能也是最欠缺的(然而貌似有更好的配置办法,今天看看!)。
二 为什么要应用依赖注入:
- 传统的代码,每个对象负责管理与本人须要依赖的对象,导致如果须要切换依赖对象的实现类时,须要批改多处中央。同时,适度耦合也使得对象难以进行单元测试。
- 依赖注入把对象的发明交给内部去治理, 很好的解决了代码紧耦合(tight couple)的问题,是一种让代码实现松耦合(loose couple)的机制。
- 松耦合让代码更具灵活性,能更好地应答需要变动,以及不便单元测试。