第三步->手撕spring源码之基于Cglib实现实例化策略

为什么深入研究spring源码?
其实每一个程序员每天的工作都是一贯的CRUD 实现业务和需求完成的操作。几年这样的操作让我感觉在这方面要提神能力 光靠CRUD是绝对不可能的事情 CRUD只是满足你作为一个搬砖人而已。编程能力提升?其实更多的编程能力的提升是你对复杂场景的架构把控以及对每一个技术实现细节点的不断用具有规模体量的流量冲击验证时,是否能保证系统稳定运行从而决定你见识了多少、学到了多少、提升了多少!
本这步骤是解决上一步骤中 Bean 对象在含有构造函数进行实例化的坑。
演示上一步骤的问题
将实例化的bean进行改造 如下所示
在这里插入图片描述
在这里插入图片描述
主要原因就是因为 beanDefinition.getBeanClass().newInstance(); 实例化方式并没有考虑构造函数的入参
解决上述方案如下所示
解决流程
在这里插入图片描述
spring源码中
1:BeanFactory 中添加 Object getBean(String name, Object… args) 接口,这样就可以在获取 Bean 时把构造函数的入参信息传递进去了。
2:另外一个核心的内容是使用什么方式来创建含有构造函数的 Bean 对象呢?这里有两种方式可以选择,一个是基于 Java 本身自带的方法 DeclaredConstructor,另外一个是使用 Cglib 来动态创建 Bean 对象。Cglib 是基于字节码框架 ASM 实现。
以下开始实战
总体设计思想:工程中添加 InstantiationStrategy 实例化策略接口,补充相应的 getBean 入参信息,让外部调用时可以传递构造函数的入参并顺利实例化。
新增 getBean 接口
在这里插入图片描述
实例化策略接口定义
在这里插入图片描述
1:接口 instantiate 方法中添加必要的入参信息,包括:beanDefinition、 beanName、ctor、args
2:Constructor是 java.lang.reflect 包下的 Constructor 类,里面包含了一些必要的类信息,有这个参数的目的就是为了拿到符合入参信息相对应的构造函数。
3::args 就是一个具体的入参信息了,最终实例化时候会用到。
JDK 实例化
在这里插入图片描述
重点为有构造函数的实例化,实例化方式为 clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);,把入参信息传递给 newInstance 进行实例化。
Cglib 实例化

public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {
    @Override
    public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(beanDefinition.getClassBean());
        enhancer.setCallback(new NoOp() {
            @Override
            public int hashCode() {
                return super.hashCode();
            }
        });
        if (null == ctor) return enhancer.create();
        return enhancer.create(ctor.getParameterTypes(), args);
    }
}

进行策略的调用

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            bean = createBeanInstance(beanDefinition, beanName, args);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        addSingleton(beanName, bean);
        return bean;
    }

    protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
        Constructor constructorToUse = null;
        Class<?> beanClass = beanDefinition.getClassBean();
        Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
        for (Constructor ctor : declaredConstructors) {
            if (null != args && ctor.getParameterTypes().length == args.length) {
                constructorToUse = ctor;
                break;
            }
        }
        return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
    }

1:在 AbstractAutowireCapableBeanFactory 抽象类中定义了一个创建对象的实例化策略属性类 InstantiationStrategy instantiationStrategy,这里我们选择了 Cglib 的实现类。
2:接下来抽取 createBeanInstance 方法,在这个方法中需要注意 Constructor 代表了你有多少个构造函数,通过 beanClass.getDeclaredConstructors() 方式可以获取到你所有的构造函数,是一个集合。
3:接下来就需要循环比对出构造函数集合与入参信息 args 的匹配情况,这里我们对比的方式比较简单,只是一个数量对比,而实际 Spring 源码中还需要比对入参类型,否则相同数量不同入参类型的情况,就会抛异常了。
测试看下效果
在这里插入图片描述
测试无参数构造

  @Test
    public void test_newInstance() throws IllegalAccessException, InstantiationException {
        UserService userService = UserService.class.newInstance();
        System.out.println(userService);

    }

