问:聊聊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的初始化和销毁等步骤。
原文链接→

相关推荐
Asthenia04121 小时前
Spring扩展点与工具类获取容器Bean-基于ApplicationContextAware实现非IOC容器中调用IOC的Bean
后端
bobz9651 小时前
ovs patch port 对比 veth pair
后端
Asthenia04121 小时前
Java受检异常与非受检异常分析
后端
uhakadotcom1 小时前
快速开始使用 n8n
后端·面试·github
JavaGuide2 小时前
公司来的新人用字符串存储日期,被组长怒怼了...
后端·mysql
bobz9652 小时前
qemu 网络使用基础
后端
Asthenia04122 小时前
面试攻略:如何应对 Spring 启动流程的层层追问
后端
Asthenia04122 小时前
Spring 启动流程:比喻表达
后端
Asthenia04123 小时前
Spring 启动流程分析-含时序图
后端
ONE_Gua3 小时前
chromium魔改——CDP(Chrome DevTools Protocol)检测01
前端·后端·爬虫