【手撕 Spring】 -- Bean 的创建以及获取

🌈手写简化版 Spring 框架通过构建一个精简版的 Spring 框架,深入理解 Spring 的核心机制,掌握其设计思想,进一步提升编程能力

🌈项目代码地址:https://github.com/YYYUUU42/mini-Spring

如果该项目对你有帮助,可以在 github 上点个 ⭐ 喔 🥰🥰

🌈手撕 Spring 系列可以点开专栏,参看完整的文档

目录

1. 创建简单的 Bean 容器

1.1. Spring Bean 容器是什么

1.2. 实现思路

1.3. 代码

2. Bean 的定义、注册、获取

2.1. 代码实现

2.2. 类之间的关系

2.3. 设计目的

2.4. 测试

3. 类图


1. 创建简单的 Bean 容器

1.1. Spring Bean 容器是什么

Spring 容器用于管理应用中的对象配置和生命周期,类似于一个对象的承载和管理容器。通过配置每个 Bean 的创建方式,开发者可以控制这些对象是以单例模式存在,还是每次请求时创建新的实例,以及它们之间的关联和协作方式。

当对象交给 Spring 容器管理时,它会被封装为一个 Bean。Bean 的定义将对象的属性、依赖关系等信息进行解耦,这使得 Spring 容器可以轻松管理对象,处理像循环依赖这样的复杂场景。

一旦 Bean 被定义并注册到容器中,Spring 会负责整个装配过程,包括初始化、属性注入、依赖解析等步骤。最终,Spring 容器会提供一个完全初始化的 Bean 供应用程序使用。

1.2. 实现思路

为了实现一个简单的 Spring Bean 容器,需要以下三个基本步骤:定义注册获取

  1. 定义BeanDefinition 是 Spring 中常见的一个类,用于定义 Bean 的信息。在初步实现中,我们简化处理,将 Bean 以 Object 类型存储。
  2. 注册:这个过程类似于将数据存放到 HashMap 中,不同的是我们存放的是 Bean 的定义信息。
  3. 获取:通过 Bean 的名称作为 key,在 Spring 容器中直接获取初始化后的 Bean 对象。

1.3. 代码

BeanDefinition 用于定义 Bean 的实例化信息,当前实现将 Bean 以 Object 类型存储

/**
 * @description 用于定义 Bean 实例化信息
 */
public class BeanDefinition {

    private Object bean;

    public BeanDefinition(Object bean) {
        this.bean = bean;
    }

    public Object getBean() {
        return bean;
    }
}

BeanFactory 代表了 Bean 对象的工厂,负责将 Bean 的定义信息注册到 Map 中,并提供获取 Bean 的功能

/**
 * @description 代表了 Bean 对象的工厂,可以存放 Bean 定义到 Map 中以及获取
 */
public class BeanFactory {

    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    public Object getBean(String name) {
        return beanDefinitionMap.get(name).getBean();
    }

    public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(name, beanDefinition);
    }
}

2. Bean 的定义、注册、获取

2.1. 代码实现

BeanFactory 是 Spring 容器的核心接口,它定义了从容器中获取 Bean 的基本方法,如 getBean()。这是所有 Bean 容器的顶层接口,也是 Spring IoC 容器的核心入口。所有 Bean 管理的实现类都基于这个接

public interface BeanFactory {

    Object getBean(String name) throws BeansException;
}

AbstractBeanFactory 实现了 BeanFactory 接口,提供了从容器中获取 Bean 的基础逻辑,包含单例 Bean 的管理和 Bean 创建的模板方法。它主要负责:

  • 单例管理:通过从单例缓存中获取 Bean。
  • 模板方法模式:定义了 createBean()getBeanDefinition() 的抽象方法,由子类实现具体的 Bean 创建和定义获取逻辑。
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {

    /**
     * 从 Map 中获取单例 bean 对象
     * 如果不存在则获取 bean 定义创建 bean 对象
     */
    @Override
    public Object getBean(String name) throws BeansException {
        Object bean = getSingleton(name);
        if (bean != null) {
            return bean;
        }

        BeanDefinition beanDefinition = getBeanDefinition(name);
        return createBean(name, beanDefinition);
    }

    /**
     * 获取 bean 的定义
     */
    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

    /**
     * 创建 bean 对象
     */
    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException;

}

AbstractAutowireCapableBeanFactory 继承了 AbstractBeanFactory,实现了 Bean 的创建逻辑,尤其是自动装配相关的功能。它通过反射机制实例化 Bean,并支持自动注入依赖。这个类扩展了 AbstractBeanFactory,并为自动装配提供了支持。

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

    /**
     * 创建 Bean 实例并将其注册为单例
     */
    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {
        Object bean;
        try {
            // 通过反射创建 Bean 实例
            bean = beanDefinition.getBeanClass().newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        // 将创建的 Bean 注册为单例
        addSingleton(beanName, bean);
        return bean;
    }
}

