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)的机制。
- 松耦合让代码更具灵活性,能更好地应答需要变动,以及不便单元测试。