手写Spring:第3章-实现Bean的定义、注册、获取

news2024/11/26 14:40:24

文章目录

  • 一、目标:实现Bean的定义、注册、获取
  • 二、设计:实现Bean的定义、注册、获取
  • 三、实现:实现Bean的定义、注册、获取
    • 3.1 工程结构
    • 3.2 实现Bean的定义、注册、获取类图
    • 3.3 定义Bean异常
    • 3.4 BeanDefinition定义和注册
      • 3.4.1 BeanDefinition定义
      • 3.4.2 BennDefinition定义注册接口
    • 3.5 单例注册接口定义和实现
      • 3.5.1 单例注册接口
      • 3.5.2 单例注册接口的实现
    • 3.6 抽象类定义模板方法
      • 3.6.1 Bean工厂接口
      • 3.6.2 抽象Bean工厂基类,定义模板方法
      • 3.6.3 实例化Bean类
      • 3.6.4 核心类:默认的Bean工厂实现
  • 四、测试:实现Bean的定义、注册、获取
  • 五、总结:实现Bean的定义、注册、获取

一、目标:实现Bean的定义、注册、获取

💡 实现 Bean 容器关于 Bean 对象的注册和获取?

  • Bean 的创建交给容器,而不是我们在调用时候传递一个实例化好的 Bean 对象。
  • 另外还需要考虑单例对象,在对象的二次获取时是可以从内存中获取对象的。
  • 此外不仅要实现功能还需要完善基础容器框架的类结构体,否则将来就很难扩容进去其他的功能。

二、设计:实现Bean的定义、注册、获取

💡 实现Bean的定义、注册、获取?

  • 首先是在 Bean 注册的时候只注册一个类信息,而不会直接把实例化信息注册到 Spring 容器中。
  • 那么就需要修改 BeanDefinition 中的属性 ObjectClass,接下来就是在获取 Bean 对象时需要处理 Bean 对象的实例化操作以及判断当前单例对象在容器中是否已经缓存起来。

在这里插入图片描述

  • 首先需要定义 BeanFactory 这样一个 Bean 工厂,提供 Bean 的获取方法 getBean(String name),之后这个 Bean 工厂接口由抽象类 AbstractBeanFactory 实现。
    • 这样用 模板模式 的设计方式,可以统一设计通用核心方法的调用逻辑和标准定义,也就很好的控制了后续的实现者不用关心调用逻辑。
    • 按照统一方式执行。那么类型的继承者只需要关心具体方法的逻辑实现即可。
  • 在继承抽象类 AbstractBeanFactory 后的 AbstractAutowireCapableBeanFactory 就可以实现相应的抽象方法了。
    • 因为 AbstractAutowireCapableBeanFactory 本身也是一个抽象类,所以它只会实现属于自己的抽象方法,其他抽象方法由继承 AbstractAutowireCapableBeanFactory 的类实现。
    • 这里就体现了类实现过程中的各司其职,你只需要关心属于你的内容,不是你的内容,不要参与。
  • 另外还有块重要的知识点,就是关于单例 SingletonBeanRegistry 的接口定义实现,而 DefaultSingletonBeanRegistry 对接口实现后,会被抽象类 AbstractBeanFactory 继承。
    • 现在 AbstractBeanFactory 就是一个非常完整且强大的抽象类了,也能非常好的体现出它对模板模式的抽象定义。

三、实现:实现Bean的定义、注册、获取

3.1 工程结构

spring-step-02
|-src
    |-main
    |   |-java
    |       |-com.lino.springframework
    |           |-factory
    |           |   |-config
    |           |   |   |-BeanDefinition.java
    |           |   |   |-SingletonBeanRegistry.java
    |           |   |-support
    |           |   |   |-AbstractAutowireCapableBeanFactory.java
    |           |   |   |-AbstractBeabFactory.java
    |           |   |   |-BeanDefinitionRegistry.java
    |           |   |   |-DefaultListableBeanFactory.java
    |           |   |   |-DefaultSingletonBeanRegistry.java
    |           |   |-BeanFactory.java
    |           |-BeansException.java
    |-test
        |-java
            |-com.lino.springframework.test
            |-bean
            |   |-UserService.java
            |-ApiTest.java

