2. Spring IoC
2.1 Spring IoC的基本概念
控制反转(Inversion of Control,IoC)是Spring框架的核心,用来消减计算机程序的偶合问题。
依赖注入(Dependency Injection,DI)是IoC的另外一种说法,只是从不同的角度描述相同的概念。
当Spring框架出现后,对象的实例不再由调用者来创建,而是由Spring容器来创建。Spring容器会负责控制程序之间的关系,而不是由调用者的程序代码直接控制。这样,控制权由调用者转移到Spring容器,控制权发生了反转,这就是Spring的控制反转。
从Spring容器角度来看,Spring容器负责将被依赖对象赋值给调用者的成员变量,相当于为调用者注入它所依赖的实例,这就是Spring的依赖注入。
综上所述,控制反转是一种通过描述(在Spring中可以是XML或注解)并通过第三方去产生或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入。
2.2 Spring IoC容器
2.2.1 BeanFactory
BeanFactory由org.springframework.beans.factory.BeanFactory接口定义,他提供了完整的IoC服务支持,是一个管理Bean的工厂,主要负责初始化各种Bean。BeanFactory接口有多个实现类,其中比较常用的是org.springframework.beans.factory.xml.XmlBeanFactory。
在创建BeanFactory实例时需要提供XML文件的绝对路径。下面是main方法中的一个示例代码:
public static void main(String[] args) {
//初始化Spring容器,加载配置文件
BeanFactory beanFac = new XmlBeanFactory(
new FileSystemResource("D:eclipse-workspacech1src
applicationContext.xml")
);
//通过容器获取test实例
TestDao tt = (TestDao)beanFac.getBean("test");
tt.sayHello();
}
2.2.2 ApplicationContext
ApplicationContext是BeanFactory的子接口,也称为应用上下文,由org.springframework.context.ApplicationContext接口定义。ApplicationContext接口除了包含BeanFactory的所有功能以外,还添加了对国际化、资源访问、事件传播等内容的支持。
创建ApplicationContext接口实例通常有一下3种方法:
-
通过ClassPathXmlApplicationContext创建
ClassPathXmlApplicationContext将从类路径目录(src根目录)中寻找指定的XML配置文件。代码如下:
public static void mian(String[] args) { //初始化Spring容器ApplicationContext,加载配置文件 ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml"); //通过容器获取test实例 TestDao tt = new (TestDao)appCon.getBean("test"); tt.sayHello(); }
-
通过FileSystemXmlApplicationContext创建
FileSystemXmlApplicationContext将从指定文件的绝对路径中寻找XML配置文件,找到并装载完成ApplicationContext的实例化工作。代码如下:
public static void mian(String[] args) { //初始化Spring容器ApplicationContext,加载配置文件 ApplicationContext appCon = new FileSystemXmlApplicationContext("D:eclipse-workspacech1srcapplicationContext.xml"); //通过容器获取test实例 TestDao tt = new (TestDao)appCon.getBean("test"); tt.sayHello(); }
采用绝对路径的加载方式将导致程序的灵活性变差,一般不推荐使用。因此块,通常在Spring的Java应用中采取通过ClassPathXmlApplicationContext类来实例化ApplicationContext容器的方式,而在Web应用中,ApplicationContext容器的实例化工作将交给Web服务器完成。
-
通过Web服务器实例化ApplicationContext容器
在Web服务器实例化ApplicationContext容器时,一般使用基于org.framework.web.context.ContextLoaderListener的实现方式(需要将spring-web-x.x.x.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.framework.web.context.ContextLoaderListener </listener-class> </listener>
2.3 依赖注入的类型
在Spring中实现IoC容器的方法是依赖注入,依赖注入的作用是在使用Spring框架创建对象时动态地将其所依赖的对象(例如属性值)注入Bean组件中。Spring框架的依赖注入通常有两种实现方式,一种是使用构造方法注入,另一种是使用属性的setter方法注入。
2.3.1 使用构造方法注入
Spring框架可以采用Java的反射机制,通过构造方法完成依赖注入。
实例讲解:
-
创建dao包
创建dao包在service中使用构造方法依赖注入TestDIDao接口对象。
TestDIDao接口代码如下:
package dao; public interface TestDIDao { public void sayHello(); }
TestDIDaoImpl实现类代码如下:
package dao; public class TestDIDaoImpl implements TestDIDao { @Override public void sayHello() { system.out.println("TestDIDao say: Hello, Study hard!"); } }
-
创建service包
在TestDIServiceImpl中使用构造方法依赖注入TestDIDao接口对象。
TestDIService接口代码如下:
package service; public interface TestDIService { public void sayHello(); }
TestDIServiceImpl实现类代码如下:
package service; import dao.TestDIDao; public class TestDIServiceImlp implements TestDIService { private TestDIDao 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"> <!-- 模拟spring执行过程 创建service实例:BookService bookService = new BookServiceImpl() IoC <bean> 创建dao实例:BookDao bookDao = new BookDaoImpl() IoC 将dao设置给service:bookService.setBookDao(bookDao); DI <property> <property> 用于进行属性注入 name: bean的属性名,通过setter方法获得 setBookDao ##> BookDao ##> bookDao ref :另一个bean的id值的引用 --> <!-- 将指定类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> </beans>
-
创建test包
创建测试类TestDI,代码如下:
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import service.TestDIService; public classs TestDI { public static void main(String[] args) { //初始化Spring容器ApplicationContext,加载配置文件 ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml"); //通过容器获取testDIService实例,测试构造方法注入 TestDIService ts = (TestDIService)appCon.getBean("testDIService"); ts.sayHello(); } }
-
运行效果:
2.3.2 使用属性的setter方法注入
使用setter方法注入是Spring框架中最主流的注入方式,它利用Java Bean规范所定义的setter方法来完成注入,灵活且可读性高。对于setter方法注入,Spring框架也是使用Java的反射机制实现的。
实例讲解:
-
创建接口实现类TestDIServiceImpl
在service包中创建接口实现类TestDIServiceImpl1,在TestDIServiceImpl1中使用属性的setter方法依赖注入TestDIDao接口对象,代码如下:
package service; import dao.TestDIDao; public class TestDIServiceImpl 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!"); } }
-
将TestDIServiceImpl1类托管给Spring
让Spring创建其对象,同时调用TestDISeriveImpl1类的setter方法完成依赖注入。代码如下:
<!-- 使用setter方法注入 --> <bean id="testDIService1" class="service.TestDIServiceImpl1"> <!-- 调用TestDIServiceImpl1类的setter方法,将myTestDIDao注入到TestDIServiceImpl1类的属性testDIDao上 --> <property name="testDIDao" ref="myTestDIDao" /> </bean>
-
在test中测试setter方法注入
//通过容器获取testDIService实例,测试setter方法注入 TestDIService ts1=(TestDIService)appCon.getBean("testDIService1"); ts1.sayHello();
-
运行效果: