Spring源码解析二十三

上一篇,我们了解了Spring实例化bean的另外一种实例化bean的方式,也就是通过实现FactoryBean接口中的getObject方法,来自定义bean实例化的逻辑。这一篇,我们来看下Spring默认是如何来实例化bean的。
在这里插入图片描述
在第一次调用getBean方法时,缓存中肯定是获取不到bean实例的,所以会走到上图的分支。可以看到,首先会调用方法isProptoTypeCurrentInCreation进行判断,我们到方法isProptoTypeCurrentInCreation中看下:
在这里插入图片描述
可以看到,在方法isProptoTypeCurrentInCreation中有一个集合prototypesCurrentlyInCreation,主要的逻辑就是判断下集合中是否存在beanName。我们知道方法isSingletonCurrentlyInCrreation是用来判断当前spring中,是否存在名称beanName的单例bean正在创建,类似的。方法isPropotypeCurrentInCreation则是用来判断当前spring中,是否存在protoType类型的,且名称为beanName的bean正在创建。Spring创建bean的类型默认为单例,对于单例的bean Spring在容器中仅仅只会保留一份bean的实例,而prototype类型的bean,大家可以理解为每次调用getBean方法时都会去创建一个崭新的bean实例出来。正是因为prototype类型的bean,每次来获取时都会创建一个新的bean实例,所以Spring也就没必要为这种类型的bean进行缓存。对于循环依赖问题,单例类型的bean可以通过三级缓存来解决,但是prototype类型的bean因为是没有缓存的,所以prototype类型的bean是无法解决循环依赖的,哪怕是setter循环依赖。而prototype类型的bean实例,因为是没有缓存存放的,所以,一旦发现名称为beanName的bean正在创建,就会抛出异常来终止本次bean的创建。
在这里插入图片描述如果Spring容器中不存在beanName对应的BeanDefinition,并且当前的Spring容器的父容器是存在的,就会到Spring的父容器中获取bean的实例了。我们先到方法originalBeanName中:
在这里插入图片描述
也就是在转换得到bean的实际名称beanName之后,在判断传进来的name是否有前缀&,如果又的话,就在bean实际名称beanName前添加符号&。通过这个我们应该知道通过方法originalBeanName修饰过的名称,要不就是从spring容器中获取一个普通的bean,要不就是获取一个实现接口FactoryBean的bean实例。
在这里插入图片描述
这个就比较简单了,就是通过父容器获取bean的实例。我们之前的容器初始化是没有设置父容器的,所以不会走这个流程的。

