深入解析Spring IoC容器

1.引言

1.1 Spring IoC容器的概念

Spring IoC(Inversion of Control)容器是Spring框架的核心功能之一,它负责管理应用程序中的对象(Bean)的生命周期、依赖关系以及配置。
两个核心概念的解释:
控制反转(IoC):IoC是一种软件设计原则,它反转了传统的应用程序控制流程。传统方式中,应用程序负责创建和管理对象,而在IoC中,控制权被转移到容器中,容器负责创建、管理和注入对象,应用程序只需使用这些对象。
容器:容器是IoC的核心,它是一个运行时环境,用于管理应用程序中的Bean。容器负责创建、配置、装配和管理Bean,并提供对它们的依赖注入。

2.IoC容器的背景

2.1回顾传统的应用程序架构和问题

传统的开发模式【以反射举例子】
以下是一个简单的 JdbcUtils 类示例,通过配置文件来完善数据库连接信息。这里使用了 properties 配置文件来存储数据库相关的信息。

首先,创建一个 jdbc.properties 配置文件,包含如下内容(根据你的数据库信息修改):

# 数据库连接配置
jdbc.url=jdbc:mysql://localhost:3306/mydb
jdbc.username=username
jdbc.password=password

接下来,创建 JdbcUtils.java 类,并使用 Java 代码读取配置文件中的信息,并创建数据库连接:

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class JdbcUtils {
    private static String url;
    private static String username;
    private static String password;

    // 静态代码块,在类加载时执行,用于初始化数据库连接信息
    static {
        try {
            // 从配置文件加载数据库连接信息
            Properties properties = new Properties();
            InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
            properties.load(inputStream);

            // 获取配置信息
            url = properties.getProperty("jdbc.url");
            username = properties.getProperty("jdbc.username");
            password = properties.getProperty("jdbc.password");

            // 注册数据库驱动
            Class.forName("com.mysql.jdbc.Driver");
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException("Failed to load JDBC properties or register driver.", e);
        }
    }

    // 获取数据库连接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }

    // 关闭数据库连接
    public static void closeConnection(Connection connection) {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

用图解的方式解释就是这样的:
传统开发模式
传统方式:

  1. 程序员编程程序,在程序中读取配置信息
  2. 创建对象,new Object()/反射方式
  3. 使用对象完成任务

我们可以试着分析传统方式的缺点:

  1. 紧耦合:在传统的应用程序中,各个模块或组件之间通常存在紧耦合。这意味着一个模块的修改可能会影响其他模块,导致系统难以维护和扩展。
  2. 复杂的依赖管理:传统应用程序中,手动管理对象之间的依赖关系通常需要大量的代码。这包括对象的创建、初始化、连接和销毁等操作。
  3. 配置硬编码:配置信息通常被硬编码到应用程序的源代码中,这意味着任何配置更改都需要修改代码并重新编译,缺乏灵活性。
  4. 难以测试:由于紧耦合和复杂的依赖关系,传统的应用程序往往难以进行单元测试和集成测试,从而导致质量问题。
  5. 不易重用:传统的应用程序通常缺乏模块化,难以重用和组合不同的组件。

2.2 引出IOC容器

因此我们将引出本次话题的主角IOC,让我们看看IOC是如何解决以上问题的。
在这里插入图片描述
解读上图

  1. Spring 根据配置文件 xml/注解,创建对象,并放入到容器(ConcurrentHashMap)中,并可以完成对象之间的依赖
  2. 当需要使用某个对象实例的时候,就直接从容器中获取即可
  3. 这样程序员可以更加关注如何使用对象完成相应的业务

Spring IoC容器通过控制反转(IoC)的方式解决了传统应用程序架构的问题,具有以下优势:

  1. 解耦和模块化:Spring IoC容器通过依赖注入将组件之间的依赖关系外部化,降低了耦合度,使得应用程序更容易维护和扩展。模块化的设计使得组件可以被独立开发和测试,然后轻松组合成完整的应用程序。

  2. 灵活的配置:Spring IoC容器允许将配置信息外部化,通常使用XML配置文件、Java注解或Java配置类。这使得配置更加灵活,可以在不修改代码的情况下进行更改。

  3. 依赖注入:Spring IoC容器通过依赖注入自动管理组件之间的依赖关系,无需手动编写繁琐的依赖管理代码。这简化了开发,提高了可维护性。

  4. 测试友好:由于组件之间的松散耦合,Spring应用程序更容易进行单元测试和集成测试,从而提高了代码质量和可靠性。

  5. 重用和扩展性:Spring IoC容器提供了丰富的库和模块,可以轻松地集成各种技术组件,如数据库访问、事务管理、消息队列等,从而增强了应用程序的重用和扩展性。

3. IOC容器使用的快速入门和详细解析

3.1需求说明

  1. 通过 Spring 的方式【配置文件的方式】,获取 JavaBean:Monster 的对象,并给该对象属性赋值,输出该对象的信息。
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/771ad9352c484d6db8f25b52a986f9f8.png

3.2完成步骤

  1. 先下载 Spring 开发包【本示例用的是Spring5.3.8】
  2. 新建 Java 工程
  3. 引入 Spring 开发基本包【1.Bean 2.Core 3.Context 4.Expression 5.commons-logging 写日志】
    在这里插入图片描述
    右键add as library
  4. 创建 Monster.java 类
package com.hspedu.spring.bean;

/**
 * @Author ZhangShuo
 * @Date 2023/10/2 20:30
 * @Version 1.0
 * JavaBean/entity
 */
public class Monster {
    private Integer monsterId;
    private String name;
    private String skill;

    public Monster(Integer monsterId, String name, String skill) {
        this.monsterId = monsterId;
        this.name = name;
        this.skill = skill;
    }
    //无参构造器一定要给,Spring反射创建对象时,需要使用
    public Monster() {
    }

    public Integer getMonsterId() {
        return monsterId;
    }

    public void setMonsterId(Integer monsterId) {
        this.monsterId = monsterId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSkill() {
        return skill;
    }

    public void setSkill(String skill) {
        this.skill = skill;
    }

    @Override
    public String toString() {
        return "Monster{" +
                "monsterId=" + monsterId +
                ", name='" + name + '\'' +
                ", skill='" + skill + '\'' +
                '}';
    }
}

  1. 创建容器配置文件 beans.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">
    <!--
    1.配置Monster对象
    2.在beans中可以配置多个bean
    3.bean代表一个java对象
    4.class属性是用来指定类的全路径【spring底层使用反射创建】
    5.id属性表示该java对象在spring容器中的id【通过id可以获取到该对象】
    6.<property name="monsterId" value="100"/>用于给该对象的属性赋值,没有给就是默认值
    -->
    <bean class="com.hspedu.spring.bean.Monster" id="monster01">
        <property name="monsterId" value="100"/>
        <property name="name" value="牛魔王"/>
        <property name="skill" value="芭蕉扇"/>
    </bean>
</beans>

6.新建测试类spring/src/com/hspedu/spring/test/SpringBeanTest.java

package com.hspedu.spring.test;

import com.hspedu.spring.bean.Monster;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author ZhangShuo
 * @Date 2023/10/2 20:43
 * @Version 1.0
 */
public class SpringBeanTest {
    @Test
    public void getMonster(){
        //解读:
        //1.创建容器 ApplicationContext
        //2.该容器和容器配置文件是关联的
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        //3.通过getBean获取对应的对象
        //默认返回的是Object,但是它的运行类型是Monster
        //Object monster01 = ioc.getBean("monster01");
        Monster monster01 = (Monster) ioc.getBean("monster01");
        //4.输出看效果
        System.out.println("monster01="+monster01+" 运行类型"+monster01.getClass());
        System.out.println("monster01="+monster01+" 获取对应的属性 name="+monster01.getName()+
                "monsterId="+monster01.getMonsterId());
        //5.也可以在获取的时候直接指定Class类型,上面获取过一次,下面也可以再次获取
        Monster monster011 = ioc.getBean("monster01", Monster.class);
        System.out.println("monster011="+monster011);
        System.out.println("monster011.name="+monster011.getName());
        System.out.println("ok~~~");

    }
}

输出:
![在这里插入图片描述](https://img-blog.csdnimg.cn/771ad9352c484d6db8f25b52a986f9f8.png

3.3注意事项和细节

  1. 说明ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");为什么能读取到 beans.xml?
    ClassPathXmlApplicationContext 是按照类加载路径读取的,而类加载路径是spring/out/production/spring,因此读到的是spring/out/production/spring/beans.xml
  2. 解释一下类加载路径,输出一下
    //验证类加载路径
    @Test
    public void classPath(){
        File file = new File(this.getClass().getResource("/").getPath());
        //这个地方就能看到类的加载路径
        System.out.println("file="+file);
    }

输出:file=/Users/zhangshuo/MyJavaProject/hspedu_spring/spring/out/production/spring
在这里插入图片描述
3. debug 看看 Spring 容器结构/机制
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值