3.2 实现Bean的定义、注册、获取类图

在这里插入图片描述

  • Spring Bean 容器的功能实现,已经具备了一定的设计复杂性,这些复杂的类关系设计在各个接口定义和实现以及在抽象类继承中都有所体现。
    • BeanFactory 的定义由 AbstractBeanFactory 抽象类实现接口的 getBean 方法。
    • AbstractBeanFactory 又继承实现了 SingletonBeanRegistryDefaultSingletonBeanRegistry 类。这样 AbstractBeanFactory 抽象类就具备了单例 Bean 的注册功能。
    • AbstractBeanFactory 中又定义了两个抽象方法:getBeanDefinition(String beanName)createBean(String beanName, BeanDefinition beanDefinition),这两个抽象方法分别由 DefaultListableBeanFactoryAbstractAutowireCapableBeanFactory 实现。
    • 最终 DefaultListableBeanFactory 还会继承抽象类 AbstractAutowireCapableBeanFactory 也就可以调用抽象类中的 createBean 方法了。
  • 所有的实现都以职责划分、共性分离以及调用关系定义为标准搭建的类关系。

3.3 定义Bean异常

BeansException.java

package com.lino.springframework;

/**
 * @description: 定义 Bean 异常
 */
public class BeansException extends RuntimeException {

    public BeansException(String msg) {
        super(msg);
    }

    public BeansException(String msg, Throwable cause) {
        super(msg, cause);
    }
}

3.4 BeanDefinition定义和注册

3.4.1 BeanDefinition定义

BeanDefinition.java

package com.lino.springframework.factory.config;

/**
 * @description: Bean 对象信息定义
 */
public class BeanDefinition {

    /**
     * bean对象
     */
    private Class beanClass;

    public BeanDefinition(Class beanClass) {
        this.beanClass = beanClass;
    }

    public Class getBeanClass() {
        return beanClass;
    }

    public void setBeanClass(Class beanClass) {
        this.beanClass = beanClass;
    }
}
  • Bean 定义类中把 Object 替换为 Class,这样就可以把 Bean 的实例化操作放到容器中处理了。

3.4.2 BennDefinition定义注册接口

BeanDefinitionRegistry.java

package com.lino.springframework.factory.support;

import com.lino.springframework.factory.config.BeanDefinition;

/**
 * @description: Bean 定义注册接口
 */
public interface BeanDefinitionRegistry {
    /**
     * 向注册表中注册 BeanDefinition
     *
     * @param beanName       Bean 名称
     * @param beanDefinition Bean 定义
     */
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
}
  • Bean 定义注册接口只有一个接口方法,就是向注册表中注册 BeanDefinition

3.5 单例注册接口定义和实现

3.5.1 单例注册接口

SingletonBeanRegistry

package com.lino.springframework.factory.config;

/**
 * @description: 单例 Bean 注册表
 */
public interface SingletonBeanRegistry {

    /**
     * 返回在给定名称下注册的(原始)单例对象
     *
     * @param beanName 要查找的bean的名称
     * @return 返回注册的单例对象
     */
    Object getSingleton(String beanName);

    /**
     * 注册单例对象
     *
     * @param beanName        Bean 对象名称
     * @param singletonObject Bean 对象
     */
    void registerSingletonBean(String beanName, Object singletonObject);
}
  • 这个类比较简单,主要是定义了一个获取单例对象和注册单例对象的接口。

3.5.2 单例注册接口的实现

DefaultSingletonBeanRegistry.java

package com.lino.springframework.factory.support;

import com.lino.springframework.factory.config.SingletonBeanRegistry;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 通用的注册表实现
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

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

    @Override
    public Object getSingleton(String beanName) {
        return singletonObjects.get(beanName);
    }

    @Override
    public void registerSingletonBean(String beanName, Object singletonObject) {
        singletonObjects.put(beanName, singletonObject);
    }
}
  • DefaultSingletonBeanRegistry 主要实现 getSingleton 方法和 registerSingletonBean 方法。

3.6 抽象类定义模板方法

3.6.1 Bean工厂接口

BeanFactory.java

