ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Spring源码 - 获取单例(上)

2022-01-22 19:32:54  阅读:167  来源: 互联网

标签:实例 mbd Spring beanName bean Bean 源码 ex 单例


Spring源码 - 获取单例(上)

Spring版本:Spring 5.3.13-release


# 1、doGetBean()创建Bean实例

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;

		// 尝试从缓存中获取 Bean 实例对象
		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);
		}

		else {
			
            // 源码篇幅过长,省略部分源码
			try {
				if (requiredType != null) {
					beanCreation.tag("beanType", requiredType::toString);
				}

				// 源码篇幅过长,省略部分源码
				// 单实例 Bean 实例化
				if (mbd.isSingleton()) {

					// 返回以 beanName 的(原始)单例对象, 如果未注册, 则使用 SingletonFactory 创建并注册一个对象
					sharedInstance = getSingleton(beanName, () -> {
						try {
							// 为给定的合并后的 BeanDefinition 和创建该 Bean 所需的参数来创建一个 Bean 实例对象
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							// 显示地从单例缓存中删除实例 : 它可能是由创建过程急切地放在那里, 以允许循环依赖解。
							// 还要移除接收到该 Bean 临时引用的任何 Bean
							destroySingleton(beanName);
							// 重新抛出异常
							throw ex;
						}
					});
					// 从 BeanInstance 中获取公开的 Bean 对象, 主要处理 BeanInstance 是 FactoryBean 的情况
					// 如果不是 FactoryBean 会直接返回 beanInstance 实例
					beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
				// 原型模式的创建
				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					// 检查当前 Bean 是否是 FactoryBean<?> 类型的 Bean 实例
					beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}
				else {
					// 指定 Scope 上实例化 Bean
					String scopeName = mbd.getScope();
					if (!StringUtils.hasLength(scopeName)) {
						throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
					}
					Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						// 检查当前 Bean 是否是 FactoryBean<?> 类型的 Bean 实例
						beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new ScopeNotActiveException(beanName, scopeName, ex);
					}
				}
			}
			catch (BeansException ex) {
				beanCreation.tag("exception", ex.getClass().toString());
				beanCreation.tag("message", String.valueOf(ex.getMessage()));
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
			finally {
				beanCreation.end();
			}
		}
		// 对实例化的 Bean 实例进行类型转换
		return adaptBeanInstance(name, beanInstance, requiredType);
	}

doGetBean()方法的源码篇幅较长,总结下来Spring会优先尝试从缓存中获取Bean实例,如果未获取到则需要创建当前beanName对应的Bean实例。对于单实例Bean的加载,Spring则是将详细的加载过程委派给了DefaultSingletonBeanRegistry#getSingleton()方法。


# 2、getSingleton()加载Bean实例

DefaultSingletonBeanRegistry#getSingleton()重载方法源码:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		// 同步全局变量, 保证线程安全
		synchronized (this.singletonObjects) {
			// 这一步是非常有必要的, 否则可能会重复创建 Bean 实例
			Object singletonObject = this.singletonObjects.get(beanName);
			// 如果获取不到对象, 则进行 singleton 的初始化
			if (singletonObject == null) {
				if (this.singletonsCurrentlyInDestruction) {
					throw new BeanCreationNotAllowedException(beanName,
							"Singleton bean creation not allowed while singletons of this factory are in destruction " +
							"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
				}

				// 创建单例之前的回调, 默认实现将单例注册为当前正在创建中, 便于循环依赖的检测
				beforeSingletonCreation(beanName);

				// 表示生成了新单例对象的标记, 默认为 false, 表示没有生成新的单例对象
				boolean newSingleton = false;
				// 有抑制异常记录标记, 没有时为 true, 否则为 false
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);

				// 如果没有抑制异常记录
				if (recordSuppressedExceptions) {
					// 对抑制的异常列表进行实例化
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					// 从单例工厂中获取对象, 调用在 AbstractBeanFactory 中传递进来的 ObjectFactory<T> 函数式接口
					singletonObject = singletonFactory.getObject();

					// 生成了新的单例对象标记为 true, 表示生成了新的单例对象
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						throw ex;
					}
				}
				catch (BeanCreationException ex) {
					if (recordSuppressedExceptions) {
						for (Exception suppressedException : this.suppressedExceptions) {
							ex.addRelatedCause(suppressedException);
						}
					}
					throw ex;
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					// 当 Bean 加载结束后需要移除缓存中对该 Bean 的正在加载状态的记录
					afterSingletonCreation(beanName);
				}
				// 如果是生成新的单例 Bean
				if (newSingleton) {
					// 加入一级缓存
					addSingleton(beanName, singletonObject);
				}
			}
			// 返回加载的 Bean 实例
			return singletonObject;
		}
	}

