Spring源码十五:Bean的加载

上一篇我们通过Spring源码十四:Spring生命周期介绍了refresh的最后两个方法,至此通过前面大概十篇左右的篇幅介绍完了Spring容器初始化,接下来,将进入Spring另外一个模块Bean相关的知识点。

在Spring框架中,Bean加载过程是一个复杂且至关重要的环节。在这一节的内容中,我们将探讨如何从BeanDefinition实例化一个Bean,并了解Spring在ApplicationContext初始化时的一些高级特性在Bean加载过程中如何发挥作用。下面是我们的分析步骤:


回到我们最开始的示例代码如下:

java 复制代码
package org.springframework;

import org.springframework.ApplicationContext.MyselfClassPathXmlApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.dto.JmUser;
import org.springframework.event.MyselfEvent;

/**
 * @author Jeremy
 * @version 1.0
 * @description: 主方法
 * @date 2024/6/30 02:58
 */
public class Main {
	public static void main(String[] args) {
		ApplicationContext context = new MyselfClassPathXmlApplicationContext("applicationContext.xml");
		JmUser jmUser = (JmUser)context.getBean("jmUser");
//		System.out.println(jmUser.getName());
//		System.out.println(jmUser.getAge());
		// 发布事件
		MyselfEvent event = new MyselfEvent("事件源:source", "This is a custom event");
		context.publishEvent(event);


	}
}

到目前我们为止,咱们知识跟踪了:

复制代码
ApplicationContext context = new MyselfClassPathXmlApplicationContext("applicationContext.xml");

这一段代码接下来我们看下面的方法:


getBean

判断Spring容器是否激活和关闭,如果测试容器还未激活或者已经关闭则直接抛出异常。毕竟Spring中的bean都是存在容器中的,容器都没有了到哪儿获取bean呢?

接着往下看,通过getBeanFactory先获取到容器,返回的是DefaultListableBeanFactory,前面有分析过它的类图,这里就展开了,在执行的getBeanFactory方法的时候,我特意将BeanDefinitionNames属性展开了,可以这个名字很熟悉。在给大家看下我们的xml配置,这下是不是就很熟悉了呢:

铺垫了这么多就是为了让大家将前面的内容给串联起来,我们在Spring中的每个配置最终都会转化成Spring容器中的属性。好了接下来我们开始正式进入getBean方法。


看到我们进入了BeanFactory的getBean方法,这个时候回去调用doGetBean方法。在Spring中但凡看到do作为前缀的一定要留一个心眼,基本上是真正处理逻辑的地方。

进入doGetBean方法一探究竟:


doGetBean

