Spring 容器 —— bean 的加载过程概述

介绍

public Object getBean(String name) throws BeansException {
	return doGetBean(name, null, null, false);
}

获取 bean 我们通常通过 getBean 函数进行调用,而 getBean 函数复用了 doGetBean 函数,我们就从 doGetBean 函数开始研究 bean 的加载过程

源码

protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {
	// 1、转换对应的 beanName
	String beanName = transformedBeanName(name);
	Object bean;

	// 2、检查缓存中或者实例工厂中是否有对应的实例
	// Eagerly check singleton cache for manually registered singletons.
	Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
		...
		// 3、返回对应的实例
		// 有时候返回的不是实例本身二是返回指定方法返回的实例
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}
	else {
		// 4、原型模式下如果存在循环依赖则抛出异常
		// Fail if we're already creating this bean instance:
		// We're assumably within a circular reference.
		if (isPrototypeCurrentlyInCreation(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}

		// 5、如果在已加载类中找不到 beanName,则尝试从 parentBeanFactory 中检测
		// 以递归的方式寻找
		// Check if bean definition exists in this factory.
		BeanFactory parentBeanFactory = getParentBeanFactory();
		if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
			// Not found -> check parent.
			String nameToLookup = originalBeanName(name);
			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 if (requiredType != null) {
				// No args -> delegate to standard getBean method.
				return parentBeanFactory.getBean(nameToLookup, requiredType);
			}
			else {
				return (T) parentBeanFactory.getBean(nameToLookup);
			}
		}

		// 如果不是仅仅做类型检查,而是创建 bean,则需要进行记录
		if (!typeCheckOnly) {
			markBeanAsCreated(beanName);
		}

		try {
			// 6、将从 XML 文件中解析出来的 GernericBeanDefinition 转换为 RootBeanDefinition
			// 如果是子 Bean,则合并父节点的内容
			RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
			checkMergedBeanDefinition(mbd, beanName, args);

			// 7、对于依赖,递归实例化依赖的 bean
			// Guarantee initialization of beans that the current bean depends on.
			String[] dependsOn = mbd.getDependsOn();
			if (dependsOn != null) {
				for (String dep : dependsOn) {
					// 循环依赖,抛出异常
					if (isDependent(beanName, dep)) {
						throw new BeanCreationException(...);
					}
					registerDependentBean(dep, beanName);
					try {
						getBean(dep);
					}
					catch (NoSuchBeanDefinitionException ex) {
						throw new BeanCreationException(...);
					}
				}
			}

			// 8、根据不同 scope 实例化 mbd(RootBeanDefinition)
			// Create bean instance.
			if (mbd.isSingleton()) {
				sharedInstance = getSingleton(beanName, () -> {
					...
					return createBean(beanName, mbd, args);
					...
				});
				bean = 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 = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
			}

			else {
				String scopeName = mbd.getScope();
				if (!StringUtils.hasLength(scopeName)) {
					throw new IllegalStateException(...);
				}
				Scope scope = this.scopes.get(scopeName);
				if (scope == null) {
					throw new IllegalStateException(...);
				}
				try {
					Object scopedInstance = scope.get(beanName, () -> {
						beforePrototypeCreation(beanName);
						try {
							return createBean(beanName, mbd, args);
						}
						finally {
							afterPrototypeCreation(beanName);
						}
					});
					bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
				}
				catch (IllegalStateException ex) {
					throw new BeanCreationException(...);
				}
			}
		}
		catch (BeansException ex) {
			cleanupAfterBeanCreationFailure(beanName);
			throw ex;
		}
	}
	
	// 9、检查类型是否是需要的,按需进行类型转换
	// Check if required type matches the type of the actual bean instance.
	if (requiredType != null && !requiredType.isInstance(bean)) {
		try {
			T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
			if (convertedBean == null) {
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
			return convertedBean;
		}
		catch (TypeMismatchException ex) {
			if (logger.isTraceEnabled()) {
				logger.trace("Failed to convert bean '" + name + "' to required type '" +
						ClassUtils.getQualifiedName(requiredType) + "'", ex);
			}
			throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
		}
	}
	return (T) bean;
}

过程详解

  1. 转换对应 beanName
    传入的参数可能是别名,也可能是 FactoryBean,所以需要解析为实际的 beanName。主要包括以下两种情况:
    - 去除 FactoryBean 中的修饰符,如 name="&aa" 转换为 name=“aa”
    - 取指定 alias 所表示的最终 beanName,如别名 A 指向名称 B,则最终都解析为 B。

  2. 尝试从缓存中加载单例
    单例在 Spring 中只会被加载一次,后续再获取 bean,就直接从缓存中读取。而在创建依赖 bean 的时候,为了避免循环依赖,会将创建 bean 的 ObjectFactory 提前加入到缓存中,这样下一个 bean 创建的时候可以直接使用 ObjectFactory。

  3. bean 的实例化
    从缓存中获取的 bean 是原始状态,并不一定是我们想要的 bean。可能需要调用其中的一些方法获取最终结果。getObjectForBeanInstance 就是完成这个工作的。

  4. 原型模式的依赖检查
    原型模式下如果存在循环依赖则抛出异常

  5. 检测 parentBeanFactory
    缓存中没有且加载的 XML 文件也不包含当前的 bean 数据,那么就需要使用父类工厂加载 bean。

  6. 将从 XML 文件中解析出来的 GernericBeanDefinition 转换为 RootBeanDefinition
    后续转换是基于 RootBeanDefinition 的,所以需要这个转换。如果是子 bean,那么会将父 bean 合入。

  7. 寻找依赖
    寻找当前 bean 的依赖,并初始化依赖。

  8. 针对不同的 scope 进行 bean 的创建
    不同配置有不同的初始化策略。

  9. 类型转换
    通常 requiredType 是空,不需要转换,但是也存在需要的情况,如返回 bean 是 String,但需要 Integer,这时候就需要进行转换了。Spring 提供了各种各样的转换器,用户也可以自己扩展转换器来满足需求。

接下来就是深入到每一个步骤进行分析了。Spring容器 —— 深入 bean 的加载(一).

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值