前言
经过了前一章的分析(spring 源码解析之 doGetBean)),我们是对获取 bean实例有了大致的理解,上篇主要说当Object sharedInstance = getSingleton(beanName)
获取 bean 实例不为空
时,如何从工厂获取最终的实例 bean
。以及说了一下当 bean 不存在,bean 创建前做了一些准备
,如获取父级BeanFactory:getParentBeanFactory
、合并beangetMergedLocalBeanDefinition
、以及 bean 依赖的处理mbd.getDependsOn()
。然后本篇文章才是重头戏,当 bean 缓存不存在的时候,也就是还没创建时,bean 是如何创建的
。接下来开卷!
源码解析
getSingleton
java
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);
// bean 实例为空,则创建单例bean
if (singletonObject == null) {
// 判断,如果当前beanFactory正在被销毁则直接抛出异常,不允许创建单例bean
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 + "'");
}
// 做一些bean创建前的准备工作: 记录beanName 正在加载的状态(添加到 singletonsCurrentlyInCreation 缓存中),
// 若bean已经正在加载,则抛出异常。为了解决循环引用的问题
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
singletonObject = singletonFactory.getObject();
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;
}
// 从singletonsCurrentlyInCreation删除正在记录创建中的beanName
afterSingletonCreation(beanName);
}
// 添加到singletonObjects缓存中 从二级缓存 三级缓存中移除
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
this.singletonObjects.get(beanName)
:再次尝试从缓存中获取bean,若获取到,就大吉大利直接返回。若为空,则需要创建 bean。if (this.singletonsCurrentlyInDestruction)
:未获取到检测bena是否正在销毁,若是则抛出异常beforeSingletonCreation
方法中记录bean正在创建的状态将beanName添加到singletonsCurrentlyInCreation集合中)。在循环依赖时可根据此判断。singletonObject = singletonFactory.getObject()
:调用 ObjectFactory.getObject() 方法来实例化bean- afterSingletonCreation 方法
移除bean
正在夹杂的状态 - addSingleton(beanName, singletonObject): 对
各种缓存状态
做处理。
beforeSingletonCreation
这个方法并非一个空方法
,重点是!this.singletonsCurrentlyInCreation.add(beanName)
, 记录beanName正在加载
的状态(添加到singletonsCurrentlyInCreation
缓存中)
java
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
创建bean - createBean概述
这个方法这行代码才是重头:
singletonObject = singletonFactory.getObject();
这一行代码实则就是调用doGetBean
下的createBean
createBean
是根据给定的bean名称(beanName
)和根bean定义(RootBeanDefinition
对象mbd)创建并初始化
一个Bean实例
,上面一系列的操作都是创建bean 做准备工作的。现在开始真正来创建 bean 了,下面看源码
java
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.
// 1. 锁定class, 根据mdb和 beanName 解析出来 bean的class类型
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
// 此方法的主要作用是验证和准备Bean的方法重写。
// 这个方法会遍历Bean的所有方法,如果一个方法只被重载了一次,那么就设置overloaded为false,以避免参数类型的检查。因为这个检查很耗性能
// 这样在后续调用的时候便可以直接使用找到的方法,而不需要进行方法的参数匹配了。此外,这个方法还可以提前对方法存在性进行验证。
// 为了优化方法的调用,提高程序的运行效率。
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.
// 调用BeanProcessors的方法来替代真正的实例
// 给BeanPostProcessors一个返回代理而不是目标bean实例的机会
// 如果处理器返回了代理,那么就直接返回代理,不再进行后续的初始化操作。
// 这个方法可以让用户使用处理器来创建 bean 实例,不通过 spring 来创建 bean 实例。
//InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation : 在bean初始化前调用
//BeanPostProcessor#postProcessAfterInitialization : 在bean初始化后调用
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 {
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);
}
}
可以看到,createBean 的整体流程大致如下:
-
解析并锁定Bean Class :确保在这一阶段解析出
实际的Bean类
,并在需要时克隆mbd以存储动态解析得到的Class(由于某些原因无法存储在共享合并的bean定义中)。具体做法是调用resolveBeanClass(mbd, beanName)方法。resolveBeanClass(mbd, beanName)
方法用于根据给定的RootBeanDefinition (mbd) 和 beanName 来获取实际的Bean类
(即Java Class对象)- 判断条件resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null表示:
-
resolvedClass != null
: 确保已经成功解析出一个Class对象。!mbd.hasBeanClass()
: 检查当前的RootBeanDefinition是否已经包含了一个已知的Class引用(例如,它可能是从XML或注解配置中读取的)。mbd.getBeanClassName() != null
: 确认RootBeanDefinition中包含了可用于查找类名的字符串信息。- 如果满足这些条件,则创建一个新的RootBeanDefinition实例(mbdToUse),将
原始mbd的其他属性复制过来
,并使用刚解析得到的Class对象(resolvedClass
)来设置新的Bean定义的Class属性
。
这样做的原因在于,原始的RootBeanDefinition可能存储的是一个类名字符串而不是Class对象本身,而在后续处理过程中,有时需要直接操作Class对象进行更高效的操作,比如反射调用、生成代理类等。
-
准备方法重写 :通过调用
mbdToUse.prepareMethodOverrides()
来验证和准备Bean的方法重写
。这个过程会遍历Bean的所有方法,优化后续对方法的调用,提高运行效率
,同时提前进行方法存在性验证。 -
应用BeanPostProcessor :在真正实例化Bean之前,提供一个机会让注册的
InstantiationAwareBeanPostProcessor
或BeanPostProcessor
通过resolveBeforeInstantiation(beanName, mbdToUse)
方法返回代理对象替代目标Bean实例。如果处理器返回了非空代理对象,则直接返回该代理对象,不再继续执行后续的Bean初始化流程。 -
创建并初始化Bean实例 :如果没有
BeanPostProcessor
返回代理对象或者返回的对象为空,函数将调用doCreateBean(beanName, mbdToUse, args)
方法来实际地创建并初始化Bean实例。此方法内部包含了依赖注入、属性填充以及调用初始化方法等一系列操作。 -
异常处理 :在整个创建过程中,函数会捕获并处理可能出现的任何异常,包括但不限于
BeanCreationException
和ImplicitlyAppearedSingletonException
。若出现异常,函数会抛出相应的异常信息,其中包含有关异常发生的上下文,以便于调试定位问题。 最终,当Bean实例成功创建并初始化完成后,函数返回该Bean实例。
prepareMethodOverrides
这里我又理解了好久,主要一开始看到这里说的
方法重写
就想到了方法重载与重写
的那个,其实这里所说的是不一样
的。这里主要是理解
lookup-method
和replaced-method
两个属性与@Override
这玩意是不一样的。
lookup-method
:这种注入方式主要用于注入方法返回结果
,也就是说能通过配置方式替换方法返回结果
。例如,如果有一个单例模式的bean引用了一个原型模式的bean,我们不希望被引用的原型模式bean被缓存,那么这个时候就需要用到lookup-method
注入。replace-method
:这种注入方式可以实现方法主体或返回结果的替换
。例如,如果你想改变某个方法的实现,你可以使用replace-method注入。@Override
:在Java中,当你想要修改继承自父类的某个方法时,可以在子类中重写这个方法。重写的方法必须与父类中的方法有相同的名称、参数列表和返回类型。使用@Override注解可以帮助编译器检查你的方法是否正确地重写了父类的方法。- lookup-method和replace-method:这两种方式是Spring框架提供的,用于在
运行时动态地改变bean的行为
,不知道大家是否记得上一篇(spring 源码解析之 doGetBean)中的AbstractBeanFactory#getObjectFromFactoryBean这个方法
,这里就有提到过我们在缓存中获取到的 bean 都是初始状态的,并非最终状态,我们真正需要的是工厂 bean 中定义的 factory-method
方法中返回的 bean,而getObjectForBeanInstance就是完成这个工作的在前面 bean。
总的来说,@Override是在编译时确定的,它要求子类的方法必须与父类的方法完全相同。而Spring的lookup-method和replace-method是在运行时确定的,它们可以动态地改变方法的行为。
看一下源码
java
public void prepareMethodOverrides() throws BeanDefinitionValidationException {
// Check that lookup methods exist and determine their overloaded status.
// 判断是否有方法需要重写
if (hasMethodOverrides()) {
getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride);
}
}
protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
// 获取对应的类中的对应方法名的个数
int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
// 等于0抛出异常。上面已经验证有方法需要覆盖,这里为0肯定错误
if (count == 0) {
throw new BeanDefinitionValidationException(
"Invalid method override: no method with name '" + mo.getMethodName() +
"' on class [" + getBeanClassName() + "]");
}
else if (count == 1) {
// 标记 MethodOverride 暂未被覆盖,避免参数类型检查的开销
// Mark override as not overloaded, to avoid the overhead of arg type checking.
mo.setOverloaded(false);
}
}
在Spring配置中存在lookup-method和replace-method时,这两个配置在加载xml的时候就会统一存放在BeanDefinition中的methodOverrides属性里。当创建bean的时候,AbstractAutowireCapableBeanFactory类会调用prepareMethodOverride方法来准备方法的覆盖。
知道上面这些概念与MethodOverrides这个属性的来历就很简单了,如果没有需要覆盖的,就不做处理,如果有,则判断是否超过 1 个,等于 1 个就
mo.setOverloaded(false);这个方法主要就是验证并准备给定的方法覆盖。为了之后创建 bean 时,是否需要检测参数。因为在创建 bean 的时候,一个方法可能会有多种入参方式,而最终在实例化的时候就要确定使用哪个构造函数,这个检测就很耗性能,所以做一个标记位
。
resolveBeforeInstantiation
该方法主要是调用 InstantiationAwareBeanPostProcessor 来进行一些处理,这里实际上是给了
用户一次代替Spring来创建bean
的机会,代码实现上非常简单直接调用的后处理器方法,就是有几个概念这里要提一下的。
java
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
// 当前类并非合成类 && 存在 BeanPostProcessor (后处理器)
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
// 1. 获取目标类
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
// 2. 先用实例前的后处理器获取 bean
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
// 3. 前置没有则再使用后处理器应用
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
什么是合成类?什么是非合成类?为什么合成类不能使用BeanPostProcessor进行预处理?
- 合成的含义是:这些构造体是由
Java编译器引入
的,并且在源代码中没有相应的构造体。这些合成构造体通常是编译器为了实现某些特性
(如内部类访问外部类的私有成员)而生成的。例如,当AOP
需要创建一个代理
来拦截某个类的方法调用时,它可能会生成一个新的类(代理类
),这个类在源代码中并不存在,因此它是合成的
。这个代理类会包含一些额外的逻辑,比如在调用目标方法前后插入切面的代码。 - 而非合成的则直接来自
源代码
。 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation()
方法是在Bean实例化之前被调用的,它的主要目的是允许我们在Bean实例化之前进行一些自定义的处理
。但是,对于合成的类,由于它们是由编译器生成的,因此我们可能无法在Bean实例化之前对它们进行有效的处理(这个属于处理器的知识,后面会讲
)。
doCreateBean
见名知意,这是一个创建 bean 的方法,直接下源码
java
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
// 如果是单例,先从缓存中移除
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// 创建 bean 实例,并使用BeanWrapper包装器类来封装这个实例
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 获取 bean 实例
Object bean = instanceWrapper.getWrappedInstance();
// 获取 bean 的类型
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
// 设置目标解析类型
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
// 对合并后的bean定义进行同步锁定,确保其仅被后处理器修改一次
synchronized (mbd.postProcessingLock) {
// 未经过后置处理器处理
if (!mbd.postProcessed) {
try {
// 应用合并bean定义的后处理器,允许对bean的定义进行扩展或修改
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.markAsPostProcessed();
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// 如果bean是单例且允许循环引用,在创建过程中就将bean放入单例缓存中,以解决可能存在的循环依赖问题
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
// 暴露在缓存中的 bean 实例
Object exposedObject = bean;
try {
// 注入属性
populateBean(beanName, mbd, instanceWrapper);
// 初始化bean实例
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {
throw bce;
}
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
}
}
// 处理早期单例曝光后的特殊情况
if (earlySingletonExposure) {
// 从缓存获取 bean 实例,此时缓存中的 bean 实例已经完成了初始化
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 如果暴露的 bean 实例与一开始的原始 bean 实例相同,那么将 exposeObject 替换为 从缓存中获取已经实例化完成的 bean
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
// allowRawInjectionDespiteWrapping:允许在包装后仍然注入原始类型
// hasDependentBean:有依赖当前 bean 的其他 bean
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// 获取依赖当前 bean 的其他 bean
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
// 移除给定bean名称的singleton实例(如果有的话),但前提是它没有用于类型检查以外的其他目的。
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
// 能进来 则证明当前bean 的依赖 bean 还没有创建完成,还是使用的原始的 bean
actualDependentBeans.add(dependentBean);
}
}
// 如果actualDependentBeans不为空,则证明当前 bean 还有依赖 bean 还没有创建完成,那么抛出异常
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
// 根据Scopse 注册bean
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
============================
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
-
处理单例缓存 :首先检查当前要创建的bean是否为单例模式(singleton)。如果是单例,尝试从
factoryBeanInstanceCache
中移除已存在的bean实例。 -
创建bean实例 :如果缓存中没有找到bean实例,则调用
createBeanInstance
方法创建新的bean实例,并使用BeanWrapper包装器类来封装这个实例,以便后续进行属性设置和访问。重点、很难,后面会说
-
类型判断与合并后的bean定义后处理 :
获取bean的实际类型
,并更新合并后的bean定义(RootBeanDefinition)中的目标类型信息。对合并后的bean定义进行同步锁定,确保其仅被后处理器修改一次。然后应用合并bean定义的后处理器
,允许对bean的定义进行扩展或修改
。 -
早期单例曝光 :如果
bean是单例且允许循环引用
,在创建过程中就将bean放入单例缓存中,以解决可能存在的循环依赖问题。通过添加一个工厂方法到单例缓存中,该方法会在需要时返回bean的早期引用
。在上一篇(spring 源码解析之 doGetBean)就有提到三级缓存相关的,这里就是保存至singletonFactories
的地方 -
初始化bean实例 :使用
populateBean
方法完成bean属性的注入,即根据bean定义中的属性配置设置bean的字段值。调用initializeBean
方法对bean执行初始化
逻辑,包括自定义初始化方法的调用等。重点、很难,后面会说
-
处理早期单例曝光后的特殊情况 :在bean已经初始化后,再次检查
早期曝光
的单例情况,处理可能出现的因为bean实际已经被包裹而引起的原始类型的循环引用
问题。- 假设存在如下情况: BeanA和BeanB是两个单例bean,并且
互相引用
。 - 在创建BeanA的过程中,由于检测到BeanB还在创建中(处于"
currently in creation
"状态),因此Spring会将BeanB
的一个早期引用(可能是原始类型
或代理对象
)放入singleton
缓存中,以解决循环引用问题。 - 然后继续完成BeanA的实例化与初始化,接着开始BeanB的实例化过程。
- 当BeanB最终被实例化并初始化完成后,此时需要确保所有依赖于BeanB的bean都使用的是
最终初始化完成后的BeanB实例
,而不是早期曝光
的那个可能为代理或者不完整的实例
。 - 所以在
doCreateBean
方法的末尾部分,Spring会再次检查
这些早期曝光的单例bean。如果发现有其他bean在依赖于当前bean(如BeanB)的原始类型
时,而实际上已经用到了早期曝光的代理对象,则直接 抛异常。 - 这里的关键在于,在处理循环引用时,
Spring必须确保无论何时从缓存中获取bean,都是其完整初始化过的版本
,而不是为了打破循环引用而临时插入的早期引用对象。
- 假设存在如下情况: BeanA和BeanB是两个单例bean,并且
-
注册可销毁bean:最后,如果bean定义中指定了销毁方法,则调用
至此,下一篇将详细解析 doCreateBean,到底是怎么一步步创建bean 的