深入分析-Spring BeanDefinition构造元信息

news2024/9/29 1:30:48

**## Spring BeanDefinition元信息定义方式

Bean Definition是一个包含Bean元数据的对象。它描述了如何创建Bean实例、Bean属性的值以及Bean之间的依赖关系。可以使用多种方式来定义 Bean Definition 元信息,包括:

  1. XML 配置文件:使用<bean>标签定义 Bean 元数据,可以指定 Bean 类型、属性值和依赖项等信息。
  2. 注解:使用@Component@Service@Repository 等注解标记 Bean 类,并使用 @Autowired注解注入依赖项。
  3. Java 配置类:使用@Configuration@Bean注解定义Bean元数据,可以指定 Bean 类型、属性值和依赖项等信息。

除此之外,还可以通过实现 BeanDefinitionRegistryPostProcessor 接口来自定义 Bean Definition 的生成过程。这个接口有一个方法 postProcessBeanDefinitionRegistry(),允许开发人员动态地添加、修改或删除Bean Definition元信息。

XML配置文件定义Bean的元数据

<bean id="user" class="org.thinging.in.spring.ioc.overview.domain.User">
  <property name="id" value="1"/>
  <property name="name" value="Liutx"/>
</bean>

注解定义Bean的元数据

@Service(value = "HelloService")
public class HelloService {

    private final Logger logger = LoggerFactory.getLogger(HelloService.class);

    private final HelloAsyncService helloAsyncService;

        public HelloService(HelloAsyncService helloAsyncService) {
        this.helloAsyncService = helloAsyncService;
    }
}

value = “HelloService” 即为Bean:HelloService的元数据,在构造方法中的依赖关系同样属于元数据。

Java 配置类定义Bean的元数据

@Component(value = "balanceRedisProcessor")
public class BalanceRedisProcessorService implements EntryHandler<Balance>, Runnable {

    @Autowired(required = true)
    public BalanceRedisProcessorService(RedisUtils redisUtils,
                                        CanalConfig canalConfig,
                                        @Qualifier("ownThreadPoolExecutor") Executor executor, RocketMQProducer rocketMqProducer) {
        this.redisUtils = redisUtils;
        this.canalConfig = canalConfig;
        this.executor = executor;
        this.rocketMQProducer = rocketMqProducer;
    }
}

@Component(value = “balanceRedisProcessor”) 是Bean:BalanceRedisProcessorService的元数据,在构造方法中的依赖关系同样属于元数据。

BeanDefinition的元数据解析

在Spring中,无论是通过XML、注解、Java配置类定义Bean元数据,最终都是需要转换成BeanDefinition对象,然后被注册到Spring容器中。

BeanDefinition的创建过程,确实是通过AbstractBeanDefinition及其派生类、``等一系列工具类实现的。

  • 当我们使用XML配置时,Spring会解析XML文件,将其中的Bean元数据信息转换成对应的BeanDefinition对象,然后注册到Spring容器中。在这个过程中,Spring内部会使用XmlBeanDefinitionReader等相关工具类,将XML文件中定义的Bean元数据转换成BeanDefinition对象。
  • 当我们使用注解方式或Java配置类方式定义Bean元数据时,Spring会扫描相应的注解或Java配置类,然后根据其定义生成对应的BeanDefinition对象,并注册到Spring容器中。在这个过程中,Spring内部会使用AnnotationConfigApplicationContext等相关工具类,将注解或Java配置类中定义的Bean元数据转换成BeanDefinition对象。

源码分析XML是如何转化为Spring BeanDefinition的

将xml文件中的配置转为为BeanDefinition需要依赖自XmlBeanDefinitionReader类中的loadBeanDefinitions方法。

选自:Spring Framework 5.2.20 RELEASE版本的XmlBeanDefinitionReader

private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
        new NamedThreadLocal<Set<EncodedResource>>("XML bean definition resources currently being loaded"){
            @Override
            protected Set<EncodedResource> initialValue() {
                return new HashSet<>(4);
            }
        };

