Spring IoC

Posted 王陸

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring IoC相关的知识,希望对你有一定的参考价值。

一、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();

    }
}

 

以上是关于Spring IoC的主要内容,如果未能解决你的问题,请参考以下文章

Spring之IOC原理及代码详解

Spring IOC源代码具体解释之整体结构

Spring 框架学习——IOC思想原型及实质

Spring IOC源代码具体解释之容器依赖注入

[Spring 源解系列] 重温 IOC 设计理念

Spring IoC