Spring IOC parentBeanFactory与DependsOn处理

5 篇文章 0 订阅
parentBeanFactory与DependsOn处理概述原型检查isPrototypeCurrentlyInCreation(beanName)parentBeanFactory加载originalBeanName(name)合并BeanDefinitiongetMergedLocalBeanDefinition(beanName)checkMergedBeanDefinitionDepen...
摘要由CSDN通过智能技术生成

概述

else {
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			//3.因为spring只支持解决单例模式下的循环依赖,所以原型模式下会抛出异常。
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}
			// Check if bean definition exists in this factory.
			/**
			 * 执行到这里说明在上面没有获取到bean,分为两种情况
			 * 1.要获取的bean不是单例的
			 * 2.bean是单例,但是还在加载的过程中
			 */
			//4.如果容器中没有找到就从父类容器中加载
			BeanFactory parentBeanFactory = getParentBeanFactory();
			//这里有个问题,什么时候,beanname是不会在BeanDefinition里面的呢
			//这里应该是指不在自己的BeanDefinitionMap里但是在父类的里面
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				//验证方法注入
				//确定原始beanName,如果去掉了前缀就把前缀给加回来
				String nameToLookup = originalBeanName(name);
				//父类若为AbstractBeanFactory,那么委托父类进行加载
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
			}
			//5.表示是否为仅仅进行类型检查获取 Bean 对象。
			// 如果不是仅仅做类型检查,而是创建 Bean 对象,就要执行以下的逻辑
			//这个值是false,代表这个if一定会执行。
			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}

			try {
				//6.从容器中获取beanname对应的 GenericBeanDefinition 对象,
				// 并将其转换为 RootBeanDefinition 对象,转换的原因是RootBeanDefinition是子类
				//它提供了更多的方法供之后使用
				//这里提到的合并BeanDefinition指的是将beanName对应的子类的BeanDefinition
				//与父类RootBeanDefinition的属性融合,得到RootBeanDefinition。可以理解为向上转型
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				//检查给定的合并的BeanDefinition
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				String[] dependsOn = mbd.getDependsOn();
				//7.处理所依赖的bean
				//注意这里的依赖指的并不是循环依赖里面的那个依赖,这个说的应该是@dependsOn这个属性,用来
				//指定Bean加载的顺序的
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						//若给定的依赖bean已经注册为依赖给定的bean
						//指循环依赖的情况,抛出异常
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						//缓存依赖调用.......
						registerDependentBean(dep, beanName);
						try {
							//递归处理依赖bean
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}

什么情况会执行到这里,第一:待获取到bean不是单例 第二:待获取的bean是单例,但是还在创建之中

这段代码主要做了以下的事:

  1. 判断当前bean是否是正在创建的多例bean,因为之后单例bean支持解决循环依赖,如果获取到了正在创建的原型bean直接抛出异常
  2. 如果当前工厂的父工厂不为空,并且当前beanname不存在与BeanDefinitionMap当中,将beanname委托给父类加载
  3. 判断是否是仅仅需要类型检查获取bean对象,如果还需要创建对象,那么需要做记录
  4. mergedBeanDefinitions 中获取 beanName 对应的 RootBeanDefinition 对象。如果这个 BeanDefinition 是子 Bean 的话,则会合并父类的相关属性。
  5. 处理DependsOn属性。这个属性决定了Bean加载的顺序,一定不能出现循环依赖,不然就会抛出异常。

下面将分别解析这个5个地方

原型检查

	//3.因为spring只支持解决单例模式下的循环依赖,所以原型模式下会抛出异常。
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

isPrototypeCurrentlyInCreation(beanName)

	/**
	 * Return whether the specified prototype bean is currently in creation
	 * (within the current thread).
	 * @param beanName the name of the bean
	 * 判断当前正在加载的bean是否是原型,如果是原型就返回true
	 */
	protected boolean isPrototypeCurrentlyInCreation(String beanName) {
		//prototypesCurrentlyInCreation是一个ThreadLocal,所以获取到的是当前线程的私有数据,这个ThreadLocal中
		//是一个set,里面包含了当前正在创建的原型类
		Object curVal = this.prototypesCurrentlyInCreation.get();
		return (curVal != null &&
				(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
	}


/** Names of beans that are currently in creation */
	private final ThreadLocal<Object> prototypesCurrentlyInCreation =
			new NamedThreadLocal<>("Prototype beans currently in creation");

检测方式和单例的检查方式一样,用一个集合保存所有正在加载的bean的名字,然后在这个集合中查找有没有这个beanname。不同的是单例使用的是一个set,原型使用的是ThreadLocal。

parentBeanFactory加载

	//4.如果容器中没有找到就从父类容器中加载
			BeanFactory parentBeanFactory = getParentBeanFactory();
			//这里有个问题,什么时候,beanname是不会在BeanDefinition里面的呢
			//这里应该是指不在自己的BeanDefinitionMap里但是在父类的里面
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				//验证方法注入
				//确定原始beanName,如果去掉了前缀就把前缀给加回来
				String nameToLookup = originalBeanName(name);
				//父类若为AbstractBeanFactory,那么委托父类进行加载
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
			}

这里我不是很懂,为什么beanName会不存在于BeanDefinition中,或者说什么情况下会执行到委托父类进行加载这里来呢。

但是对于本代码来说,containsBeanDefinition(beanName)返回false的时候,就会调用父类的getBean方法进行加载。程序的逻辑比较简单,但是最终都是调用父类的getBean方法来加载。

originalBeanName(name)

	protected String originalBeanName(String name) {
		String beanName = transformedBeanName(name);
		if (name.startsWith(FACTORY_BEAN_PREFIX)) {
			beanName = FACTORY_BEAN_PREFIX + beanName;
		}
		return beanName;
	}

transformedBeanName方法就是doGetBean方法一开始执行的那个方法,它去除了FactoryBean的前缀,处理了别名。

在originalBeanName的方法中,我们将被去除的前缀&又加了回去,然后返回。

这里要理清一个逻辑,就是&的作用是干嘛的

比如在我的实例当中,
<bean id = "car"  class="itheima.Service.CarFactoryBean">
	<property name="carInfo" value="大众SUV,180,180000"></property>
</bean>
是这样声明的,通过调试,这个FactoryBean的beanname是car。我们通过传入getBean("car")表示我们想获取到这个FactoryBean创造的类,但是我们如果用getBean("&car"),那么就代表我们想获得这个FactoryBean的实例。  这两个方向的逻辑处理在getObjectForBeanInstance方法中完成的。

所以我们在向上委托父类的时候,一定要将name恢复到原状。

合并BeanDefinition

	//6.从容器中获取beanname对应的 GenericBeanDefinition 对象,
				// 并将其转换为 RootBeanDefinition 对象,转换的原因是RootBeanDefinition提供了更多的方法供之后使用
				//这里提到的合并BeanDefinition指的是将beanName对应的子类的BeanDefinition
				//与同为子类的RootBeanDefinition的属性融合,得到RootBeanDefinition。可以理解为类型转换一下
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				//检查给定的合并的BeanDefinition
				checkMergedBeanDefinition(mbd, beanName, args);

getMergedLocalBeanDefinition(beanName)

	protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
		// Quick check on the concurrent map first, with minimal locking.
		//快速从缓存中获取
		RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
		if (mbd != null) {
			return mbd;
		}
		//缓存中没有再调用getMergedBeanDefinition,进行合并
		return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
	}

  • 首先快速到缓存中获取,如果没有
  • 调用getBeanDefinition(beanname)获取到BeanDefinition之后,传入getMergedBeanDefinition,进行合并返回。 若获取的 BeanDefinition 为 BeanDefinition,则需要合并父类的相关属性。
  • getMergedBeanDefinition方法的源码这里不深入解析了。

checkMergedBeanDefinition

	protected void checkMergedBeanDefinition(RootBeanDefinition mbd, String beanName, @Nullable Object[] args)
			throws BeanDefinitionStoreException {

		if (mbd.isAbstract()) {
			throw new BeanIsAbstractException(beanName);
		}
	}

检查获取到得RootBeanDefinition。

DependsOn处理

如果A DependsOn B,那么处理化A之前一定会先初始B。这就是解决DependsOn依赖。

	//7.处理所依赖的bean
				//注意这里的依赖指的并不是循环依赖里面的那个依赖,这个说的应该是@dependsOn这个属性,用来
				//指定Bean加载的顺序的
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						//若给定的依赖bean已经注册为依赖给定的bean
						//指循环依赖的情况,抛出异常
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						//缓存依赖调用.......
						registerDependentBean(dep, beanName);
						try {
							//递归处理依赖bean
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}这里的代码逻辑比较绕,我们先理解一下等一下要用到的两个Map
	/** Map between dependent bean names: bean name --> Set of dependent bean names */
	private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);

	/** Map between depending bean names: bean name --> Set of bean names for the bean's dependencies */
	private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
  • 第一个map保存bean-----依赖这个bean的bean集合
  • 第二个map保存bean-----这个bean依赖的bean集合

