问:聊聊Spring IOC机制
一、IOC概述及作用
IOC(Inversion of Control):即控制反转,是一种设计思想,其核心是将对象的创建、赋值、管理等操作交给代码之外的容器来实现。在传统的编程模式中,对象的创建和依赖关系的管理通常由程序员手动完成,例如使用new
关键字创建对象,通过setXxx()
方法设置属性值。这种方式存在高耦合、不易维护和扩展的问题。而IOC通过引入一个“第三方”——IOC容器,将对象的控制权交给容器,从而实现了对象之间的解耦,提高了程序的可扩展性和可维护性。
二、Spring IOC机制详解
Spring框架通过IOC机制实现了对象的管理和依赖注入,其核心组件包括Spring容器和配置文件(或注解)。
1. Spring容器
Spring容器是Spring框架的核心,它负责对象的创建、配置、组装和管理。Spring容器提供了两种类型的容器:BeanFactory
和ApplicationContext
。其中,ApplicationContext
是BeanFactory
的子接口,提供了更多的功能,如事件发布、国际化支持等。
Spring容器在启动时会读取配置文件或注解,根据配置创建和管理对象。当需要获取对象时,可以从容器中通过getBean()
方法获取。
2. 配置文件
在Spring框架中,通常使用XML文件作为配置文件来定义和管理对象。配置文件中通过<bean>
标签来声明对象,包括对象的ID、类名、作用域、初始化方法、销毁方法等属性。
例如:
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userDao" class="com.example.dao.UserDaoImpl"/><bean id="userService" class="com.example.service.UserServiceImpl"/></beans>
在上面的配置文件中,定义了两个对象userDao
和userService
,分别对应UserDaoImpl
和UserServiceImpl
类。
3. 依赖注入
依赖注入(Dependency Injection,DI)是IOC的一种实现方式,它允许程序员在代码中声明依赖关系,而由容器在运行时动态地将依赖注入到对象中。Spring框架支持三种依赖注入的方式:构造方法注入、set方法注入和接口注入(不常用)。
-
构造方法注入:通过构造方法传递依赖对象。这种方式要求被注入的类有对应的构造方法。
例如:
public class UserServiceImpl implements UserService {private UserDao userDao;public UserServiceImpl(UserDao userDao) {this.userDao = userDao;}@Overridepublic void saveService() {userDao.save();System.out.println("userService save method running...");} }
在配置文件中,需要指定构造方法的参数:
<bean id="userService" class="com.example.service.UserServiceImpl"><constructor-arg ref="userDao"/> </bean>
-
set方法注入:通过setter方法传递依赖对象。这种方式要求被注入的类有对应的setter方法。
例如:
public class UserServiceImpl implements UserService {private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}@Overridepublic void saveService() {userDao.save();System.out.println("userService save method running...");} }
在配置文件中,通过
<property>
标签指定setter方法的参数:<bean id="userService" class="com.example.service.UserServiceImpl"><property name="userDao" ref="userDao"/> </bean>
-
接口注入:不常用,且Spring框架不推荐使用。
三、Spring IOC使用
下面通过一个示例来说明Spring IOC的使用。
1. 创建Maven工程并引入依赖
首先,创建一个Maven工程,并在pom.xml
文件中引入Spring框架的依赖。
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.13</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency>
</dependencies>
2. 创建实体类、Dao接口及实现类
创建一个Student
实体类,一个StudentDao
接口及其实现类StudentDaoImpl
。
// Student实体类
package com.example.pojo;public class Student {private int id;private String name;private String address;// 构造方法、getter和setter方法省略
}// StudentDao接口
package com.example.dao;import com.example.pojo.Student;public interface StudentDao {Student findById(int id);
}// StudentDao实现类
package com.example.dao;import com.example.pojo.Student;public class StudentDaoImpl implements StudentDao {@Overridepublic Student findById(int id) {return new Student(id, "程序员", "北京");}
}
3. 编写XML配置文件
在src/main/resources
目录下创建一个applicationContext.xml
文件,并配置StudentDao
和StudentService
对象。
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="studentDao" class="com.example.dao.StudentDaoImpl"/></beans>
4. 测试从Spring容器获取对象
编写一个测试类来测试从Spring容器中获取对象并调用其方法。
package com.example;import com.example.dao.StudentDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class SpringTest {@Testpublic void testIOC() {// 1. 创建Spring容器对象ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");// 2. 从容器中获取StudentDao对象StudentDao studentDao = (StudentDao) applicationContext.getBean("studentDao");// 3. 调用StudentDao对象的方法Student student = studentDao.findById(1);System.out.println(student);}
}
5. 运行测试
运行测试类SpringTest
中的testIOC
方法,观察输出结果。如果一切正常,将会看到从StudentDaoImpl
中返回的Student
对象的信息。
四、Spring IOC机制解析
Spring IOC机制的底层实现依赖于Spring容器,而Spring容器本质上是一个对象工厂。在Spring框架中,BeanFactory
接口是Spring容器的核心接口,它提供了获取Bean的基本功能。ApplicationContext
接口是BeanFactory
接口的子接口,提供了更多的功能,如事件发布、国际化支持等。
在Spring容器启动时,会读取配置文件或注解,解析并注册Bean定义。当需要获取Bean时,Spring容器会根据Bean的定义创建Bean实例,并注入依赖关系。这个过程涉及到多个类的协同工作,包括BeanDefinitionReader
、BeanDefinitionRegistry
、BeanFactoryPostProcessor
、BeanPostProcessor
等。
具体来说,Spring容器的工作流程大致如下:
- Bean定义的读取和注册:Spring容器通过
BeanDefinitionReader
读取配置文件或注解,将Bean定义注册到BeanDefinitionRegistry
中。 - Bean定义的解析:在Bean定义注册完成后,Spring容器会对Bean定义进行解析,包括解析Bean的依赖关系、初始化方法等。
- Bean的创建和依赖注入:当需要获取Bean时,Spring容器会根据Bean的定义创建Bean实例,并注入依赖关系。这个过程涉及到
BeanWrapper
、PropertyEditor
等类的协同工作。 - Bean的初始化:在Bean创建完成后,Spring容器会调用Bean的初始化方法(如果有的话),完成Bean的初始化工作。
- Bean的销毁:当Spring容器关闭时,会调用Bean的销毁方法(如果有的话),完成Bean的销毁工作。
五、总结
Spring IOC机制是Spring框架的核心之一,它通过引入IOC容器实现了对象的管理和依赖注入,降低了程序的耦合度,提高了程序的可扩展性和可维护性。通过配置文件或注解,程序员可以声明对象及其依赖关系,而由Spring容器在运行时动态地创建和管理对象。Spring IOC机制的底层实现依赖于Spring容器和多个类的协同工作,包括Bean定义的读取和注册、Bean定义的解析、Bean的创建和依赖注入、Bean的初始化和销毁等步骤。
原文链接→