java 复制代码
/**
	 * Return an instance, which may be shared or independent, of the specified bean.
	 * @param name the name of the bean to retrieve
	 * @param requiredType the required type of the bean to retrieve
	 * @param args arguments to use when creating a bean instance using explicit arguments
	 * (only applied when creating a new instance as opposed to retrieving an existing one)
	 * @param typeCheckOnly whether the instance is obtained for a type check,
	 * not for actual use
	 * @return an instance of the bean
	 * @throws BeansException if the bean could not be created
	 */
	@SuppressWarnings("unchecked")
	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		// 获取转换后的Bean名称
		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		// 检查单例缓存中是否有手动注册的单例Bean
		Object sharedInstance = getSingleton(beanName);
		// 如果单缓存中存在则直接从缓存中获取,
		// 如果不存在就走else分支,进行实例化
		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 + "'");
				}
			}
			// 获取Bean的实例对象
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// 如果当前bean还未被实例化,则在这个判断中准备实例化
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			// 如果bean的类型是prototype且正在创建,直接抛出异常
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// Check if bean definition exists in this factory.
			// 检查Bean定义是否存在于当前工厂
			/// 获取容器的父容器
			BeanFactory parentBeanFactory = getParentBeanFactory();
			// 存在父容器,且当前容器没有beanName的BeanDefinition,则通过父容器获取bean
			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);
				}
			}

			if (!typeCheckOnly) {
				// 标记bean已经开始创建,其实就是将当前beanName 放入alreadyCreated容器中
				markBeanAsCreated(beanName);
			}

			// 进入实例化bean阶段
			try {
				// 合并容器中beanName对应的BeanDefinition,得到新的root类型BeanDefinition
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				// 校验合并后的rootBeanDefinition是否达到实例化标准,abstractFlag默认是false,如果是true则抛出异常
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				// 获取当前beanName所依赖的BeanNames数组
				String[] dependsOn = mbd.getDependsOn();
				// 如果依赖的bean不为空,则注册这些bean且递归调用getBean,提前实例化需要依赖的Bean
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						// 注册依赖的bean
						registerDependentBean(dep, beanName);
						try {
							// 递归调用getBean,需要依赖的Bean先实例化
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}

				// Create bean instance.
				// 判断beanDefinition是单例?
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							//是单例:则开始创建单例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.
							destroySingleton(beanName);
							throw ex;
						}
					});
					// 获取bean实例对象
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				// 如果是原型bean
				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实例对象
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				// 其他bean类型处理
				else {
					String scopeName = mbd.getScope();
					final 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, () -> {
							// 创建其他类型作用域bean的前置准备工作
							beforePrototypeCreation(beanName);
							try {
								// 创建其他类型作用域bean
								return createBean(beanName, mbd, args);
							}
							finally {
								// 后置处理作用
								afterPrototypeCreation(beanName);
							}
						});
						// 获取其他作用域类型实例化对象
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

		// Check if required type matches the type of the actual bean instance.
		// 如果requiredType,且bean类型与入参要求不一致,则需要惊喜转换
		if (requiredType != null && !requiredType.isInstance(bean)) {
			try {
				// 将BeanDefinition转化成指定类型的bean
				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;
	}

在Spring框架中,doGetBean方法是获取Bean实例的核心方法。这个方法涉及很多步骤,包括从缓存中获取单例Bean、处理原型Bean的创建、检查Bean定义、处理循环依赖等。下面我们会想分析refresh方法一样,先整体看下大概内容,再细节到一个一个方法进行解读。

流程总结

  1. 获取转换后的Bean名称:
    ○ 通过transformedBeanName方法获取转换后的Bean名称。
  2. 单例缓存检查:
    ○ 检查单例缓存中是否有手动注册的单例Bean。如果存在并且args为空,则直接从缓存中获取该Bean实例。
    ○ 如果在缓存中找到了Bean实例,并且args为空,则获取该Bean的实际对象并返回。
  3. Bean创建:
    ○ 如果单例缓存中没有找到该Bean实例,或者args不为空,则准备创建该Bean。
    ○ 如果当前Bean是Prototype且正在创建,则抛出异常,避免循环依赖。
    ○ 检查当前Bean的定义是否存在于当前工厂中,如果不存在则向父工厂请求该Bean实例。
  4. 标记Bean创建状态:
    ○ 如果typeCheckOnly为false,则将该Bean标记为已经开始创建。
  5. 合并Bean定义:
    ○ 合并容器中的Bean定义,得到新的Root类型的Bean定义。
  6. 校验合并后的Bean定义:
    ○ 校验合并后的Bean定义是否符合实例化要求,例如是否是抽象Bean。
  7. 处理依赖关系:
    ○ 获取当前Bean依赖的Bean数组,并递归调用getBean方法,提前实例化需要依赖的Bean。
  8. 实例化Bean:
    ○ 根据Bean的作用域(单例、原型或其他)进行实例化。
    ○ 对于单例Bean,获取单例缓存或创建新的单例实例。
    ○ 对于原型Bean,创建新的原型实例。
    ○ 对于其他作用域的Bean,通过作用域获取Bean实例。
  9. 类型转换:
    ○ 如果requiredType不为空且Bean实例的类型与requiredType不一致,则进行类型转换。

doGetBean方法是Spring框架中Bean创建与获取的核心逻辑,实现了复杂的Bean生命周期管理 。通过单例缓存合并Bean定义 、处理依赖关系 以及类型转换等步骤,确保了Bean实例的正确创建与获取。此流程通过细致的检查与处理,避免了循环依赖,并支持多种作用域的Bean管理,为应用提供了灵活且可靠的Bean管理机制。

总结

相关推荐
懒大王爱吃狼1 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
阿伟*rui2 小时前
配置管理,雪崩问题分析,sentinel的使用
java·spring boot·sentinel
XiaoLeisj4 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck4 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
dayouziei4 小时前
java的类加载机制的学习
java·学习
逐·風5 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
Devil枫5 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
Yaml46 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~6 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616886 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端