如果有不了解ObjectFactory<T>接口的可以查看这篇文章:Spring源码 - 核心接口ObjectFactory

  • Spring首先会锁定全局变量,然后尝试从一级缓存中获取,这一步非常重要,避免重复创建。如果获取到了则直接返回。
  • 如果没有从缓存中获取到,首先会将当前beanName对应的Bean实例状态记录为正在创建,便于循环依赖的检测。
  • 调用ObjectFactory<T>接口中的getObject()方法进行Bean的实例化。如果实例化成功则记录当前Bean为新创建的Bean
  • 无论创建成功或者失败,首先移除beanName对应的Bean的正在创建的状态。
  • 如果创建成功则将其加入一级缓存中。同时删除加载Bean过程中所记录的各种辅助状态。
  • 最终返回创建的Bean实例。

需要注意,在调用ObjectFactory<?>接口中的getObject()方法其实质则是激活AbstractAutowireCapableBeanFactory#doCreateBean()方法中的createBean()方法。


# 3、createBean()创建Bean实例

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		if (logger.isTraceEnabled()) {
			logger.trace("Creating instance of bean '" + beanName + "'");
		}
		RootBeanDefinition mbdToUse = mbd;

		// Make sure bean class is actually resolved at this point, and
		// clone the bean definition in case of a dynamically resolved Class
		// which cannot be stored in the shared merged bean definition.
		// 锁定 class, 根据设置的 class 属性或者根据 className 来解析 class
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		// 进行条件筛选, 重新赋值 RootBeanDefinition, 并设置 BeanClass 属性
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			// 重新创建一个 RootBeanDefinition 对象
			mbdToUse = new RootBeanDefinition(mbd);
			// 设置 BeanClass 属性
			mbdToUse.setBeanClass(resolvedClass);
		}

		// Prepare method overrides.
		// 验证及准备覆盖的方法 : lookup-method, replace-method。当需要创建的 Bean 对象中包含了
		// lookup-method 和 replace-method 标签的时候, 会产生覆盖操作
		try {
			mbdToUse.prepareMethodOverrides();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
					beanName, "Validation of method overrides failed", ex);
		}

		try {
			// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
			// 给 BeanPostProcessors 一个机会来返回代理来替代正真的实例, 应用实例化前的前置处理器, 用户自定义动态代理的方式
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
				return bean;
			}
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
					"BeanPostProcessor before instantiation of bean failed", ex);
		}

		try {
			// 实际创建 Bean 调用
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
		catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
			// A previously detected exception with proper bean creation context already,
			// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
		}
	}

  • Spring首先会锁定class,根据设置的class属性或者根据className来解析class
  • overrides属性进行标记以及验证。
  • 激活之前容器中所有注册的BeanPostProcessor,并且解析指定Bean是否存在初始化前的短路操作,AOP功能的实现就是基于这里的判断。
  • 将创建Bean实例委托给doCreateBean()重载方法。

在分析doCreateBean()方法源码之前还需了解Spring的循环依赖,请见Spring源码 - 获取单例(下)

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

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

更多Java技术笔记可扫码关注下方微信公众号。

目目沐沐微信公众号

标签:实例,mbd,Spring,beanName,bean,Bean,源码,ex,单例
来源: https://blog.csdn.net/qq_43911324/article/details/122642180

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有