spring(三)-----------什么是beanDefinition?

news2024/9/20 22:33:12

上篇我们以mybatis如何注入mapper对象为引,发现mybatis使用了FactoryBean(动态代理)+动态注册beanDefinition 的方式实现了对多个bean进行注入。

这篇我们延续上篇的问题,什么是beanDefinition?动态注入beanDefinition是个什么样的过程?

1、beanDefinition

beanDefinition可以称之为spring bean的建模对象,

        那么什么是spring bean 的建模对象呢?还是一言概之就是把一个 bean 实例化出来的模型对象?有人会 问把一个bean 实例化出来有 Class 就行了啊, Class 也就是我们通常说的类对象,就是一个普通对象的建 模对象,那么为什么spring 不能用 Class 来建立 bean ? 很简单,因为 Class 无法完成 bean 的抽象,比如 bean的作用域, bean 的注入模型, bean 是否是懒加载等等信息, Class 是无法抽象出来的,故而需要 一个BeanDefifinition 类来抽象这些信息,以便于 spring 可以根据这些信息去完美的实例化一个 bean
        上述文字可以简单理解spring 当中的 BeanDefifinition 就是 java 当中的 Class Class 可以用来描述一个 类的属性和方法等等其他信息 BeanDefifintion 可以描述 springbean 当中的 scope lazy ,以及属性和 方法等等其他信息。

BeanDefifintion 是一个接口,但是 spring 框架当中有很多类实现了 BeanDefifintion ,各个不同的
BeanDefifintion 的作用有一点点区别 —— 大同小异。关于 BeanDefifintion 的详细内容后面我们再来分 析;这里先列举几个和本节课有关的公共属性;上图如果用一个类代码来表示:
BeanDefintion{

    //如果这个对象当中有值,spring会把他作为构造方法的参数填充
    private ConstructorArgumentValues constructorArgumentValues;

    //默认单例
    private String scope = SCOPE_DEFAULT;

    //是否需要依赖别的bean 默认没有
    private String[] dependsOn;

    //这个beanDefinition对象所描述的类
    private volatile Object beanClass;

    //如果这个对象当中有值,spring会把他作为setter方法的参数填充
    private MutablePropertyValues propertyValues;
}
好了如果你对 spring BeanDefifinition 有了基本理解;那么下面在通过图来继续说明 beanDefifinition 是怎 么关联spring bean 的实例化的。先用一副图来说明 java 实例化一个对象的基本流程:
上图说的是假设磁盘上有 N .java 文件,首先我们把这些 java 文件编译成 class 文件,继而 java 虚拟机启 动会把这些class 文件 load 到内存,当遇到 new 关键字的时候会根据类的模板信息实例化这个对象;
但是 spring bean 实例化过程和一个普通 java 对象的实例化过程还是有区别的,同样用一幅图来说明一 下:

特别说明:上图扫描之后识别 X Y 后存的不是类,是 ASM 直接读取的,但是为了容易理解姑且认为就是类吧
前提:假设磁盘上有 A C X Y Z D E 这些类;其中只有 X Y 是被加了注解(能够被 spring 扫描 到);
:启动 spring 容器,开始扫描,识别 X Y 后存入集合
:遍历集合实例化一个 BeanDefifinition 对象,解析 X Y ;填充 BeanDefifinition
:把初始化好、填充好的 beandefifinition 对象存到一个 beandefifinitionMap 当中
:调用所有 BeanFactoryPostProcessor (假设你没有提供、没有扩展,只会执行 spring 内部的,不 走红色)
spring 在实例化 bean 之前会对 beandefifinition 进行 validate (验证是否单例、是否 lazy 等等)
:如果一切正常(先不考虑不正常的),则直接实例化对象,完成自动注入等等后放到单例池
这是正常流程,一个简化版的 bean 实例化过程(实际远比这个复杂,后面再来分析复杂版的)
当然如果你对 spring 做了扩展,比如你提供了一个类实现了 BeanFactoryPostProcessor ,并且被 spring 扫描到了 且你提供的这个BeanFactoryPostProcessor 修改了 beandefifinitionMap 当中的信息则第 步之后后先走
:回调你提供的 BeanFactoryPostProcessor ,根据 key="x" 获取 beandefifinitionMap 当中的
beandefifinition 对象,然后修改这个 beandefifinition 对象的 beanclass D.class 那么最后实例化出来的bean 就是 D 而不是 X。
总结:又是一言概之 —— 单例池当中的 bean 可能与你提供的类无关;和 beandefifinitionMap 是有直接关 系的,可以被偷梁换柱;
言归正传,我们现在知道 BeanDefifinition在spring bean形成的大概作用和流程,下面我们还需要回到我们文章的主题,如何动态注册BeanDefifinition?
2、如何动态注册BeanDefifinition?
1)、 BeanFactoryPostProcessor接口
从上面的bean实例化过程可以知道,在没实例化成bean之前,spring提供了一个 BeanFactoryPostProcessor扩展接口(bean工厂的后置处理器),我们去实现该接口,就可以获取到BeanFactory对象,此时就能拿到BeanDefifinition对象,然后进行设置了,大概代码如下:
public class testAA implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        BeanDefinition aa = beanFactory.getBeanDefinition("aa");
        aa.setLazyInit(true);
        aa.setFactoryBeanName("aaaa");
    }
}