SingletonBeanRegistry 定义了 Spring 中关于单例管理的规范,允许将单例 Bean 注册到容器中,以及从容器中获取单例 Bean。它是 Spring 中管理单例对象的关键接口。

public interface SingletonBeanRegistry {

	Object getSingleton(String beanName);
}

DefaultSingletonBeanRegistry 实现了 SingletonBeanRegistry 接口,提供了 Spring 容器中单例 Bean 的注册和获取功能。它维护了一个单例缓存,用于存储和管理单例 Bean

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    private final Map<String, Object> singletonObjects = new HashMap<>();

    /**
     * 从 Map 中获取单例对象
     */
    @Override
    public Object getSingleton(String beanName) {
        return singletonObjects.get(beanName);
    }

    /**
     * 将 bean 存入 Map
     */
    protected void addSingleton(String beanName, Object singletonObject) {
        singletonObjects.put(beanName, singletonObject);
    }

BeanDefinitionRegistry 定义了 Spring 容器中管理 BeanDefinition 的基本功能,它允许向容器中注册、移除和获取 Bean 定义。这是 Spring 管理 Bean 元数据的核心接口。

public interface BeanDefinitionRegistry {

    /**
     * 向注册表中注册 BeanDefinition
     */
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);

}

DefaultListableBeanFactory 是 Spring 容器中最核心的实现类之一,集成了 AbstractAutowireCapableBeanFactoryBeanDefinitionRegistryDefaultSingletonBeanRegistry 等多种功能。它既可以注册和管理 Bean 定义,也可以创建和管理单例 Bean。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {

    /**
     * 存储 BeanDefinition 的 Map,用于注册和获取 Bean 定义
     */
    private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    /**
     * 将 BeanDefinition 注册到容器中。
     */
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(beanName, beanDefinition);
    }

    /**
     * 根据名称获取 BeanDefinition,如果找不到则抛出异常。
     */
    @Override
    public BeanDefinition getBeanDefinition(String beanName) throws BeansException {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new BeansException("No bean named '" + beanName + "' is defined");
        }
        return beanDefinition;
    }

}

2.2. 类之间的关系

  1. DefaultSingletonBeanRegistry 实现了 SingletonBeanRegistry 接口,提供单例管理的实现,AbstractBeanFactory 继承了它,获得了单例管理的能力。
  2. AbstractBeanFactory 实现了 BeanFactory 接口,并从 DefaultSingletonBeanRegistry 继承单例管理能力。它提供了模板方法 createBean()getBeanDefinition(),而具体的创建 Bean 和获取 Bean 定义的逻辑由子类实现。
  3. AbstractAutowireCapableBeanFactory 继承自 AbstractBeanFactory,扩展了自动装配和通过反射创建 Bean 的能力,实现了 createBean() 方法。
  4. BeanDefinitionRegistry 是管理 Bean 定义的接口,DefaultListableBeanFactory 实现了该接口,提供了注册和管理 Bean 定义的功能。
  5. DefaultListableBeanFactory 继承了 AbstractAutowireCapableBeanFactory,因此它不仅支持创建 Bean,还可以进行自动装配和单例管理。它同时实现了 BeanDefinitionRegistry,因此可以管理 Bean 定义。

2.3. 设计目的

  1. 职责分离:这些类通过分层次继承和接口实现,将不同的职责(如单例管理、Bean 创建、Bean 定义管理等)进行拆分。每个类负责一项或多项特定的功能,使得代码更具可扩展性和可维护性。例如,DefaultSingletonBeanRegistry 专注于单例管理,而 AbstractAutowireCapableBeanFactory 专注于 Bean 的创建和自动装配。
  2. 高内聚低耦合:每个类只处理它所负责的核心功能,通过接口和继承来组合这些功能,从而减少类之间的耦合。例如,AbstractBeanFactory 负责 Bean 的获取,具体的 Bean 创建逻辑交给子类 AbstractAutowireCapableBeanFactory 去实现。
  3. 模板方法模式AbstractBeanFactory 使用模板方法模式,它定义了获取和创建 Bean 的流程,但具体的实现由子类去完成。这种设计可以让代码结构清晰,并且便于扩展。
  4. 灵活的扩展性DefaultListableBeanFactory 实现了所有这些核心功能,并结合了 Bean 的创建、注册、单例管理等功能。通过继承和接口组合的方式,Spring 可以在不影响整体设计的前提下,灵活地对某些功能进行扩展。

