一、Spring IoC的基本概念
控制反转(Inversion of Control,IoC)是一个比较抽象的概念,是Spring框架的核心,用来消减计算机程序的耦合问题。依赖注入(Dependency Injection,DI)是IoC的另外一种说法,只是从不同的角度,描述相同的概念。
IOC(Inverse of Control)反转控制的概念,就是将原本在程序中手动创建对象的控制权,交由Spring框架管理。当某个Java对象(调用者)需要调用另一个Java对象(被调用者)时,在传统编程模式下,调用者通常会采用“new 被调用者”的代码方式来创建对象。这种方式会增加调用者与被调用者之间的耦合性,不利于后期代码的升级与维护。
当Spring框架出现后,对象的实例不再由调用者来创建,而是由Spring容器来创建。Spring容器会负责控制程序之间的关系,而不是由调用者的程序代码直接控制。这样,控制权由调用者转移到Spring容器,控制权发生了反转,这就是Spring的控制反转。
从Spring容器角度来看,Spring容器负责将被依赖对象赋值给调用者的成员变量,相当于为调用者注入它所依赖的实例,这就是Spring的依赖注入。
控制反转是一种通过描述(在Spring中可以是XML或注解)并通过第三方去产生或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入。
这些书面语还是有点抽象,看到有大佬写了一篇生活化的解释,https://blog.csdn.net/u013030441/article/details/53896091
二、Spring IOC容器
实现控制反转的是Spring IOC容器,Spring IOC容器的设计主要是基于Bean Factory和ApplicationContext两个接口。
2.1、BeanFactory接口
BeanFactory由org.springframework.beans.factory.BeanFactory接口定义,它提供了完整的IOC服务支持,是一个管理Bean的工厂,主要负责初始化各种Bean。
BeanFactory接口最常用的实现类是org.springframework.beans.factory.xml.XMLBeanFactory,该类会根据XML配置文件中的定义来装配Bean。
BeanFactory实例创建代码如下:
@Test public void demo(){ FileSystemResource file = new FileSystemResource("C:\\demo\\applicationContext.xml"); BeanFactory beanFactory = new XmlBeanFactory(file); TestDao testDao = (TestDao) beanFactory.getBean("testDao"); testDao.sayHello(); }
由于使用BeanFactory实例加载Spring配置文件在实际开发中并不多见,所以对于该接口仅了解即可。
2.2、ApplicationContext接口
ApplicationContext是BeanFactory的子接口,也称为应用上下文,由org.springframework.context.ApplicationContext接口定义。ApplicationContext接口除了包含BeanFactory的所有功能以外,还添加了对国际化、资源访问、事件传播等内容的支持。
创建ApplicationContext接口实例通常有以下3中方法:
通过ClassPathXmlApplicationContext创建
通过FileSystemXmlApplicationContext创建
通过Web服务器实例化ApplicationContext容器
下面对这3种方法的代码做演示:
(1)ClassPathXmlApplicationContext
使用ClassPathXmlApplicationContext将从类路径目录(src根目录)中寻找指定的XML配置文件,代码示例:
@Test public void demo(){ //初始化Spring容器ApplicationContext,加载配置文件 ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml"); //通过容器获取testDao实例 TestDao testDao = (TestDao) application.getBean("testDao"); testDao.sayHello(); }
(2)FileSystemXmlApplicationContext
使用FileSystemXmlApplicationContext将从指定文件的绝对路径中寻找XML配置文件,找到并装载完成ApplicationContext的实例化工作,代码示例:
@Test public void demo(){ ApplicationContext application = new FileSystemXmlApplicationContext("C:\\demo\\applicationContext.xml"); TestDao testDao = (TestDao) application.getBean("testDao"); testDao.sayHello(); }
(3)使用Web服务器实例化
通过Web服务器实例化ApplicationContext容器,一般使用org.springframework.web.context.ContextLoaderListener的实现方式,需要添加Spring-web依赖,pom.xml配置如下:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.0.2.RELEASE</version> </dependency>
在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>
通过web.xml配置,web容器会自动加载context-param中的配置文件初始化ApplicationContext实例,然后就可以在web应用中通过WebApplicationContextUtils.getWebApplicationContext方法获取ApplicationContext的引用,Servlet中的代码如下:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws javax.servlet.ServletException, IOException { ApplicationContext application = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext()); TestDao testDao = (TestDao) application.getBean("testDao"); testDao.sayHello(); }
三、依赖注入(基于XML方式)
在Spring中实现IoC容器的方法是依赖注入,依赖注入的作用是在使用Spring框架创建对象时,动态地将其所依赖的对象(如属性值)注入Bean组件中。Spring框架的依赖注入通常有两种实现方式:一种是构造方法注入,另一种是属性setter方法注入。
构造方法注入
Spring框架可以采用Java的反射机制,通过构造方法完成依赖注入。
创建dao
在ch2应用中,创建dao包,并在该包中创建TestDIDao接口和接口实现类TestDIDaoImpl。创建dao的目的是在service中使用构造方法依赖注入TestDIDao接口对象。
package chap10.dao; public interface TestDao { public void sayHello(); }
package chap10.dao; public class TestDaoImpl implements TestDao{ @Override public void sayHello() { System.out.println("Hello, Study hard!"); } }
创建service
在ch2应用中,创建service包,并在该包中创建TestDIService接口和接口实现类TestDIServiceImpl。在TestDIServiceImpl中使用构造方法依赖注入TestDIDao接口对象。
package chap10.service; public interface TestService { void sayHello(); }
package chap10.service; import chap10.dao.TestDao; public class TestServiceImpl implements TestService { private TestDao testDao; public TestServiceImpl(TestDao testDao) { this.testDao = testDao; } public TestServiceImpl() { } public TestDao getTestDao() { return testDao; } public void setTestDao(TestDao testDao) { this.testDao = testDao; } @Override public void sayHello() { testDao.sayHello(); } }
编写配置文件
在src根目录下,创建Spring配置文件applicationContext.xml。在配置文件中,首先,将dao.TestDIDaoImpl类托管给Spring,让Spring创建其对象。其次,将service.TestDIServiceImpl类托管给Spring,让Spring创建其对象,同时给构造方法传递实参。
<bean id="test" class="spring01.dao.TestDaoImpl"> </bean> <bean id="testServ" class="spring01.service.TestServImpl"> <constructor-arg ref="test"></constructor-arg> </bean>
创建test
在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(); } }
属性setter方法注入
setter方法注入是Spring框架中最主流的注入方式,它利用Java Bean规范所定义的setter方法来完成注入,灵活且可读性高。setter方法注入,Spring框架也是使用Java的反射机制实现的。
创建接口实现类TestDIServiceImpl1
在service包中,创建接口实现类TestDIServiceImpl1,在TestDIServiceImpl1中使用属性setter方法依赖注入TestDIDao接口对象
TestDIServiceImpl1.java
package service; import dao.TestDIDao; public class TestDIServiceImpl1 implements TestDIService{ private TestDIDao testDIDao; public void setTestDIDao(TestDIDao testDIDao) { this.testDIDao = testDIDao; } public void sayHello() { testDIDao.sayHello(); System.out.println("hello in service2"); } }
TestDIServiceImpl1类托管Spring
将service.TestDIServiceImpl1类托管给Spring,让Spring创建其对象。同时,调用TestDIServiceImpl1类的setter方法完成依赖注入。在配置文件添加如下代码:<!-- 使用setter方法注入 --> <bean id="testDIService1" class="service.TestDIServiceImpl1"> <property name="testDIDao" ref="myTestDIDao" /> </bean>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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 将指定类TestDaoImpl配置给Spring,让Spring创建其实例 --> <bean id="myTestDIDao" class="dao.TestDIDaoImpl" /> <bean id="testDIService" class="service.TestDIServiceImpl"> <constructor-arg index="0" ref="myTestDIDao" /> </bean> <!-- 使用setter方法注入 --> <bean id="testDIService1" class="service.TestDIServiceImpl1"> <property name="testDIDao" ref="myTestDIDao" /> </bean> </beans>
在test中测试setter方法注入
在Test.java主类中,添加如下代码,测试setter方法注入:
TestDIService ts1 = (TestDIService)appCon.getBean("testDIService1");// test为配置文件中的id ts1.sayHello();
Test.java
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import dao.TestDIDao; import service.TestDIService; public class Test { public static void main(String[] args) { // 初始化Spring容器ApplicationContext,加载配置文件 ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml"); TestDIService ts = (TestDIService)appCon.getBean("testDIService");// test为配置文件中的id ts.sayHello(); TestDIService ts1 = (TestDIService)appCon.getBean("testDIService1");// test为配置文件中的id ts1.sayHello(); } }