package com.lino.springframework.factory;

import com.lino.springframework.BeansException;
import com.lino.springframework.factory.config.BeanDefinition;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: 定义 Bean 工厂接口
 */
public interface BeanFactory {
    /**
     * 返回 Bean 的实例对象
     *
     * @param name 要检索的bean的名称
     * @return 实例化的 Bean 对象
     * @throws BeansException 不能获取 Bean 对象,抛出异常
     */
    Object getBean(String name) throws BeansException;
}

3.6.2 抽象Bean工厂基类,定义模板方法

AbstractBeanFactory.java

package com.lino.springframework.factory.support;

import com.lino.springframework.BeansException;
import com.lino.springframework.factory.BeanFactory;
import com.lino.springframework.factory.config.BeanDefinition;

/**
 * @description: 抽象的 Bean 工厂基类,定义模板方法
 */
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {

    @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 对象
     *
     * @param beanName 要检索的bean的名称
     * @return Bean 对象
     */
    protected abstract BeanDefinition getBeanDefinition(String beanName);

    /**
     * 创建Bean对象
     *
     * @param beanName       要检索的bean的名称
     * @param beanDefinition Bean对象
     * @return 实例化的Bean对象
     */
    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition);
}
  • AbstractBeanFactory 首先继承了 DefaultSingletonBeanRegistry,也就具备了使用单例注册类方法。
  • 接下来很重要的一点是关于接口 BeanFactory 的实现。
    • 在方法 getBean 的实现过程中可以看到,主要是对单例 Bean 对象的获取以及在获取不到时需要拿到 Bean 的定义做相应 Bean 实例化操作。
    • 那么 getBean 并没有自身的去实现这些方法,而是只定义了调用过程以及提供了抽象方法,由实现此抽象类的其他类做相应实现。
  • 后续继承抽象类 AbstractBeanFactory 的类有两个。
    • 包括:AbstractAutowireCapableBeanFactoryDefaultListableBeanFactory,这两个类分别做了相应的实现处理。

3.6.3 实例化Bean类

AbstractAutowireCapableBeanFactory.java

package com.lino.springframework.factory.support;

import com.lino.springframework.BeansException;
import com.lino.springframework.factory.config.BeanDefinition;

/**
 * @description: 实现默认bean创建的抽象bean工厂超类
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition) {
        Object bean = null;
        try {
            bean = beanDefinition.getBeanClass().newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new BeansException("Instantiation of bean failed", e);
        }
        registerSingletonBean(beanName, bean);
        return bean;
    }
}
  • AbstractAutowireCapableBeanFactory 类中实现了 Bean 的实例化操作 newInstance
  • 在处理完 Bean 对象的实例化后,直接调用 registerSingletonBean 方法存放到单例对象的缓存中去。

3.6.4 核心类:默认的Bean工厂实现

DefaultListableBeanFactory.java

package com.lino.springframework.factory.support;

import com.lino.springframework.BeansException;
import com.lino.springframework.factory.config.BeanDefinition;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 默认的Bean工厂实现类
 */
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {

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

    @Override
    protected BeanDefinition getBeanDefinition(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new BeansException("No bean named '" + beanName + "' is defined");
        }
        return beanDefinition;
    }

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(beanName, beanDefinition);
    }
}
  • DefaultListableBeanFactorySpring 的源码中也是一个非常核心的类。
  • DefaultListableBeanFactory 继承了 AbstractAutowireCapableBeanFactory 类,也就具备了接口 BeanFactoryAbstractBeanFactory 等一连串的功能实现。
  • 除此之外这个类还实现了接口 BeanDefinitionRegistry 中的 registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法。
    • 还会看到一个 getBeanDefinition 的实现,这个方法是抽象类 AbstractBeanFactory 中定义的抽象方法。现在注册 Bean 定义与获取 Bean 定义就可以同时使用了。
    • 接口定义了注册,抽象类定义了获取,都集中在 DefaultListableBeanFactory 中的 beanDefinitionMap 里。

四、测试:实现Bean的定义、注册、获取

ApiTest.java

