Sping源码(九)—— Bean的初始化(非懒加载)— getMergedLocalBeanDefinition

本文主要是介绍Sping源码(九)—— Bean的初始化(非懒加载)— getMergedLocalBeanDefinition,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

序言

前两篇文章介绍了Bean初始化之前的一些准备工作,包括设置BeanFacroty的ConversionService属性以及将Bean进行冻结。这篇文章将会进入到preInstantiateSingletons方法。进一步了解Bean的初始化流程。

preInstantiateSingletons

	public void preInstantiateSingletons() throws BeansException {// 将所有BeanDefinition的名字创建一个集合List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);// Trigger initialization of all non-lazy singleton beans...// 触发所有非延迟加载单例bean的初始化,遍历集合的对象for (String beanName : beanNames) {// 合并父类BeanDefinitionRootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);//省略部分代码....}}

分析

遍历BeanDefinitionName首先做的第一步是合并父类的beanDefinition。
合并过程大致可以分为以下几个步骤:

  1. mergedBeanDefinitions缓存中获取,缓存中有且不需要重新合并定义,则return
  2. 如果 containingBd 为null,则再次从缓存mergedBeanDefinitions中获取。
  3. 如果缓存中依然没有或者需要重新定义。
    3.1 获取parentName,如果parentName = null则创建(克隆)一个RootBeanDefinition封装当前Bean。
    3.2 parentName != null , 且当前beanName = parentName ,使用父工厂获取parentBeanName对应的合并BeanDefinition赋值给父BeanDefinition
    3.3 parentName != null , 且当前beanName != parentName , 如果父类尚未被加载,且父工厂属于ConfigurableBeanFactory
    调用父工厂进行merge操作。

源码

源码中涉及到很多方法的重载和重写,阅读源码时还要仔细分清。
getMergedLocalBeanDefinition
先尝试从缓存中拿。

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {// Quick check on the concurrent map first, with minimal locking.//先尝试从mergedBeanDefinitions缓存中取RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);//如果缓存中有 && 不需要重新合并定义 则直接returnif (mbd != null && !mbd.stale) {return mbd;}//获取 beanName对应的合并BeanDefinition,如果是ChildBeanDefinition 则需要合并return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));}

getMergedBeanDefinition
整体流程步骤上面大致已经介绍。

protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)throws BeanDefinitionStoreException {synchronized (this.mergedBeanDefinitions) {RootBeanDefinition mbd = null;RootBeanDefinition previous = null;// Check with full lock now in order to enforce the same merged instance.// 先尝试从mergedBeanDefinitions缓存中取if (containingBd == null) {mbd = this.mergedBeanDefinitions.get(beanName);}if (mbd == null || mbd.stale) {previous = mbd;//parentName为null,说明没有父类BeanDefinition需要合并if (bd.getParentName() == null) {// Use copy of given root bean definition.//如果当前的bd是RootBeanDefinition 则直接克隆if (bd instanceof RootBeanDefinition) {mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();}else {//否则则创建RootBeanDefinitionmbd = new RootBeanDefinition(bd);}}else {// Child bean definition: needs to be merged with parent.//parentName不为null,则需要合并BeanDefinition pbd;try {//通过transformedBeanName方法获取parentBean的最终别名String parentBeanName = transformedBeanName(bd.getParentName());//如果当前beanName不等于parentBeanNameif (!beanName.equals(parentBeanName)) {// 获取parentBeanName的"合并的"BeanDefinition赋值给pdbpbd = getMergedBeanDefinition(parentBeanName);}else {//如果当前beanName等于parentBeanName,则获取父类工厂BeanFactory parent = getParentBeanFactory();//如果父类工厂是ConfigurableBeanFactory,则使用父工厂获取parentBeanName对应的合并BeanDefinition赋值给pdbif (parent instanceof ConfigurableBeanFactory) {pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);}}}// Deep copy with overridden values.//生成RootBeanDefinitionmbd = new RootBeanDefinition(pbd);mbd.overrideFrom(bd);}// Set default singleton scope, if not configured before.//设置默认的scopeif (!StringUtils.hasLength(mbd.getScope())) {mbd.setScope(SCOPE_SINGLETON);}if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {mbd.setScope(containingBd.getScope());}//暂时缓存合并的bean定义(稍后可能仍会重新合并以获取元数据更正),如果没有传入包含bean定义 且 当前工厂是同意缓存bean元数据//cacheBeanMetadata:默认为true 代表是缓存bean元数据,还是在每次访问时重新获取它if (containingBd == null && isCacheBeanMetadata()) {this.mergedBeanDefinitions.put(beanName, mbd);}}if (previous != null) {copyRelevantMergedBeanDefinitionCaches(previous, mbd);}return mbd;}}

getMergedBeanDefinition
父类调用的((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);

public BeanDefinition getMergedBeanDefinition(String name) throws BeansException {//获取实际的beanNameString beanName = transformedBeanName(name);// Efficiently check whether bean definition exists in this factory.// 当前beanName在当前工厂的beanDefinitionMap中不存在 && 父工厂 属于 ConfigurableBeanFactoryif (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {//使用父工厂返回beanName的合并BeanDefinition【如有必要,将子bean定义与其父级合并】return ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName);}// Resolve merged bean definition locally.//本地解决合并的bean定义return getMergedLocalBeanDefinition(beanName);}

流程图

在这里插入图片描述

总结

总结:
没有父类就创建一个RootBeanDefinition封装信息、放入缓存后返回。
有父类则调用父类工厂进行merge操作后,同样创建RootBeanDefinition封装父类信息后返回给子类,子类拿到父类返回的BeanDefinition后再次封装进RootBeanDefinition,设置信息后、放入缓存返回。

值得一提的是mergedBeanDefinitions变量,在Spring中有很多类似的应用,将不太变化且经常可以用到的东西放入缓存中,用时先在缓存中获取,包括beanDefinitionMap等变量都是如此。

mergedBeanDefinitions变量其实我们并不是第一次见到,源码中在执行invokeBeanFactoryPostProcessors方法时,会调用beanFactory.getBeanNamesForType方法获取系统中实现了BeanFactoryPostProcessorBeanDefinitionRegistoryPostProcessor的类。

beanFactory.getBeanNamesForType方法中就有beaDefinition的merge操作,感兴趣可以再了解了解。

这篇关于Sping源码(九)—— Bean的初始化(非懒加载)— getMergedLocalBeanDefinition的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除