/**
 * Load bean definitions from the specified XML file.
 * @param encodedResource the resource descriptor for the XML file,
 * allowing to specify an encoding to use for parsing the file
 * @return the number of bean definitions found
 * @throws BeanDefinitionStoreException in case of loading or parsing errors
 */
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (logger.isTraceEnabled()) {
        logger.trace("Loading XML bean definitions from " + encodedResource);
    }

    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

    if (!currentResources.add(encodedResource)) {
        throw new BeanDefinitionStoreException(
                "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    }

    try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
        InputSource inputSource = new InputSource(inputStream);
        if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
        }
        // 实际上从指定的 XML 文件加载 Bean 定义
        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException(
                "IOException parsing XML document from " + encodedResource.getResource(), ex);
    }
    finally {
        currentResources.remove(encodedResource);
        if (currentResources.isEmpty()) {
            this.resourcesCurrentlyBeingLoaded.remove();
        }
    }
}


//实际上从指定的 XML 文件加载 Bean 定义
/**
 * Actually load bean definitions from the specified XML file.
 * @param inputSource the SAX InputSource to read from
 * @param resource the resource descriptor for the XML file
 * @return the number of bean definitions found
 * @throws BeanDefinitionStoreException in case of loading or parsing errors
 * @see #doLoadDocument
 * @see #registerBeanDefinitions
 */
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {

    try {
        Document doc = doLoadDocument(inputSource, resource);
        int count = registerBeanDefinitions(doc, resource);
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + count + " bean definitions from " + resource);
        }
        return count;
    }
}
  1. 使用ThreadLocal线程级别的变量存储带有编码资源的集合,保证每个线程都可以访问到XmlBeanDefinitionReader在加载XML配置文件时当前正在加载的资源,以确保加载过程中的完整性和正确性。
  2. ThreadLocal中获取到当前正在加载的xml资源,转换为输入流
  3. 开始执行doLoadBeanDefinitions,实际上从指定的 XML 文件加载 Bean 定义,该方法会返回加载的Bean定义数量。
  4. doLoadBeanDefinitions方法中,首先调用doLoadDocument方法加载XML文件并生成一个Document对象。
  5. 然后,调用registerBeanDefinitions方法来注册Bean定义,将其放入Spring容器中。该方法会返回注册的Bean定义数量。
  6. 最后,根据需要记录日志信息,并返回加载的Bean定义数量。

源码分析配置类、注解是如何转化为Spring BeanDefinition的

在Spring中,配置类和注解都可以被转换为Bean定义(BeanDefinition)。下面是关于如何将配置类和注解转换为Bean定义的简要源码分析:

  1. 配置类转换为Bean定义:
    • 当使用Java配置类时,Spring会通过解析配置类中的注解来生成相应的Bean定义。主要实现是通过ConfigurationClassParser类完成的。
    • ConfigurationClassParser会解析配置类上的注解,包括@Configuration@ComponentScan@Bean等,然后将其转换为对应的Bean定义。
    • 在解析过程中,Spring会创建一个ConfigurationClass对象表示配置类,并根据不同的注解类型生成相应的Bean定义,包括RootBeanDefinitionMethodMetadata
    • RootBeanDefinition代表配置类本身,而MethodMetadata代表配置类中的方法上的注解,例如@Bean注解。
    • 最终,这些生成的Bean定义会被注册到DefaultListableBeanFactory中,以供后续的Bean实例化和依赖注入。
  1. 注解转换为Bean定义:
    • 当使用注解方式配置Bean时,Spring会扫描指定的包或类,并解析其中的注解来生成Bean定义。
    • Spring提供了AnnotationBeanDefinitionReader类用于处理注解,它会扫描指定的包路径或类,并根据注解生成相应的Bean定义。
    • 在扫描过程中,AnnotationBeanDefinitionReader会解析常见的注解,比如@Component@Controller@Service@Repository等,然后生成相应的Bean定义。
    • 注解生成的Bean定义同样会被注册到DefaultListableBeanFactory中,以供后续的Bean实例化和依赖注入。

总而言之,无论是配置类还是注解,Spring都会通过解析注解并生成对应的Bean定义,最终将这些Bean定义注册到DefaultListableBeanFactory中。这样,在容器启动时,Spring就能够根据这些Bean定义来实例化Bean并进行依赖注入。

配置类、注解转换为Spring BeanDefition源码后续博客中展示,敬请期待。