@Test
public void test_BeanFactory() {
    // 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.queryUserInfo();

    // 3.第二次获取bean
    UserService userService_singleton = (UserService) beanFactory.getBean("userService");
    userService_singleton.queryUserInfo();
}
  • 在此次的单元测试中处理包括:Bean 工厂、注册 Bean、获取 Bean 三个步骤,还额外增加了一次对象的获取和调用。
    • 这里主要测试验证单例对象是否正确的存放到了缓存中。
  • 此时,我们把 UserService.class 传递给了 BeanDefinition

测试结果

查询用户信息查询用户信息

在这里插入图片描述

  • 这里会有两次测试信息,一次是获取 Bean 时直接创建的对象,另外一次是从缓存中获取的实例化对象。
  • 此外从调试的截图中看出第二次获取单例对象,是从内存中获取的。

五、总结:实现Bean的定义、注册、获取

  • Spring Bean 容器的实现类中要重点关注类之间的职责和关系,几乎所有的程序功能设计都离不开接口、抽象类、实现、继承。
    • 而这些不同特性类的使用就可以非常好的隔离开类的功能职责和作用范围。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决 Spring Boot 与 springfox 的 NullPointerException 问题

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

MRI多任务技术及应用

目录 一、定量心血管磁共振成像&#xff08;CMR&#xff09;的改进方法二、磁共振多任务三、磁共振多任务的成像框架四、磁共振多任务的图像模型和采样和重建策略五、利用MR多任务进行快速三维稳态CEST(ss-CEST)成像5.1 利用MR多任务进行快速三维稳态CEST(ss-CEST)成像介绍5.2 …

嵌入式学习笔记(14)位置有关编码

位置无关编码&#xff08;PIC,position independent code&#xff09;&#xff1a;汇编源文件被编码成二进制可执行程序时编码方式与位置&#xff08;内存地址&#xff09;无关。在我们写程序时&#xff0c;必须给链接器指定地址。将来的程序被执行时必须放在当时链接时给定的地…

iPhone照片导入电脑的绝佳教程,赶快get起来!

为什么都说苹果手机拍照比安卓手机好看&#xff1f;因为苹果手机拍照更加真实&#xff0c;其镜头和硬件能让照片更好地接近原始色彩。手机拍照好看就会让人忍不住多拍几张&#xff0c;久而久之手机内存就满了。有什么方法能将照片保存到电脑上吗&#xff1f;iPhone照片导入电脑…

《vue3实战》运用push()方法实现电影评价系统的添加功能

目录 前言 电影评价系统的添加功能是什么&#xff1f; 电影评价系统的添加功能有什么作用&#xff1f; 一、push&#xff08;&#xff09;方法是什么&#xff1f;它有什么作用&#xff1f; 含义&#xff1a; 作用&#xff1a; 二、功能实现 这段是添加开始时点击按钮使…

私募证券基金动态-23年8月报

成交量&#xff1a;8月日均8,252.00亿元 8月A股两市日均成交8,252.00亿元&#xff0c;环比下降12.23%、同比下降18.11%。8月整体23个交易日&#xff0c;仅有3个交易日单日成交金额过万亿&#xff0c;且成交量起伏较大&#xff0c;单日成交金额最低仅有6805.32亿元&#xff08;…

C# 实现电子签名

本项目基于Emgu.CV&#xff08;C#下OpenCv的封装&#xff09;开发的&#xff0c;编译器最新版Vs2022&#xff0c;编译环境x86 直接看效果图 1.主页面 2.我们先看手写的方式&#xff1a; 点击确认就到主界面&#xff0c;如下 &#xff1a; 点击自动适配-&#xff0c;再点击生成…

windows安装向量数据库milvus

本文介绍windows下安装milvus的方法。 一.Docker安装 1.1docker下载 首先到Docker官网上下载docker:Docker中文网 官网 1.2.安装前前期准备 先使用管理员权限打开windows powershell 然后在powershell里面输入下面那命令&#xff0c;启用“适用于 Linux 的 Windows 子系统”…

JMeter压力测试入门教程

Apache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试&#xff0c;它最初被设计用于Web应用测试但后来扩展到其他测试领域。 它可以用于测试静态和动态资源例如静态文件、Java小服务程序、CGI脚本、Java 对象、数据库&#xff0c; FTP服务器, 等等。J…