我们继续往后看:
在这里插入图片描述
其中参数typeCheckOnly在方法doGetBean被调用时就会传入,默认值为false。我们到方法markBeanAsCreated看下:
在这里插入图片描述
在方法markBeanAsCreated中,首先会判断alreadyCreated中是否存在beanName,第一次过来里面是没有的。而且,在方法markBeanAsCreated内部是做了double check,这个也是为了避免多线程并发的问题。这里最为关键的是在集合alreadyCreated中添加beanName。
我们继续往下看:
在这里插入图片描述
我们看到了一行关键的代码,通过方法getMergedLocalBeanDefinition的名称,我们可以基本知道方法是用来获取beanName对应的BeanDefinition,但是,在方法中getMergedLocalBeanDefinition返回了类型为 RootBeanDefinition的BeanDefinition。Spring默认是通过GenericBeanDefinition类型的BeanDefinition来封装Bean的信息的。
我们到getMergedLocalBeanDefinition方法中看下:
在这里插入图片描述
在getMergedLocalBeanDefinition方法中也有一个缓存mergedBeanDefinitions,它是用来存放beanName以及相应的BeanDefinition。我们第一次进入缓存中是没有的,也就是mbd为空。
而在调用getMergedBeanDefinition方法的同时也调用了getBeanDefinition,通过传入beanName来获取Spring容器中的BeanDefinition。我们来看下在方法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.
			if (containingBd == null) {
				mbd = this.mergedBeanDefinitions.get(beanName);
			}

			if (mbd == null || mbd.stale) {
				previous = mbd;
				if (bd.getParentName() == null) {
					// Use copy of given root bean definition.
					if (bd instanceof RootBeanDefinition) {
						mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
					}
					else {
						mbd = new RootBeanDefinition(bd);
					}
				}
				else {
					// 处理父类bean的BeanDefinition
					// Child bean definition: needs to be merged with parent.
					BeanDefinition pbd;
					try {
						// 获取父类bean的实际名称
						String parentBeanName = transformedBeanName(bd.getParentName());
						// 如果父类bean的名称和当前bean的名称不同
						if (!beanName.equals(parentBeanName)) {
							// 递归调用,获取父类bean的RootBeanDefinition
							pbd = getMergedBeanDefinition(parentBeanName);
						}
						// 如果父类bean的名称 和当前bean的名称不一样,那就到父类容器中获取BeanDefinition
						else {
							BeanFactory parent = getParentBeanFactory();
							if (parent instanceof ConfigurableBeanFactory) {
								pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
							}
							else {
								throw new NoSuchBeanDefinitionException(parentBeanName,
										"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
										"': cannot be resolved without a ConfigurableBeanFactory parent");
							}
						}
					}
					catch (NoSuchBeanDefinitionException ex) {
						throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
								"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
					}
					// Deep copy with overridden values.
					mbd = new RootBeanDefinition(pbd);
					mbd.overrideFrom(bd);
				}

				// Set default singleton scope, if not configured before.
				// 设置默认的bean 为单例类型
				if (!StringUtils.hasLength(mbd.getScope())) {
					mbd.setScope(SCOPE_SINGLETON);
				}

				// A bean contained in a non-singleton bean cannot be a singleton itself.
				// Let's correct this on the fly here, since this might be the result of
				// parent-child merging for the outer bean, in which case the original inner bean
				// definition will not have inherited the merged outer bean's singleton status.
				if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
					mbd.setScope(containingBd.getScope());
				}

				// Cache the merged bean definition for the time being
				// (it might still get re-merged later on in order to pick up metadata changes)
				if (containingBd == null && isCacheBeanMetadata()) {
					// 将封装好的RootBeanDefinition添加到缓存
					this.mergedBeanDefinitions.put(beanName, mbd);
				}
			}
			if (previous != null) {
				copyRelevantMergedBeanDefinitionCaches(previous, mbd);
			}
			return mbd;
		}
	}

这个方法的逻辑主要涉及了以下几个步骤:
在这里插入图片描述

1、通过创建一个RootBeanDefinition,封装从Spring容器中获取到的BeanDefinition,因为容器中的BeanDefinition是GenericBeanDefinition类型的,是没有记录父子bean标签的关系的,Spring在实例化前需要通过RootBeanDefinition来组织。
在这里插入图片描述
2、如果说当前的bean配置了父类bean,那就先封装父类的bean的RootBeanDefinition,否则的话,直接封装当前bean对应的BeanDefinition即可。
在这里插入图片描述
3、将RootBeanDefinition中的类型设置为单例。
在这里插入图片描述
4、将封装好的RootBeanDefinition添加到缓存中。

我们们接着往下看:
在这里插入图片描述
可以看到,会调用checkMergedBeanDefinition方法,我们进去看下:
在这里插入图片描述
在方法checkMergedBeanDefinition中,只不过是判断下BeanDefinition对应的Bean是否是一个抽象类的BeanDefinition,抽象类是不能实例化对象的,所以就会抛出异常。

我们继续往下看:
在这里插入图片描述
首先会通过getDependsOn方法获取属性dependsOn的值,我们先看下BeanDefinition中的dependsOn是什么:
在这里插入图片描述
比如,我们当前要实例化User对象,而在User对象中配置了属性depends-on的值为User1,表示user的这个bean在实例时,需要依赖User1对象。所以在实例化User之前,Spring会先实例化User1这个对象,其中,BeanDefinition中的getDependsOn方法获取的值就是标签中属性的depends-on的值user1。

