您当前的位置:首页 > 电脑百科 > 程序开发 > 架构

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

时间:2024-01-08 14:22:02  来源:今日头条  作者:互联网高级架构师

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定义,包括RootBeanDefinition和MethodMetadata。 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);

 

作者:FirstMrRight
链接:
https://juejin.cn/post/7320779681673134091



Tags:Spring   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  Search: Spring  点击:(52)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  Search: Spring  点击:(39)  评论:(0)  加入收藏
详解基于SpringBoot的WebSocket应用开发
在现代Web应用中,实时交互和数据推送的需求日益增长。WebSocket协议作为一种全双工通信协议,允许服务端与客户端之间建立持久性的连接,实现实时、双向的数据传输,极大地提升了用...【详细内容】
2024-01-30  Search: Spring  点击:(8)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  Search: Spring  点击:(84)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19  Search: Spring  点击:(86)  评论:(0)  加入收藏
Spring Boot2.0深度实践 核心原理拆解+源码分析
Spring Boot2.0深度实践:核心原理拆解与源码分析一、引言Spring Boot是一个基于Java的轻量级框架,它简化了Spring应用程序的创建过程,使得开发者能够快速搭建一个可运行的应用...【详细内容】
2024-01-15  Search: Spring  点击:(93)  评论:(0)  加入收藏
SpringBoot3+Vue3 开发高并发秒杀抢购系统
开发高并发秒杀抢购系统:使用SpringBoot3+Vue3的实践之旅随着互联网技术的发展,电商行业对秒杀抢购系统的需求越来越高。为了满足这种高并发、高流量的场景,我们决定使用Spring...【详细内容】
2024-01-14  Search: Spring  点击:(90)  评论:(0)  加入收藏
Spring Boot 3.0是什么?
Spring Boot 3.0是一款基于Java的开源框架,用于简化Spring应用程序的构建和开发过程。与之前的版本相比,Spring Boot 3.0在多个方面进行了改进和增强,使其更加易用、高效和灵活...【详细内容】
2024-01-11  Search: Spring  点击:(130)  评论:(0)  加入收藏
GraalVM与Spring Boot 3.0:加速应用性能的完美融合
在2023年,SpringBoot3.0的发布标志着Spring框架对GraalVM的全面支持,这一支持是对Spring技术栈的重要补充。GraalVM是一个高性能的多语言虚拟机,它提供了Ahead-of-Time(AOT)编...【详细内容】
2024-01-11  Search: Spring  点击:(124)  评论:(0)  加入收藏
Spring Boot虚拟线程的性能还不如Webflux?
早上看到一篇关于Spring Boot虚拟线程和Webflux性能对比的文章,觉得还不错。内容较长,抓重点给大家介绍一下这篇文章的核心内容,方便大家快速阅读。测试场景作者采用了一个尽可...【详细内容】
2024-01-10  Search: Spring  点击:(115)  评论:(0)  加入收藏
▌简易百科推荐
对于微服务架构监控应该遵守的原则
随着软件交付方式的变革,微服务架构的兴起使得软件开发变得更加快速和灵活。在这种情况下,监控系统成为了微服务控制系统的核心组成部分。随着软件的复杂性不断增加,了解系统的...【详细内容】
2024-04-03  步步运维步步坑    Tags:架构   点击:(4)  评论:(0)  加入收藏
大模型应用的 10 种架构模式
作者 | 曹洪伟在塑造新领域的过程中,我们往往依赖于一些经过实践验证的策略、方法和模式。这种观念对于软件工程领域的专业人士来说,已经司空见惯,设计模式已成为程序员们的重...【详细内容】
2024-03-27    InfoQ  Tags:架构模式   点击:(13)  评论:(0)  加入收藏
哈啰云原生架构落地实践
一、弹性伸缩技术实践1.全网容器化后一线研发的使用问题全网容器化后一线研发会面临一系列使用问题,包括时机、容量、效率和成本问题,弹性伸缩是云原生容器化后的必然技术选择...【详细内容】
2024-03-27  哈啰技术  微信公众号  Tags:架构   点击:(10)  评论:(0)  加入收藏
DDD 与 CQRS 才是黄金组合
在日常工作中,你是否也遇到过下面几种情况: 使用一个已有接口进行业务开发,上线后出现严重的性能问题,被老板当众质疑:“你为什么不使用缓存接口,这个接口全部走数据库,这怎么能扛...【详细内容】
2024-03-27  dbaplus社群    Tags:DDD   点击:(11)  评论:(0)  加入收藏
高并发架构设计(三大利器:缓存、限流和降级)
软件系统有三个追求:高性能、高并发、高可用,俗称三高。本篇讨论高并发,从高并发是什么到高并发应对的策略、缓存、限流、降级等。引言1.高并发背景互联网行业迅速发展,用户量剧...【详细内容】
2024-03-13    阿里云开发者  Tags:高并发   点击:(5)  评论:(0)  加入收藏
如何判断架构设计的优劣?
架构设计的基本准则是非常重要的,它们指导着我们如何构建可靠、可维护、可测试的系统。下面是这些准则的转换表达方式:简单即美(KISS):KISS原则的核心思想是保持简单。在设计系统...【详细内容】
2024-02-20  二进制跳动  微信公众号  Tags:架构设计   点击:(36)  评论:(0)  加入收藏
详解基于SpringBoot的WebSocket应用开发
在现代Web应用中,实时交互和数据推送的需求日益增长。WebSocket协议作为一种全双工通信协议,允许服务端与客户端之间建立持久性的连接,实现实时、双向的数据传输,极大地提升了用...【详细内容】
2024-01-30  ijunfu  今日头条  Tags:SpringBoot   点击:(8)  评论:(0)  加入收藏
PHP+Go 开发仿简书,实战高并发高可用微服务架构
来百度APP畅享高清图片//下栽のke:chaoxingit.com/2105/PHP和Go语言结合,可以开发出高效且稳定的仿简书应用。在实现高并发和高可用微服务架构时,我们可以采用一些关键技术。首...【详细内容】
2024-01-14  547蓝色星球    Tags:架构   点击:(114)  评论:(0)  加入收藏
GraalVM与Spring Boot 3.0:加速应用性能的完美融合
在2023年,SpringBoot3.0的发布标志着Spring框架对GraalVM的全面支持,这一支持是对Spring技术栈的重要补充。GraalVM是一个高性能的多语言虚拟机,它提供了Ahead-of-Time(AOT)编...【详细内容】
2024-01-11    王建立  Tags:Spring Boot   点击:(124)  评论:(0)  加入收藏
Spring Boot虚拟线程的性能还不如Webflux?
早上看到一篇关于Spring Boot虚拟线程和Webflux性能对比的文章,觉得还不错。内容较长,抓重点给大家介绍一下这篇文章的核心内容,方便大家快速阅读。测试场景作者采用了一个尽可...【详细内容】
2024-01-10  互联网架构小马哥    Tags:Spring Boot   点击:(115)  评论:(0)  加入收藏
站内最新
站内热门
站内头条