2.4. 测试

public class UserService {

	public void getUserInfo(){
		System.out.println("得到用户信息");
	}
}
@Test
public void test(){
	// 1.初始化 BeanFactory
	DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
	// 2.注册 bean
	BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
	beanFactory.registerBeanDefinition("userService", beanDefinition);
	// 3.第一次获取 bean
	UserService userService = (UserService) beanFactory.getBean("userService");
	userService.getUserInfo();
	// 4.第二次获取 bean from Singleton
	UserService userServiceSingleton = (UserService) beanFactory.getSingleton("userService");
	userServiceSingleton.getUserInfo();
}

调用流程

  1. 实例化 DefaultListableBeanFactory 对象。
  2. UserService 类作为构造器参数,实例化一个 BeanDefinition 对象。
  3. 使用第 1 步创建的工厂对象调用 registerBeanDefinition 方法,将第 2 步的 beanDefinition 对象注册到 BeanDefinitionMap 容器中。
  4. 通过工厂获取 UserService 的 bean 对象。首先尝试从单例对象容器中获取,如果未找到,则从 BeanDefinitionMap 容器中获取 UserService 类的定义并进行实例化(通过 newInstance),然后将实例化后的对象存入单例对象容器中,便于下次调用。
  5. 第二次调用 UserService 的 bean 对象时,工厂直接从单例对象容器中获取,不再重新实例化。

3. 类图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/915861.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Jdbc学习笔记(四)--PreparedStatement对象、sql攻击(安全问题)

目录 &#xff08;一&#xff09;使用PreparedStatement对象的原因&#xff1a; 使用Statement对象编写sql语句会遇到的问题 ​编辑 &#xff08;二&#xff09;sql攻击 1.什么是sql攻击 2.演示sql攻击 &#xff08;三&#xff09;防止SQL攻击 1.PreparedStatement是什么 …

Jmeter中的定时器(二)

5--JSR223 Timmer 功能特点 自定义延迟逻辑&#xff1a;使用脚本语言动态计算请求之间的延迟时间。灵活控制&#xff1a;可以根据测试数据和条件动态调整延迟时间。支持多种脚本语言&#xff1a;支持 Groovy、JavaScript、BeanShell 等多种脚本语言。 支持的脚本语言 Groov…

【Istio】Istio原理

第一章 Istio原理 一、服务网格(servicemesh)1、六个时代2、服务网格定义及优缺点二、Istio1、Istio定义2、Istio安装3、Istio架构1.5版本之前1.5版本之后4、bookinfo案例架构部署5、CRD一、服务网格(servicemesh) 微服务:架构风格,职责单一,api通信 服务网格:微服务时代的…

4.远程访问及控制

SSH 简介&#xff1a; SSH&#xff08;Secure Shell&#xff09;协议是一种安全通道协议&#xff0c;对通信数据进行了加密处理&#xff0c;用于远程管理。 OpenSSH简介 OpenSSH 服务名称&#xff1a;sshd 服务端主程序&#xff1a;/usr/sbin/sshd 服务端配置文件&#xf…

精通rust宏系列教程-入门篇

Rust最令人敬畏和强大的特性之一是它使用和创建宏的能力。不幸的是&#xff0c;用于创建宏的语法可能相当令人生畏&#xff0c;并且对于新开发人员来说&#xff0c;这些示例可能会令人不知所措。我向你保证Rust宏非常容易理解&#xff0c;本文将为你介绍如何创建自己的宏。 什么…

设计模式之装饰器模式(SSO单点登录功能扩展,增加拦截用户访问方法范围场景)

前言&#xff1a; 两个本想描述一样的意思的词&#xff0c;只因一字只差就让人觉得一个是好牛&#xff0c;一个好搞笑。往往我们去开发编程写代码时也经常将一些不恰当的用法用于业务需求实现中&#xff0c;但却不能意识到。一方面是由于编码不多缺少较大型项目的实践&#xff…

kubernetes简单入门实战

本章将介绍如何在kubernetes集群中部署一个nginx服务&#xff0c;并且能够对其访问 Namespace Namespace是k8s系统中一个非常重要的资源&#xff0c;它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。 默认情况下&#xff0c;k8s集群中的所有的Pod都是可以相…

webpack5 + vue3 从零配置项目

前言 虽然在实际项目当中很少会从 0 到 1 配置一个项目&#xff0c;毕竟很多重复工作是没有必要的&#xff0c;脚手架将这些重复性的工作进行了整合&#xff0c;方便开发者使用。也正因如此&#xff0c;导致部分开发者过于依赖脚手架&#xff0c;却不清楚其内部的实现流程&…

Linux git-bash配置

