Spring源码 - getObjectForBeanInstance()

# Spring源码 - getObjectForBeanInstance()

Spring版本:Spring 5.3.13-release


# 1、getObjectForBeanInstance() - 检测FactoryBean<?>

Spring获取Bean相关源码中,可以发现,每当Spring尝试去获取或者创建一个Bean实例的时候,一旦这个动作完成Spring会立即调用getObjectForBeanInstance()方法。这个方法看似不起眼,其实对于FactoryBean<?>起着非常关键的作用。

因为之前已经写过FactoryBean<?>相关的介绍文章,所以这里对于FactoryBean<?>不做过多的介绍,如果不了解FactoryBean<?>可以查看这篇文章Spring源码 - 核心接口FactoryBean

AbstractBeanFactory#doGetBean()方法片段:

	protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {

		String beanName = transformedBeanName(name);
		Object beanInstance;

		Object sharedInstance = getSingleton(beanName);

		// 如果 Bean 的单例对象找到了, 并且没有创建实例时需要使用的参数
		if (sharedInstance != null && args == null) {
			if (logger.isTraceEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			// 返回对象的实例。
			beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		// 源码过长, 此处省略部分源码
		return adaptBeanInstance(name, beanInstance, requiredType);
	}

正如上面所说的,Spring在使用getSingleton()方法尝试从缓存中获取Bean之后,如果获取到的Bean不为空就会立即调用getObjectForBeanInstance()方法。Spring为何要如此做,原因是因为当Spring无论从哪种方式,哪种scope获取Bean实例之后,都需要对这个Bean进行检查其是否是FactoryBean<?>类型,如果这个BeanFactoryBean<?>类型,则需要回调FactoryBean<?>接口中定义的三个方法进行初始化开发者实现FactoryBean<?>接口对特殊Bean实施的高级定制初始化逻辑。


# 2、getObjectForBeanInstance()检查FactoryBean<?>

AbstractBeanFactory#getObjectForBeanInstance()源码:

protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

		// Don't let calling code try to dereference the factory if the bean isn't a factory.
		// 如果 BeanName 是 FactoryBean<?> 相关的 BeanName
		if (BeanFactoryUtils.isFactoryDereference(name)) {
			// BeanInstance 是 NullBean
			if (beanInstance instanceof NullBean) {
				// 直接返回 beanInstance
				return beanInstance;
			}
			// 如果不是 FactoryBean<?> 类型的 Bean 实例
			if (!(beanInstance instanceof FactoryBean)) {
				// 抛出异常 : 当前 Bean 不是一个工厂
				throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
			}
			// 如果 BeanName 与 FactoryBean<?> 相关 && beanInstance 不是 NullBean
			// && beanInstance 是 FactoryBean<?> 类型 && RootBeanDefinition 不为空
			if (mbd != null) {
				// 当前 Bean 实例为 FactoryBean<?> 类型的 Bean
				mbd.isFactoryBean = true;
			}
			// 直接返回当前 Bean 实例, 这也是为什么针对于一个 FactoryBean<?> 类型的 Bean 实例而言
			// 使用 '&' + beanName 就能获取到 FactoryBean<?> 本身
			return beanInstance;
		}

		// Now we have the bean instance, which may be a normal bean or a FactoryBean.
		// If it's a FactoryBean, we use it to create a bean instance, unless the
		// caller actually wants a reference to the factory.
		// 现在拥有了一个新的 BeanInstance, 这个实例可能是常规 Bean 也有可能是 FactoryBean<?>
		// 如果是 FactoryBean<?> 则使用它创建实例, 但如果是开发者想要直接获取工厂实例而不是工厂的 getObject()
		// 方法对应的实例, 那么传入的 BeanName 应该加入前缀 '&'
		if (!(beanInstance instanceof FactoryBean)) {
			// 如果不是 FactoryBean 直接返回当前 Bean 实例
			return beanInstance;
		}

		// 如果是 FactoryBean
		Object object = null;
		if (mbd != null) {
			mbd.isFactoryBean = true;
		}
		else {
			// 从缓存中加载
			object = getCachedObjectForFactoryBean(beanName);
		}

		// 激活 FactoryBean<?> 的 getObject() 方法
		if (object == null) {
			// Return bean instance from factory.
			// 这里已经明确知道 beanInstance 一定是 FactoryBean<?> 类型
			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;

			// Caches object obtained from FactoryBean if it is a singleton.
			// 如果 mbd 为空 && 从 XML 配置文件中加载的 BeanDefinition 中包含 BeanName 对应的 Bean 定义信息
			if (mbd == null && containsBeanDefinition(beanName)) {
				// 将解析 XML 配置文件的 GenericBeanDefinition 转换为 RootBeanDefinition, 如果指定的 BeanName
				// 是子 Bean 的话同时会合并父类的相关属性
				mbd = getMergedLocalBeanDefinition(beanName);
			}

			boolean synthetic = (mbd != null && mbd.isSynthetic());
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}
  • Spring首先会判断当前beanName是否是FactoryBean<?>类型的BeanNameFactoryBean<?>类型的BeanName&拼接上原始BeanBeanName。即一个BeanNamekapcbBean实现了FactoryBean<?>接口,那么使用kapcb就会从容器中获取到这个Bean,如果需要获取FactoryBean则需要使用&kapcb从容器中获取。所以Spring在对于Bean的检查中第一步做的就是判断当前获取BeanBeanName是否为FactoryBean<?>的类型的BeanName。如果获取的是FactoryBean<?>类型的BeanName则进行相关校验。如果确实是FactoryBean<?>类型并且BeanDefinition不为空,则修改该BeanBeanDefinition中的isFactoryBean属性为true代表该Bean是一个FactoryBean<?>类型的Bean,最后直接返回当前Bean实例。节省性能开销,解析过一次后避免后面重复解析的性能开销。

  • 如果当前beanName不是FactoryBean<?>类型的BeanName,则判断当前Bean实例是否是FactoryBean<?>类型,如果不是FactoryBean<?>类型则直接返回,说明该Bean只是一个常规Bean,则不需要后续处理直接返回。

  • 如果当前BeanFactoryBean<?>类型,则说明当前获取的Bean是实现FactoryBean<?>接口的原始Bean。则需要激活FactoryBean<?>中定义的三个方法对当前Bean进行实例化处理。

可以看到,上面的源码部分非常简单,对于FactoryBean<?>类型Bean的激活Spring则是直接委派给了getObjectFromFactoryBean()方法执行。


# 3、getObjectFromFactoryBean()

FactoryBeanRegistrySupport#getObjectFromFatoryBean()源码:

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
		// 如果是单例 Bean && Bean 实例已经创建过, 没有再次创建的必要, 要保证单例 Bean 全局唯一, 直接从缓存中获取
		if (factory.isSingleton() && containsSingleton(beanName)) {
			// 进入同步代码块
			synchronized (getSingletonMutex()) {
				// 从缓存中获取
				Object object = this.factoryBeanObjectCache.get(beanName);
				// 如果缓存中没有
				if (object == null) {
					// 激活 FactoryBean<?> 中的 getObject() 方法获取开发者
					// 定制的初始化逻辑
					object = doGetObjectFromFactoryBean(factory, beanName);
					// Only post-process and store if not put there already during getObject() call above
					// (e.g. because of circular reference processing triggered by custom getBean calls)
					Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
					if (alreadyThere != null) {
						object = alreadyThere;
					}
					else {
						if (shouldPostProcess) {
							if (isSingletonCurrentlyInCreation(beanName)) {
								// Temporarily return non-post-processed object, not storing it yet..
								return object;
							}
							beforeSingletonCreation(beanName);
							try {
								object = postProcessObjectFromFactoryBean(object, beanName);
							}
							catch (Throwable ex) {
								throw new BeanCreationException(beanName,
										"Post-processing of FactoryBean's singleton object failed", ex);
							}
							finally {
								afterSingletonCreation(beanName);
							}
						}
						if (containsSingleton(beanName)) {
							this.factoryBeanObjectCache.put(beanName, object);
						}
					}
				}
				return object;
			}
		}
		else {
			// 直接获取
			Object object = doGetObjectFromFactoryBean(factory, beanName);
			if (shouldPostProcess) {
				try {
					object = postProcessObjectFromFactoryBean(object, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
				}
			}
			return object;
		}
	}

可以看到,这部分源码也是非常简单。如果该FactoryBean<?>实例是单例类型,那么就必须要保证Spring中单实例Bean的唯一以及使用缓存做到避免额外开销。并且Spring在获取Bean的规则中强调到,尽可能保证所有Bean初始化之后都会激活容器中注册的BeanPostProcessor中的postProcessorAfterBeanInitialization()方法。而真正获取Bean实例则是委托给了doGetObjectFromFactoryBean()方法。


# 4、doGetObjectFromFactoryBean()激活FactoryBean<?>getObject()方法

FactoryBeanRegistrySupport#doGetObjectFromFactoryBean()源码:

private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
		Object object;
		try {
			if (System.getSecurityManager() != null) {
				AccessControlContext acc = getAccessControlContext();
				try {
					object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				// 激活 FactoryBean<?> 中的 getObject() 方法调用
				// 开发者定制化的的 Bean 注册逻辑
				object = factory.getObject();
			}
		}
		catch (FactoryBeanNotInitializedException ex) {
			throw new BeanCurrentlyInCreationException(beanName, ex.toString());
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
		}

		// Do not accept a null value for a FactoryBean that's not fully
		// initialized yet: Many FactoryBeans just return null then.
		if (object == null) {
			if (isSingletonCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(
						beanName, "FactoryBean which is currently in creation returned null from getObject");
			}
			object = new NullBean();
		}
		return object;
	}

上面源码也是非常简单。激活FactoryBean<?>接口中的getObject()方法使用Bean的定制初始化逻辑进行Bean的初始化并返回。

GitHub源码地址https://github.com/kapbc/kapcb-spring-source/tree/master/Spring-Framework-v5.3.13

备注:此文为笔者学习Spring源码的笔记,鉴于本人技术有限,文中难免出现一些错误,感谢大家批评指正。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值