有构造函数实例化
在这里插入图片描述
获取构造函数信息
在这里插入图片描述
Cglib 实例化
在这里插入图片描述
总结:
1:在InstantiationStrategy 实例化策略接口,并新增了两个实例化类。这部分类的名称与实现方式基本是 Spring 框架的一个缩小版,大家在学习过程中也可以从 Spring 源码找到对应的代码。
从不断的完善增加需求可以看到的,当你的代码结构设计的较为合理的时候,就可以非常容易且方便的进行扩展不同属性的类职责,而不会因为需求的增加导致类结构混乱。所以在我们自己业务需求实现的过程中,也要尽可能的去考虑一个良好的扩展性以及拆分好类的职责。

以上是第三步->手撕spring源码之基于Cglib实现实例化策略 关注老哥带你上高速哦。。。。。。 后续继续完成手写spring源码。
在这里插入图片描述

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

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

相关文章

用 Supabase CLI 进行本地开发环境搭建

文章目录 &#xff08;零&#xff09;前言&#xff08;一&#xff09;Supabase CLI&#xff08;1.1&#xff09;安装 Scoop&#xff08;1.2&#xff09;用 Scoop 安装 Supabase CLI &#xff08;二&#xff09;本地项目环境&#xff08;2.1&#xff09;初始化项目&#xff08;2…

Promise.all和 race

Promise.all() all方法可以完成并行任务&#xff0c; 它接收一个数组&#xff0c;数组的每一项都是一个promise对象。返回值&#xff1a; 成功时&#xff1a;当数组中所有的promise的状态都达到resolved的时候&#xff0c;就返回包含所有 Promise 结果的数组&#xff0c;并且…

【C++】————类与对象(上)-基础知识

目录 1.面向过程和面向对象初步认识 2.类的引入 3.类的定义 类的两种定义方式&#xff1a; 成员变量命名规则的建议&#xff1a; 4.类的访问限定符及封装 4.1 访问限定符 ​编辑 【面试题】问题&#xff1a;C中struct和class的区别是什么&#xff1f; 4.2 封装 【面试…

数据分析中大数据和云计算

大数据和云计算 前言一、大数据二、大数据定义三、数据存储单位四、大数据存储技术五、大数据应用技术六、大数据特征七、数据容量八、数据类型的多样性结构化数据半结构化数据非结构化数据 九、获取数据的速度十、可变性十一、真实性十二、复杂性十三、价值十四、云计算十五、…

小白有什么副业可以做?

对于小白来说&#xff0c;以下是一些适合做副业的选择 1. 网络销售 可以在电商平台上开设小店&#xff0c;销售自己感兴趣的产品&#xff0c;如手工制品、二手物品、个人设计的商品等。 2. 做任务 目前网上最流行的就是做任务&#xff0c;因为简单无门槛&#xff0c;我推荐百…

partially initialized module ‘replicate‘ has no attribute ‘run‘

partially initialized module replicate has no attribute run(most likely due to a circular import) 在包名上停留查看impot 包的地址。 报错原因&#xff1a; 文件重名了&#xff0c;导入了 当前文件 。 修改文件名 即可。

分布式版本控制工具 - Git

文章目录 1. 概念介绍2. 客户端2.1 介绍2.2 仓库操作2.3 文件操作2.4 分支原理与操作2.5 标签2.6 远程仓库2.7 README与IGNORE 3. IDEA集成4. 版本号4.1 介绍4.2 文件操作4.2 分支操作 5. 命令5.1 介绍5.2 仓库操作5.3 文件操作5.4 分支操作5.5 标签操作5.6 远程仓库 1. 概念介…

【WebGPU】WebGPU 中的反应扩散计算着色器

在本教程中&#xff0c;我们将使用 WebGPU 技术中的计算着色器实现图像效果。更多精彩内容尽在数字孪生平台。 程序结构 主要构建两个 WebGPU 管道&#xff1a; 运行反应扩散算法多次迭代的计算管道&#xff08;js/rd-compute.js 和 js/shader/rd-compute-shader.js&#xff…

Linux学习之路 -- 文件系统 -- 缓冲区

前面介绍了文件描述符的相关知识&#xff0c;下面我们将介绍缓冲区的相关知识。 本质上来说&#xff0c;缓冲区就是一块内存区域&#xff0c;因为内核上的缓冲区较复杂&#xff0c;所以本文主要介绍C语言的缓冲区。 目录 1.为什么要有缓冲区 2.应用层缓冲区的默认刷新策略 …

【C++】STL — map和set的使用详细介绍

