问:聊聊Spring IOC机制

一、IOC概述及作用

IOC(Inversion of Control) :即控制反转,是一种设计思想,其核心是将对象的创建、赋值、管理等操作交给代码之外的容器来实现。在传统的编程模式中,对象的创建和依赖关系的管理通常由程序员手动完成,例如使用new关键字创建对象,通过setXxx()方法设置属性值。这种方式存在高耦合、不易维护和扩展的问题。而IOC通过引入一个"第三方"------IOC容器,将对象的控制权交给容器,从而实现了对象之间的解耦,提高了程序的可扩展性和可维护性。

二、Spring IOC机制详解

Spring框架通过IOC机制实现了对象的管理和依赖注入,其核心组件包括Spring容器和配置文件(或注解)。

1. Spring容器

Spring容器是Spring框架的核心,它负责对象的创建、配置、组装和管理。Spring容器提供了两种类型的容器:BeanFactoryApplicationContext。其中,ApplicationContextBeanFactory的子接口,提供了更多的功能,如事件发布、国际化支持等。

Spring容器在启动时会读取配置文件或注解,根据配置创建和管理对象。当需要获取对象时,可以从容器中通过getBean()方法获取。

2. 配置文件

在Spring框架中,通常使用XML文件作为配置文件来定义和管理对象。配置文件中通过<bean>标签来声明对象,包括对象的ID、类名、作用域、初始化方法、销毁方法等属性。

例如:

xml 复制代码
<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">

    <bean id="userDao" class="com.example.dao.UserDaoImpl"/>
    <bean id="userService" class="com.example.service.UserServiceImpl"/>

</beans>

在上面的配置文件中,定义了两个对象userDaouserService,分别对应UserDaoImplUserServiceImpl类。

3. 依赖注入

依赖注入(Dependency Injection,DI)是IOC的一种实现方式,它允许程序员在代码中声明依赖关系,而由容器在运行时动态地将依赖注入到对象中。Spring框架支持三种依赖注入的方式:构造方法注入、set方法注入和接口注入(不常用)。

  • 构造方法注入:通过构造方法传递依赖对象。这种方式要求被注入的类有对应的构造方法。

    例如:

    java 复制代码
    public class UserServiceImpl implements UserService {
        private UserDao userDao;
    
        public UserServiceImpl(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void saveService() {
            userDao.save();
            System.out.println("userService save method running...");
        }
    }

    在配置文件中,需要指定构造方法的参数:

    xml 复制代码
    <bean id="userService" class="com.example.service.UserServiceImpl">
        <constructor-arg ref="userDao"/>
    </bean>
  • set方法注入:通过setter方法传递依赖对象。这种方式要求被注入的类有对应的setter方法。

    例如:

    java 复制代码
    public class UserServiceImpl implements UserService {
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void saveService() {
            userDao.save();
            System.out.println("userService save method running...");
        }
    }

    在配置文件中,通过<property>标签指定setter方法的参数:

    xml 复制代码
    <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框架的依赖。

xml 复制代码
<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

java 复制代码
// 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 {
    @Override
    public Student findById(int id) {
        return new Student(id, "程序员", "北京");
    }
}

3. 编写XML配置文件

src/main/resources目录下创建一个applicationContext.xml文件,并配置StudentDaoStudentService对象。

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">

    <bean id="studentDao" class="com.example.dao.StudentDaoImpl"/>

</beans>

4. 测试从Spring容器获取对象

编写一个测试类来测试从Spring容器中获取对象并调用其方法。

java 复制代码
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 {

    @Test
    public 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实例,并注入依赖关系。这个过程涉及到多个类的协同工作,包括BeanDefinitionReaderBeanDefinitionRegistryBeanFactoryPostProcessorBeanPostProcessor等。

具体来说,Spring容器的工作流程大致如下:

  1. Bean定义的读取和注册 :Spring容器通过BeanDefinitionReader读取配置文件或注解,将Bean定义注册到BeanDefinitionRegistry中。
  2. Bean定义的解析:在Bean定义注册完成后,Spring容器会对Bean定义进行解析,包括解析Bean的依赖关系、初始化方法等。
  3. Bean的创建和依赖注入 :当需要获取Bean时,Spring容器会根据Bean的定义创建Bean实例,并注入依赖关系。这个过程涉及到BeanWrapperPropertyEditor等类的协同工作。
  4. Bean的初始化:在Bean创建完成后,Spring容器会调用Bean的初始化方法(如果有的话),完成Bean的初始化工作。
  5. Bean的销毁:当Spring容器关闭时,会调用Bean的销毁方法(如果有的话),完成Bean的销毁工作。
五、总结

Spring IOC机制是Spring框架的核心之一,它通过引入IOC容器实现了对象的管理和依赖注入,降低了程序的耦合度,提高了程序的可扩展性和可维护性。通过配置文件或注解,程序员可以声明对象及其依赖关系,而由Spring容器在运行时动态地创建和管理对象。Spring IOC机制的底层实现依赖于Spring容器和多个类的协同工作,包括Bean定义的读取和注册、Bean定义的解析、Bean的创建和依赖注入、Bean的初始化和销毁等步骤。
原文链接→

相关推荐
钱多多_qdd6 分钟前
spring cache源码解析(四)——从@EnableCaching开始来阅读源码
java·spring boot·spring
waicsdn_haha8 分钟前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
Q_192849990618 分钟前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端
Code_流苏20 分钟前
VSCode搭建Java开发环境 2024保姆级安装教程(Java环境搭建+VSCode安装+运行测试+背景图设置)
java·ide·vscode·搭建·java开发环境
良许Linux22 分钟前
0.96寸OLED显示屏详解
linux·服务器·后端·互联网
求知若饥34 分钟前
NestJS 项目实战-权限管理系统开发(六)
后端·node.js·nestjs
禁默1 小时前
深入浅出:AWT的基本组件及其应用
java·开发语言·界面编程
Cachel wood1 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Code哈哈笑1 小时前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
gb42152871 小时前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端