我们继续分析下面的代码:
在这里插入图片描述
首先会遍历bean依赖的所有bean的名称dep,我们到方法isDependent中看下:
在这里插入图片描述在这里插入图片描述
接下来会进入到重载方法isDependent中,而且,重载方法中的逻辑也比较简单,首先会从dependentBeanMap中获取beanName对应集合dependentBeans。我们知道dependentBeans其实就是beanName依赖的那些bean名称集合,统一都被存在了集合dependentBeanMap中。接下来就会判断当前bean依赖的dependentBeanName,是否已经存在于beanName依赖的集合dependentBeans中。

我们可以举个例子,假如当前的user依赖的bean的集合元素为:user1、user2。它们存放于集合dependentBeans中,如果dependentBeanName的名称为user1,此时就和集合dependentBeans中的元素重复了,所以就直接返回true了。

除了检查dependentBeanName是否在beanName依赖的范围之内,还会进一步检查:
在这里插入图片描述
接下来会遍历,beanName依赖的那些bean的名称dependentBeans。我们还是以刚才的例子来看,user依赖bean的名称为user1、user2,假如user1依赖的bean集合元素为:user11、user12。此时,我们假设dependentBeanName的值为use11,首先就可以避开beanName直接依赖集合的检查,代码会执行到for循环这里,此时,在第一次遍历时就会发现dependentBeanName的名称,于user1依赖集合中的user11重复了,同样也会返回true。
通过这个我们可以知道,只要传入进来的dependentBeanName在这个beanName 的bean依赖链上,就会返回true。

我们回到前面的方法:
在这里插入图片描述
可以看到,isDependent方法返回结果为true时,就会抛出异常。如果bean配置的依赖都通过了检查,接着会调用registerDependentBean方法:
在这里插入图片描述
可以看到方法registerDependentBean中,会将bean的名称以及依赖的那些bean名称,都注册到相应的缓存中。而且因为dependsOn是当前bean依赖的那些bean,所以在实例化当前bean之前,Spring会提前调用getBean方法来实例化依赖的这些bean。
我们继续往后看:
在这里插入图片描述
首先会判断BeanDefinition中的bean类型是否为单例,在前面我们已经知道了默认就是单例的bean,接下来就会调用方法getSingleton来实例化bean。

我们梳理一下我们今天的流程图:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring源码解析是指对Spring框架的源代码进行深入分析和解读的过程。Spring框架是一个开源的Java企业级应用程序框架,提供了高度灵活和可扩展的开发环境。 Spring框架的源代码解析涉及了众多的模块和组件,包括核心容器、AOP(面向切面编程)、数据访问、Web开发等。通过对这些模块和组件的源代码进行解析,我们可以更加深入地了解Spring框架的工作原理和设计思想。 Spring源码解析的好处在于,可以帮助我们更好地理解Spring框架的各种功能和特性,并且能够帮助开发人员更加高效地使用和定制Spring框架,解决实际项目开发中的问题。 在进行Spring源码解析时,我们可以关注一些关键的概念和类,比如BeanFactory、ApplicationContext、BeanPostProcessor、AOP代理等。这些核心类和概念是理解Spring框架工作机制的重要基础。 进行Spring源码解析时,我们可以使用一些常见的工具和方法,比如IDE(集成开发环境)的调试功能、查看和分析源代码的注释和文档、调试和运行项目的示例代码等。 通过Spring源码解析,我们可以学到很多有关软件开发的知识和经验,比如面向对象编程、设计模式、依赖注入、控制反转等。这些知识和经验对于我们提升自己的技术水平和解决实际项目中的问题都有很大的帮助。 总之,Spring源码解析是一项非常有价值的学习和研究工作,可以帮助我们更好地理解和应用Spring框架,提高自己的技术能力和软件开发水平。希望以上的回答能够满足您的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

youngerone123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值