前言 本章将继续学习STL中的两个很重要的容器map和set&#xff0c;其底层实现是封装了一个红黑树&#xff0c;我们通过本节来学习和深入了解一下这两大容器。。。 序列式容器&#xff1a; string 、Vector、List 、dequeue 关联式容器&#xff1a;MAP 、SET、nordered_map、uno…

成员函数构造函数析构函数

文章目录 类的6个默认成员函数构造函数概述定义特性 析构函数概述特性 类的6个默认成员函数 空类&#xff1a; 如果一个类里面什么都没有写&#xff0c;我们称之为空类 class Date {};空类真的什么都没有吗&#xff1f; 实际上并非如此&#xff0c;编译器会自动生成6个默认成…

【大数据】HDFS

文章目录 [toc]HDFS 1.0NameNode维护文件系统命名空间存储元数据解决NameNode单点问题 SecondaryNameNode机架感知数据完整性校验校验和数据块检测程序DataBlockScanner HDFS写流程HDFS读流程HDFS与MapReduce本地模式Block大小 HDFS 2.0NameNode HANameNode FederationHDFS Sna…

C++笔试强训day19

目录 1.小易的升级之路 2.礼物的最大价值 3.对称之美 1.小易的升级之路 链接 模拟就行&#xff0c;唯一可能是难点得就是gcd&#xff08;最大公约数&#xff09; #include <iostream> using namespace std; #define int long long const int N 1e5 10; int arr[N];…

【DIY小记】深圳万象天地餐馆探店点评

第一次在技术博客里面写生活日记&#xff0c;也算是破了个小天荒。个人以为&#xff0c;博客是个人生活思考的载体&#xff0c;而技术只占生活的一部分&#xff0c;那么博客里为什么一定要限制只能够写技术内容&#xff0c;不能写点其它生活上的东西呢&#xff1f;思来想去&…

科研诚信与学术规范 2024年春 期末考试答案

章节答案&#xff1a;https://www.bilibili.com/video/BV1JZ42177F8/ 是这个课&#xff0c;网上的大多数答案都是以前的&#xff0c;跟这门课没啥关系. 期末考试的答案长这样&#xff0c;题库有80个题&#xff0c;考试一般是50个题。 期末考试答案&#xff1a;&#xff08;不…

C++动态内存区域划分、new、delete关键字

目录 一、C/C中程序的内存区域划分 为什么会存在内存区域划分&#xff1f; 二、new关键字 1、内置类型的new/delete使用方法&#xff1a; 2、new和delete的本质 一、C/C中程序的内存区域划分 为什么会存在内存区域划分&#xff1f; 因为不同数据有不同的存储需求&#xff0…

6818Linux内核--Bootloader应用分析

Bootloader应用分析 一个嵌入式 Linux 系统从软件的角度看通常可以分为四个层次&#xff1a; 引导加载程序。包括固化在固件( firmware )中的 boot 代码(可选)&#xff0c;和 Boot Loader 两大部分。 Linux 内核。特定于嵌入式板子的定制内核以及内核的启动参数。 文件系统…

Polygon市值机器人

随着区块链技术的蓬勃发展和数字货币市场的日益繁荣&#xff0c;投资者们对于如何精准把握市场动态、实现资产稳健增长的需求愈发迫切。在这个背景下&#xff08;市值管理飞//机//aishutuyu&#xff09;&#xff0c;Polygon市值机器人应运而生&#xff0c;作为一款基于Polygon公…

TriCore:Interrupt

今天简单总结下 TriCore 的中断路由模块。 名词缩写 缩写全称说明IRInterrupt Router SRService Request 包括&#xff1a; 1. External Resource 2. Internal Resource 3.SW&#xff08;Software&#xff09; SPService Privoder 包括&#xff1a; 1. CPU 2. DMA SRNServic…

【5分钟学会一个知识点】01.Elasticsearch基本操作-增删改查

目录 【5分钟学会一个知识点-探索现代搜索与分析引擎的魅力】01.Elasticsearch基本操作-增删改查1.基本操作1.1索引操作1.2文档操作1.3查询1.4修改数据1.5查询1.5.1条件查询1.5.1.1遍历所有的索引1.5.1.2查询某个索引1.5.1.3条件查询1&#xff1a;使用GET url传参数1.5.1.4条件…