如何手动构造BeanDefinition

Bean定义

public class User {

    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + ''' +
                '}';
    }
}

通过BeanDefinitionBuilder构建

//通过BeanDefinitionBuilder构建
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
//通过属性设置
beanDefinitionBuilder.addPropertyValue("id", 1L)
        .addPropertyValue("name","公众号:种棵代码技术树");

//获取BeanDefinition实例
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
// BeanDefinition 并非 Bean 终态,可以自定义修改
System.out.println(beanDefinition);

通过AbstractBeanDefinition以及派生类

// 2. 通过 AbstractBeanDefinition 以及派生类
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
//设置Bean类型
genericBeanDefinition.setBeanClass(User.class);
//通过 MutablePropertyValues 批量操作属性
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("id",1L)
        .add("name","公众号:种棵代码技术树");
// 通过 set MutablePropertyValues 批量操作属性
genericBeanDefinition.setPropertyValues(propertyValues);

后续内容文章持续更新中…

近期发布。


关于我

👋🏻你好,我是Debug.c。微信公众号:种棵代码技术树 的维护者,一个跨专业自学Java,对技术保持热爱的bug猿,同样也是在某二线城市打拼四年余的Java Coder。

🏆在掘金、CSDN、公众号我将分享我最近学习的内容、踩过的坑以及自己对技术的理解。

📞如果您对我感兴趣,请联系我。

若有收获,就点个赞吧,喜欢原图请私信我。

wallhaven-l8r85q.jpg

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

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

相关文章

Qt/QML编程学习之心得:Linux下读写文件File(24)

在Linux嵌入式系统中,经常会使用Qt来读写一个文件,判断一个文件是否存在,具体如何实现呢? 首先,要使用linux系统中相关的头文件: #include <unistd.h> #include <stdio.h> #include <stdlib.h> 其次,判断路径是否存在, if(!dir.exists()){mkdir(…

C#,字符串匹配算法(模式搜索)Z算法的源代码与数据可视化

Z算法也是模式搜索&#xff08;Pattern Search Algorithm&#xff09;的常用算法。 本文代码的运算效果&#xff1a; 一、Z 算法 线性时间模式搜索算法的Z算法&#xff0c;在线性时间内查找文本中模式的所有出现。 假设文本长度为 n&#xff0c;模式长度为 m&#xff0c;那么…

SSR 服务器端渲染:提升用户体验的新趋势(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

关于目标检测中按照比例将数据集随机划分成训练集和测试集

1. 前言 在做目标检测任务的时候&#xff0c;不少网上的数据&#xff0c;没有划分数据集&#xff0c;只是将数据和标签放在不同的文件夹下&#xff0c;没有划分数据集 虽然代码简单&#xff0c;每次重新编写还是颇为麻烦&#xff0c;这里记录一下 如下&#xff0c;有的数据集…

three.js给模型添加标签三种方式对比(矩形平面,精灵图,CSS2DObject)

three.js给模型添加标签三种方式对比&#xff08;矩形平面&#xff0c;精灵图&#xff0c;CSS2DObject&#xff09; vue3实现&#xff0c;代码如下 代码 <template><div class"app"><div ref"canvesRef" class"canvas-wrap"&g…

Android studio 无法创建AIDL文件

Android studio 创建AIDL文件的时候 提示是灰色的无法创建 处理方法在app下面的build.gradle中的buildFeatures 添加 aidl true 这个是 kotlin的写法&#xff0c;如果是使用的旧项目修改下格式就行

The Planets: Mercury

靶场环境 整个靶场的环境&#xff0c;我出现了一点点问题&#xff0c;一直找不到主机的IP地址&#xff0c;后来参考了https://www.cnblogs.com/hyphon/p/16354436.html&#xff0c;进行了相关的配置&#xff0c;最后完成靶机环境的搭建&#xff01; 信息收集 # nmap -sn 192…

第二百四十三回 再分享一个Json工具

文章目录 1. 概念介绍2. 分析与比较2.1 分析问题2.2 比较差异 3. 使用方法4. 内容总结 我们在上一章回中介绍了"分享三个使用TextField的细节"相关的内容&#xff0c;本章回中将再 分享一个Json插件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我…

吉他打谱软件Guitar Pro8苹果Mac电脑简体中文特别版

Guitar Pro 8 Mac是一款吉他编曲学习软件&#xff0c;用于吉他、贝和其他弦乐器的制谱和演奏&#xff0c;这是一个多轨编辑器&#xff0c;具有集成的 MIDI 编辑器、合唱绘图仪、吉他、节拍器和其他音乐家工具。它使您能够编辑吉他、贝司和尤克里里、乐谱、指法谱&#xff0c;并…

qt三大控件

1.QListWidget控件 先在ui界面将 QListWidget拖出来竖直对齐 再去代码中实现文本插入 两种插入方式 方法1 //listWidget使用 有左右中间对齐需求QListWidgetItem * itemnew QListWidgetItem("床前明月光"); // //上面只是独立的一句话,没有关联起来ui-&g…

如何建立标准且有效的评审流程?6个重点

为了进一步提高项目质量&#xff0c;项目评审管理需要遵循一定的标准化流程。而建立标准且有效的评审流程&#xff0c;能够快速提高项目质量和效率&#xff0c;优化团队协作&#xff0c;降低风险&#xff0c;提高项目成功率。如果组织没有建立起标准化的评审流程&#xff0c;就…

C++笔记之cout高亮输出以及纯C++实现一个彩色时钟

C笔记之cout高亮输出以及纯C实现一个彩色时钟 code review! 文章目录 C\笔记之cout高亮输出以及纯C\实现一个彩色时钟一.cout高亮输出1.1.运行1.2.代码一1.3.代码二1.4.重置终端的文本格式到默认设置说明 二.纯C\实现一个彩色时钟2.1.运行2.2.main.cc2.3.cout带颜色打印输出技…

常用Python自动化测试框架有哪些?

随着技术的进步和自动化技术的出现&#xff0c;市面上出现了一些自动化测试框架。只需要进行一些适用性和效率参数的调整&#xff0c;这些自动化测试框架就能够开箱即用&#xff0c;大大节省了测试时间。而且由于这些框架被广泛使用&#xff0c;他们具有很好的健壮性&#xff0…

蓝桥杯练习题(一)

&#x1f4d1;前言 本文主要是【算法】——蓝桥杯练习题&#xff08;一&#xff09;的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 …

ts axios 指定返回值类型,返回数据类型不确定该怎么办 typescript

ts axios 指定返回值类型&#xff0c;返回数据类型不确定该怎么办 typescript 转到 ts 以来&#xff0c;一直有个问题困扰着我&#xff0c;就是每次用 axios 获取数据时&#xff0c;返回值 res 的类型都不能确定&#xff0c;这就导致编辑器一直提示我&#xff1a; 原因 原因是…

哪些软件可以把扫描的表格转成EXCEL

也可点击“软件下载” 一、点击“软件下载”下载安装软件后使用&#xff0c;或直接用网页版添加图片再点击“提交识别”来转换。 二、软件安装成功后将待识别的图片添加进去&#xff0c;点击“识别全部”即可&#xff0c;非常简单。

基于SpringBoot大药房管理系统(程序+数据库+文档)

&#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;摘 要 伴随着全球信息化发展&…

K8S-服务访问

1 Ingress简介 原理解析 Ingress是授权入站连接到达集群服务的规则集合。从外部流量调度到nodeport上的service从service调度到ingress-controlleringress-controller根据ingress[Pod]中的定义&#xff08;虚拟主机或者后端的url&#xff09;根据虚拟主机名直接调度到后端的一…

[java小贴士]关于double类型进行运算时有误差的相等判断的替代方式

double类型在进行运算时会产生误差, 在不能进行相等判断时可以用绝对值在小于某个范围来替代相等; 如果没有进行运算, 是查询得到或者直接赋值则可以正常进行判断相等

1868_C语言单向链表的实现

Grey 全部学习内容汇总&#xff1a; GitHub - GreyZhang/c_basic: little bits of c. 1868_C语言中简单的链表实现 简单整理一下链表的实现&#xff0c;这一次结合前面看到的一些代码简单修改做一个小结。 主题由来介绍 以前工作之中链表的使用其实不多&#xff0c;主要是…