比如A DependsOn B 和 C

那么第一个集合中保存 B —>(A) C---->(A)

第二个集合中保存 A ---->(B,C)

然后接着往下看

isDependent

// DefaultSingletonBeanRegistry.java

private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
    // alreadySeen 已经检测的依赖 bean
    if (alreadySeen != null && alreadySeen.contains(beanName)) {
        return false;
    }
    // 获取原始 beanName
    String canonicalName = canonicalName(beanName);
    // 获取当前 beanName 的依赖集合
    Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
    if (dependentBeans == null) {
        return false;
    }
    // 存在,则证明存在已经注册的依赖
    if (dependentBeans.contains(dependentBeanName)) {
        return true;
    }
    // 递归检测依赖
    for (String transitiveDependency : dependentBeans) {
        if (alreadySeen == null) {
            alreadySeen = new HashSet<>();
        }
        // 添加到 alreadySeen 中
        alreadySeen.add(beanName);
        // 递推
        if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
            return true;
        }
    }
    return false;
}

这个代码挺绕的,不过使用一个实例调试一下就会清楚很多。

registerDependentBean

public void registerDependentBean(String beanName, String dependentBeanName) {
    // 获取 beanName
    String canonicalName = canonicalName(beanName);

    // 添加 <canonicalName, <dependentBeanName>> 到 dependentBeanMap 中
    synchronized (this.dependentBeanMap) {
        Set<String> dependentBeans =
                this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
        if (!dependentBeans.add(dependentBeanName)) {
            return;
        }
    }

    // 添加 <dependentBeanName, <canonicalName>> 到 dependenciesForBeanMap 中
    synchronized (this.dependenciesForBeanMap) {
        Set<String> dependenciesForBean =
                this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
        dependenciesForBean.add(canonicalName);
    }
}

到了这里为了解释清楚这里解决依赖的逻辑和检测循环依赖,可以举一个例子。

比如这里有一个A -->B ,然后B—>A.

开始isDependent(A,B)必定返回false,然后到了registerDependentBean中,注册为

dependentBeanMap:B—>(A)

dependenciesForBeanMap:A—>(B)

然后就调用getBean创建B去了。B也会执行上面的流程

在isDependent(B,A)中

if (dependentBeans.contains(dependentBeanName)) {
return true;
}

这个语句就会成立,就会返回true,然后就会抛出异常。这是比较简单的一种情况。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值