参考资料 命令提示符Windows下的Git Bash配置&#xff0c;提升你的终端操作体验WindowsTerminal添加git-bash 目录 一. git-bash配置1.1 解决中文乱码1.2 修改命令提示符 二. WindowsTerminal配置git-bash2.1 添加git-bash到WindowsTerminal2.2 解决删除时窗口闪烁问题 三. VS…

(RK3566驱动开发 - 1).pinctrl和gpio子系统

一.设备树 pinctrl部分可以参考 rockchip 官方的绑定文档 &#xff1a;kernel/Documentation/devicetree/bindings/pinctrl PIN_BANK&#xff1a;引脚所属的组 - 本次例程使用的是 GPIO3_A1 这个引脚&#xff0c;所以所属的组为 3&#xff1b; PIN_BANK_IDX&#xff1a;引脚的…

基于OpenFOAM和深度学习驱动的流体力学计算与应用

在深度学习与流体力学深度融合的背景下&#xff0c;科研边界不断拓展&#xff0c;创新成果层出不穷。从物理模型融合到复杂流动模拟&#xff0c;从数据驱动研究到流场智能分析&#xff0c;深度学习正以前所未有的力量重塑流体力学领域。近期在Nature和Science杂志上发表的深度学…

uniapp设置tabBar高斯模糊并设置tabBar高度占位

1、设置tabBar高斯模糊 2、设置tabBar高度占位 &#xff08;1&#xff09;需要先在App.vue中获取一下 uni.getSystemInfoSync().windowBottom; //返回值是tabBar的高度&#xff08;2&#xff09;在全局样式文件/uview-ui/libs/css/style.vue.scss中定义一个全局样式 3、在需…

嵌入式Linux输入系统应用编程学习总结

嵌入式Linux输入系统应用编程学习总结 目录 嵌入式Linux输入系统应用编程学习总结1. 嵌入式Linux输入系统介绍2. Linux设备输入数据的几个结构体2.1 内核中表示一个输入设备的结构体2.2 APP从输入设备获取的数据类型结构体 3. 查看LCD设备信息和输入数据3.1 查看设备信息3.2 使…

力扣=Mysql-3322- 英超积分榜排名 III(中等)

一、题目来源 3322. 英超积分榜排名 III - 力扣&#xff08;LeetCode&#xff09; 二、数据表结构 表&#xff1a;SeasonStats --------------------------- | Column Name | Type | --------------------------- | season_id | int | | team_id …

HTML之列表学习记录

练习题&#xff1a; 图所示为一个问卷调查网页&#xff0c;请制作出来。要求&#xff1a;大标题用h1标签&#xff1b;小题目用h3标签&#xff1b;前两个问题使用有序列表&#xff1b;最后一个问题使用无序列表。 代码&#xff1a; <!DOCTYPE html> <html> <he…

【ElasticSearch】定位分片不分配

记录遇到的es集群分片不分配的情况--待补全 定位分片不分配的原因 定位分片不分配的原因 在shell客户端执行如下的语句&#xff1a; curl -X GET "http://192.168.0.209:9200/_cat/shards?v&hindex,shard,prirep,state,unassigned.reason"集群中各分片的状态都…

10款PDF合并工具讲解与推荐!!!

在现在的大环境下&#xff0c;PDF文件因其跨平台、格式固定等优势&#xff0c;成为了我们工作和学习中不可或缺的一部分。是最常用的文档格式之一。然而&#xff0c;面对多个PDF文件需要合并成一个的场景&#xff0c;如何选择一款高效、易用的PDF合并工具就显得尤为重要。今天&…

「QT」窗口类 之 QWidget 窗口基类

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「QT」QT5程序设计&#x1f4da;全部专栏「Win」Windows程序设计「IDE」集成开发环境「UG/NX」BlockUI集合「C/C」C/C程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「UG/NX」NX定制…

【机器学习】如何配置anaconda环境(无脑版)

马上就要上机器学习的实验&#xff0c;这里想写一下我配置机器学习的anaconda环境的二三事 一、首先&#xff0c;下载安装包&#xff1a; Download Now | Anaconda 二、打开安装包&#xff0c;一直点NEXT进行安装 这里要记住你要下载安装的路径在哪&#xff0c;后续配置环境…

【3D Slicer】的小白入门使用指南四

开源解剖影像浏览工具Open Anatomy Browser使用及介绍 和3D slicer米有太大关系,该工具是网页版影像数据的浏览工具(可以简单理解为网页版的3D slicer) 介绍 ● 开放解剖(OA)浏览器是由神经影像分析中心开发的,基于网络浏览器技术构建的图谱查看器。 ● OA浏览器将解剖模…