但是我们一般不使用这种方式。

spring中还提供了两种方式去注入BeanDefifinition。

2)、BeanDefifinitionRegistryPostProcessor接口

public class testAA implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        BeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinitionRegistry.registerBeanDefinition("aa", beanDefinition);

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}

可以实现该接口实现postProcessBeanDefinitionRegistry方法进行BeanDefition对象的注册

或者实现postProcessBeanFactory方法,获取到beanFactory对象编写类似1)进行修改BeanDefition。

我们还是不推荐第二种方式,比起这个还有很好的第三种方式

3)、ImportBeanDefifinitionRegistrar接口

(该接口在spring、springboot大量的使用,例如@Service、@Compnent等都是用它进行动态注册bean,很多三方框架集成Spring的时候,都会通过该接口,实现扫描指定的类,然后注册到spring容器中。 比如Mybatis中的Mapper接口,springCloud中的FeignClient接口,都是通过该接口实现的自定义注册逻)

作用:可以动态注册Beandefition、bean对象等。

实现其registerBeanDefinitions方法,我们可以看看如何模拟第三方框架集成到spring中:

@Slf4j
public class testAA implements ImportBeanDefinitionRegistrar {

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


    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //模拟第三方集成spring时,扫描需要注入的对象,并将对象转化为BeanDefinition类型存进map中
        scan();
        
        //遍历扫描到的BeanDefinition对象进行属性修改操作(修改非必须),然后进行动态注册
        for(String key : map.keySet()){
            AbstractBeanDefinition mapperBd = (AbstractBeanDefinition)
                    map.get(key);
            Class mapperClz =mapperBd.getBeanClass();
            System.out.println();
            log.debug("before:{}",mapperBd.getBeanClass().getName());
            mapperBd.setBeanClass(MyFactoryBean.class);
            log.debug("after:{}",mapperBd.getBeanClass().getName());
            mapperBd.getPropertyValues().add("mapperInterface",mapperClz.getName());
            
            //动态注册BeanDefition
            registry.registerBeanDefinition(key,mapperBd);

    }

}

    private void scan() {
        //假装模拟了扫描过程,把扫描结果放到了map当中
    }
    }

然后编写配置类,将这个testAA配置类注入spring中(使用@Import注解)

@Configuration
@Import(testAA.class)
public class aaConfig{
}

(注意:上面的这几种方式都是动态注册Beanfinition,但是仍然需要将配置类注入到spring中,spring才能知道你注册了这个beandefinition)ImportBeanDefifinitionRegistrar一般搭配@Import一起使用Spring @Import导入Bean的三种类型:Component、ImportSelector、ImportBeanDefinitionRegistrar_每天都要加油呀!的博客-CSDN博客

一般第三方集成到spring时,第三方的框架是不能使用spring的注入注解的,都是在开发xxx-spring的集成插件的时候利用配置类使用@Import的方式去批量导入多个bean到spring中。

但是此时如果你用@Component注解将testAA注入spring中,此时则发现无法注入testAA中动态注册的Beandefinition,这是为什么???

registerBeanDefinitions方法的修改Beandefinition逻辑:

