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

2024-01-07 15:44

本文主要是介绍深入分析-Spring BeanDefinition构造元信息,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

**## 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"){@Overrideprotected 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;}@Overridepublic 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

这篇关于深入分析-Spring BeanDefinition构造元信息的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/580439

相关文章

JSON字符串转成java的Map对象详细步骤

《JSON字符串转成java的Map对象详细步骤》:本文主要介绍如何将JSON字符串转换为Java对象的步骤,包括定义Element类、使用Jackson库解析JSON和添加依赖,文中通过代码介绍... 目录步骤 1: 定义 Element 类步骤 2: 使用 Jackson 库解析 jsON步骤 3: 添

Java中注解与元数据示例详解

《Java中注解与元数据示例详解》Java注解和元数据是编程中重要的概念,用于描述程序元素的属性和用途,:本文主要介绍Java中注解与元数据的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参... 目录一、引言二、元数据的概念2.1 定义2.2 作用三、Java 注解的基础3.1 注解的定义3.2 内

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客

Java中List转Map的几种具体实现方式和特点

《Java中List转Map的几种具体实现方式和特点》:本文主要介绍几种常用的List转Map的方式,包括使用for循环遍历、Java8StreamAPI、ApacheCommonsCollect... 目录前言1、使用for循环遍历:2、Java8 Stream API:3、Apache Commons

JavaScript中的isTrusted属性及其应用场景详解

《JavaScript中的isTrusted属性及其应用场景详解》在现代Web开发中,JavaScript是构建交互式应用的核心语言,随着前端技术的不断发展,开发者需要处理越来越多的复杂场景,例如事件... 目录引言一、问题背景二、isTrusted 属性的来源与作用1. isTrusted 的定义2. 为

Java循环创建对象内存溢出的解决方法

《Java循环创建对象内存溢出的解决方法》在Java中,如果在循环中不当地创建大量对象而不及时释放内存,很容易导致内存溢出(OutOfMemoryError),所以本文给大家介绍了Java循环创建对象... 目录问题1. 解决方案2. 示例代码2.1 原始版本(可能导致内存溢出)2.2 修改后的版本问题在

Java CompletableFuture如何实现超时功能

《JavaCompletableFuture如何实现超时功能》:本文主要介绍实现超时功能的基本思路以及CompletableFuture(之后简称CF)是如何通过代码实现超时功能的,需要的... 目录基本思路CompletableFuture 的实现1. 基本实现流程2. 静态条件分析3. 内存泄露 bug

Java中Object类的常用方法小结

《Java中Object类的常用方法小结》JavaObject类是所有类的父类,位于java.lang包中,本文为大家整理了一些Object类的常用方法,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. public boolean equals(Object obj)2. public int ha

SpringBoot项目中Maven剔除无用Jar引用的最佳实践

《SpringBoot项目中Maven剔除无用Jar引用的最佳实践》在SpringBoot项目开发中,Maven是最常用的构建工具之一,通过Maven,我们可以轻松地管理项目所需的依赖,而,... 目录1、引言2、Maven 依赖管理的基础概念2.1 什么是 Maven 依赖2.2 Maven 的依赖传递机

C#实现系统信息监控与获取功能

《C#实现系统信息监控与获取功能》在C#开发的众多应用场景中,获取系统信息以及监控用户操作有着广泛的用途,比如在系统性能优化工具中,需要实时读取CPU、GPU资源信息,本文将详细介绍如何使用C#来实现... 目录前言一、C# 监控键盘1. 原理与实现思路2. 代码实现二、读取 CPU、GPU 资源信息1.