JavaScript中的深拷贝和浅拷贝

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 浅拷贝&#xff08;Shallow Copy&#xff09;&#xff1a;⭐深拷贝&#xff08;Deep Copy&#xff09;&#xff1a;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带…

数据结构day7栈-顺序栈的实现

用指针比用数组好&#xff0c;这样用户可以自己指定空间的大小&#xff0c;有参与感。 全部代码: main.c #include <stdio.h> #include <string.h> #include "sqstack.h"int main(int argc, char *argv[]) {sqstack *s;int i;s stack_create(100);if(…

数据结构 - 单链表

文章目录 目录 文章目录 一、什么是链表? 线性表: 顺序表: 二、链表的分类和实现 分类: 实现: 1.创建节点类 2.创建单链表 1.addTail(尾增) 2.删除节点值为key的第一个节点 3.插入节点(在指定位置) 4.获取链表长度 总结 前言 大家好,这篇博客给大家讲一下什么是…

《代码随想录》刷题笔记——数组篇【java实现】

*二分查找 题目链接 https://leetcode.cn/problems/binary-search/ 左闭右闭区间实现 时间复杂度&#xff1a;O(log n)空间复杂度&#xff1a;O(1) /*** 左闭右闭写法** param nums* param target* return*/ public static int search1(int[] nums, int target) {if (nums…

关于工信部发布的app备案以及小程序备案流程

一、相关政策 通知&#xff1a;https://beian.miit.gov.cn/#/Integrated/lawStatute 腾讯备案&#xff1a;网站备案 首次备案-网站备案-文档中心-腾讯云 阿里备案&#xff1a;网站备案_ICP备案_备案迁移_备案-阿里云 二、遇到的问题 APP备案 安卓获取平台公钥方法&#xf…

Vue + Element UI 前端篇(九):接口格式定义

接口请求格式定义 前台显示需要后台数据&#xff0c;我们这里先把前后端交互接口定义好&#xff0c;没有后台的时候&#xff0c;也方便用mock模拟。 接口定义遵循几个规范&#xff1a; 1. 接口按功能模块划分。 系统登录&#xff1a;登录相关接口 用户管理&#xff1a;用户…

Redis-1.4-过期策略

1 设置带过期时间的 key # 时间复杂度&#xff1a;O&#xff08;1&#xff09;&#xff0c;最常用方式 expire key seconds# 字符串独有方式 setex(String key, int seconds, String value)除了string独有设置过期时间的方法&#xff0c;其他类型都需依靠expire方法设置时间&a…

说说MySQL回表查询与覆盖索引

分析&回答 什么是回表查询&#xff1f; 通俗的讲就是&#xff0c;如果索引的列在 select 所需获得的列中&#xff08;因为在 mysql 中索引是根据索引列的值进行排序的&#xff0c;所以索引节点中存在该列中的部分值&#xff09;或者根据一次索引查询就能获得记录就不需要…

【Linux】Qt Remote之Remote开发环境搭建填坑小记

总体思路 基于WSL2&#xff08;Ubuntu 22.04 LTS&#xff09;原子Alpha开发板进行Qt开发实验&#xff0c;基于Win11通过vscode remote到WSL2&#xff0c;再基于WSL2通过Qt 交叉编译&#xff0c;并通过sshrsync远程到开发板&#xff0c;构建起开发工具链。 Step1 基于Win11通过…

Mock接口测试

什么是mock? 测试桩&#xff0c;模拟被测对象的返回&#xff0c;用于测试 通常意义的mock指的就是mock server, 模拟服务端返回的接口数据&#xff0c;用于前端开发&#xff0c;第三方接口联调 为什么要mock? 1. 解决依赖问题&#xff1a;当我们测试一个接口或者功能模块…

Python Opencv实践 - 轮廓特征(最小外接圆,椭圆拟合)

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/stars.PNG") plt.imshow(img[:,:,::-1])#轮廓检测 img_gray cv.cvtColor(img, cv.COLOR_BGR2GRAY) ret,thresh cv.threshold(img_gray, 127, 255, 0) contou…