遍历 map ,把 map当中的 beandefifinition 对应的 beanclass 替换成为 MyFactoryBean (因为我们的目的就是为了让 MyFactoryBean生效,使用了FactoryBean);然后又给 beandefifinition 填充了 getPropertyValues() 的值为当前类 (例如某个mapper , 故而后面 spring 源码在实例化这些 MyFactoryBean 的时候会去 setter 当前类 ;从而完成代理;
这里用了编码的方式写了一个for循环完成了多个mapper的注入
(使用ImportBeanDefifinitionRegistrar完成动态注册BeanDefinition,从而解决了上一篇文章提及的如何解决注入多个mapper的简易性问题)

 这也是@MapperScan将扫描的到Mapper动态注册为BeanDefinition的伪代码。(老版本的mybatis-spring和新版本的代码实现有点差别,但差别不大)

可以看看一些模拟@Mapper、模拟@Compnent、模拟feign集成spring等例子加深对FactoryBean+ ImportBeanDefifinitionRegistrar来实现第三方集成spring进行bean注入的理解
动态注册bean(ImportBeanDefinitionRegistrar, FactoryBean) | wangqi的blog

借助ImportBeanDefinitionRegistrar接口实现bean的动态注入 - 简书

java - 基于ImportBeanDefinitionRegistrar和FactoryBean动态注入Bean到Spring容器中 - 个人文章 - SegmentFault 思否

至此,我们知道了mybatis使用FactoryBean+ImportBeanDefifinitionRegistrar集成到spring中,将mapper对象注入到spring容器中。但这两篇文章的目的是了解对象如何注入spring的,但可以看到,我们对于扫描的部分并没有分析,下面我们开始了解spring是怎么扫描到那些标注了@Compnent注解的类,然后将他们注入到spring中的。(可以先做下上面几个链接的练习,熟悉下怎么模拟达到@Compnent的效果)

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

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

相关文章

yum安装

仓库安装[rootlocalhost ~]# rpm -ivh https://repo.mysql.com//mysql80-community-release-el9-1.noarch.rpm[roothezhihao ~]# dnf install mysql-server -y本地安装wget https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.32-1.el9.x86_64.rpm-bundle.tartar xvf mysql…

STK + C# + Cesium联合编程(二):CZML文件生成及加载

概述本文续上一篇博文&#xff0c;上一篇文章中验证了C# .NET Framework (Web Service) STK Cesium前端显示的相关技术&#xff0c;本篇通过STK安装附带的Pro Tutorial实例以及Export CZML插件演示如何创建STK场景&#xff0c;创建对象&#xff0c;计算Access&#xff0c;并通…

【软件测试】8年测试老兵的突破之路,一路升级打怪......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 目前几乎所有的大厂…

网络基础-基础网络命令

文章目录路由命令查询添加路由1.添加访问某台主机的静态路由2.添加访问某个网络的静态路由3.添加默认网关&#xff1a;删除设计关键字路由2参考路由 命令查询 通过 route --help 或man route 查询 添加路由 1.添加访问某台主机的静态路由 route add -host [目标主机IP地址…

为什么优秀的人总能拥有开挂的人生

时间不会辜负一个自信之人。坚持不懈的努力&#xff0c;如果好的回报尚未到来&#xff0c;那一定是时间还不够长&#xff0c;努力还不够多。 来自尚学堂毕业学生的感悟 我从未接触过编程&#xff0c;来到北京尚学堂学Java的&#xff0c;学习难度自然要比计算机专业的同学大很多…

OpenHarmony使用Socket实现一个UDP客户端详解

一、前言 我们在这里介绍Socket的使用,是为了后面的一篇文章实现设备配网做铺垫。 二、示例详解 点击获取BearPi-HM_Nano源码 ,以D3_iot_udp_client为例: 示例本身很简单,只需要修改 udp_client_demo.c 的2处代码,就能测试了: //连接WIFI,参数1是:WIFI名称,参数2是:…

【ChatGPT】AI 人工智能将使中国教育优势荡然无存

中国的教育现状 论述1&#xff1a; 上小学的时候&#xff0c;老师天天检查你有没有戴红领巾&#xff0c;不带就要体罚&#xff0c;大人告诉你&#xff0c;等到上中学老师就不管这些杂事了。上中学的时候&#xff0c;学习非常辛苦&#xff0c;周末还要补课&#xff0c;大人们又…

人工智能为PMO提供支持的4种方式

许多企业已经认识到 PMO &#xff08;项目管理办公室&#xff09;和 PPM &#xff08;项目组合管理&#xff09;在推动增长、提高组织敏捷性和交付更好的业务成果方面的力量。今天的PMO被期望用更少的钱做更多的事&#xff0c;而且做得更快。 虽然PMO的工作很繁重&#xff0c;…

ChIP-seq 分析:数据比对(3)

读取 reads&#xff08;二者含义相同&#xff0c;下文不做区分&#xff09;1. ChIPseq reads 比对 在评估读取质量和我们应用的任何读取过滤之后&#xff0c;我们将希望将我们的读取与基因组对齐&#xff0c;以便识别任何基因组位置显示比对读取高于背景的富集。 由于 ChIPseq…

DNS 原理入门指南(三)

六、分级查询的实例 dig命令的trace参数可以显示DNS的整个分级查询过程。 $ dig trace math.stackexchange.com 上面命令的第一段列出根域名.的所有NS记录&#xff0c;即所有根域名服务器。 根据内置的根域名服务器IP地址&#xff0c;DNS服务器向所有这些IP地址发出查询请求&…

LeetCode题目笔记——2563. 统计公平数对的数目

文章目录题目描述题目链接题目难度——中等方法一&#xff1a;排序双指针代码/Python代码/C方法二代码/Python总结题目描述 这是前天周赛的第二题。 统计公平数对的数目 - 给你一个下标从 0 开始、长度为 n 的整数数组 nums &#xff0c;和两个整数 lower 和 upper &#xff0c…

【Spring Cloud】如何使用Feign实现远程调用

本期目录前言1. 导入依赖坐标2. 开启Feign自动装配3. 声明远程调用4. 替代RestTemplate5. 测试前言 本次示例代码的文件结构如下图所示。 1. 导入依赖坐标 在 order-service 的 pom.xml 文件中导入 Feign 的依赖坐标。 <!-- Feign远程调用客户端 --> <dependency&…

FastAPI(二)路由映射

目录 一、在根目录-新建apps文件夹 二、在apps文件夹下-新建user文件夹和menu文件夹 三、编写API 四、 将子路由导入根路由中 五、改造main.py 六、启动并访问接口文档 一、在根目录-新建apps文件夹 编辑__init__.py文件如下&#xff1a; from fastapi import APIRoute…

动态规划专题——背包问题

&#x1f9d1;‍&#x1f4bb; 文章作者&#xff1a;Iareges &#x1f517; 博客主页&#xff1a;https://blog.csdn.net/raelum ⚠️ 转载请注明出处 目录前言一、01背包1.1 使用滚动数组优化二、完全背包2.1 使用滚动数组优化三、多重背包3.1 使用二进制优化四、分组背包总结…

Spring基础入门(一)之 理论基础概念

文章目录前言Spring 体系结构IOC&DI1.Ioc控制反转2.DI依赖注入3.目标IOC相关之bean1.bean的基础配置2.bean的实例化3.bean的生命周期4.IOC管理第三方beanDI相关之注入注解开发1.介绍2.注解之bean&DI3.注解管理第三方bean4.常见注解的含义AOP1.介绍2.核心概念3.常见注解…

机械革命z2黑苹果双系统改造计划

原来的系统硬盘才256G实在太小了&#xff0c;趁固态便宜搞了一块大华C900Plus-b 1T固态&#xff0c;加上之前电脑里后加的一块海康威视c2000pro 1T准备搞一个winmac双系统生产力工具 黑苹果的详细教程b站上有很多&#xff0c;也可以看下国光师傅的博客&#xff0c;https://app…

webpack打包工具及原理

一、WebpackWebpack 是一个用于现代JavaScript应用程序的静态模块打包工具&#xff0c;可以很方面的管理模块的恶依赖。1.2.1 静态模块此处的静态模块指的是开发阶段&#xff0c;可以被 Webpack 直接引用的资源&#xff08;可以直接被获取打包进bundle.js的资源&#xff09;。当…

上传文件提示java.io.IOException: java.io.FileNotFoundException:(系统找不到指定的路径。)

解决上传文件提示java.io.IOException: java.io.FileNotFoundException&#xff1a;系统找不到指定的路径。前端上传失败效果&#xff1a;后端对应的异常输出信息&#xff1a;此时后端对应的上传关键代码&#xff1a;原因分析&#xff1a;解决方案&#xff1a;transferTo 传入参…

并发设计模式

1、Immutability模式&#xff1a;如何利用不变性解决并发问题&#xff1f;“多个线程同时读写同一共享变量存在并发问题”&#xff0c;这里的必要条件之一是读写&#xff0c;如果只有读&#xff0c;而没有写&#xff0c;是没有并发问题的。解决并发问题&#xff0c;其实最简单的…

【Docker】用开源umami监控你的站点访问量

新年到&#xff0c;祝大家兔年吉祥&#xff01;&#x1f389; 1.介绍 umami是一个开源的站点访问量监看程序&#xff0c;其支持docker部署到自己的服务器上。相比较百度等收费的网站信息监看&#xff0c;这种方式对于小站长来说更加实惠一些 2.docker安装的坑 2.1 docker-co…