本文将介绍Bean的加载过程,即容器中的BeanDefinition
如何在需要时加载为Bean,核心有:
getBean
、doGetBean
的源码逻辑createBean
方法,包括bean实例创建、循环依赖、属性注入、初始化等bean创建的逻辑上一节介绍了xml
形式的配置元数据加载解析到容器中,以BeanDefinition
的形式存在,这一节将研究这些BeanDefinition
将如何进一步使用,也就是从容器中获得Bean
,加载Bean
的过程。
我们研究的入口即BeanFactory
接口的一个方法:
前面讲BeanFactory的各种实现类时,提到过,AbstractBeanFactory
抽象类实现了获取Bean的核心逻辑,即重要的,它的getBean
方法:
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
几个getBean
的重载方法最后都调用了doGetBean
方法:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException
doGetBean
方法可以先思考一下,如果让你设计从容器中获得bean,你要考虑那些?这里简要列一下doGetBean
方法都做了什么事情:
beanName
可能是别名
单例bean只加载一次,需要缓存,尝试从缓存中取
单例bean的循环依赖如何解决
FactoryBean
类型的bean获取特殊处理
BeanFactory容器继承,从父容器中加载
设置了dependsOn
属性,要先加载器依赖的bean,递归
创建bean,根据bean的不同scope,需要不同的创建方式
参数里有一个requiredType
,拿到bean之后进行类型转换
下面将针对doGetBean
方法如何做的以上事情进行分步骤分析
beanName
及别名final String beanName = transformedBeanName(name);
首先,对FactoryBean
的支持(后面**FactoryBean
的使用**中介绍),可能在name中会含有&
,对其进行转换。
另外spring容器提供根据别名获得bean功能,需要转换beanName
,这个方法将使用前面提到的AliasRegistry
接口和SimpleAliasRegistry
实现来找到最规范的别名canonicalName
,其内部维护了一个Map,每个别名最终链式指向canonicalName
,canonicalName
对应指向null。
Object sharedInstance = getSingleton(beanName);
// 允许从第三级缓存即早期引用工厂缓存中获取
getSingleton(beanName, true);
前面BeanFactory实现节提到过,单例bean相关功能操作由SingletonBeanRegistry
接口定义,DefaultSingletonBeanRegistry
类实现,这里便调用了DefaultSingletonBeanRegistry
的Singleton方法:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
// 只有要获取的bean正在创建时,才考虑使用两个为解决循环依赖的缓存。
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 获取之后,放在二级缓存早期引用缓存中
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
这里尝试从DefaultSingletonBeanRegistry
的单例缓存中获取bean,缓存中都没有的话可能返回null,注意判断条件,isSingletonCurrentlyInCreation
,只有要获取的bean正在创建时,才考虑使用两个为解决循环依赖的缓存。
可以看到使用了三个缓存,这里顺便介绍这三个缓存用来干嘛,具体单例bean/工厂/早期bean什么时候放在缓存中的,大胆猜测后面的过程中一定会有。
/** Cache of singleton objects: bean name to bean instance. */
// 即缓存真正的单例bean,name->bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
// 缓存bean创建工厂,ObjectFactory,其实就是一个函数式接口,调用后真正去创建bena
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
// 缓存早期单例bean对象,解决循环依赖用,其中的bean并没有完全初始化完成,property并没有set
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
FactoryBean
的使用FactoryBean
接口用于定义一个bean,通过它的name获取到的是这个FactoryBean
的getObject
()方法返回的对象,对应类型为它FactoryBean
的泛型参数,通过&name
才能获取到真正的FactoryBean
bean。主要是为了在getObject
方法中能够实现更多的编程创建操作,有很多实际应用,如mybatis
的Mapper加载到spring容器中时用到。
所以这里获得bean是要经过处理,在单例缓存中存储的映射是 beanName -> FactoryBean bean
,同样的,这个方法调用在后面每次获得/创建成功bean后均要使用:
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
内部检查了sharedInstance
是不是FactoryBean
,如果不是直接返回。如果是,直接调用getObject
方法,能直接调用吗,不能,需要考虑单例bean的感受啊:
object = getCachedObjectForFactoryBean(beanName);
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
这两个方法均来自前面介绍BeanFactory实现时提到过的提供FactoryBean
支持的**FactoryBeanRegistrySupport
**类,因为可能有单例bean的原因,不能每次都调用getObject
方法,它提供了一个工厂bean的缓存来解决这个问题,同时,这个类中当然也包含了getObject
方法的调用和一些后置处理器的调用。
比较简单,就是几个判断:
parentBeanFactory.getBean(nameToLookup);
dependsOn
属性的解决到了这一步,必须自己创建bean了,首先需要获取BeanDefinition
,
// 解析时即完成了父子beanDefinition的融合
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
一个递归的过程,让它的依赖先加载,如果它的依赖依赖它(使用一个map(name -> set)存储依赖关系),就抛出异常喽:
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(...);
}
registerDependentBean(dep, beanName);
// 有省略
getBean(dep);
}
第一次获得bean,缓存中当然没有啊,尝试从缓存中加载为null后,就要尝试创建了,同样使用DefaultSingletonBeanRegistry
的 getSingleton
的重载方法:
// 将具体的创建方法作为一个函数式接口传入
sharedInstance = getSingleton(beanName, () -> {
try {
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;
}
});
将具体的创建方法作为一个函数式接口传入,交个DefaultSingletonBeanRegistry
进行单例bean的加载/创建。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 要完成bean的创建并加入单例bean缓存中,这里需要进行同步
synchronized (this.singletonObjects) {
// 再次尝试获取,获取不到即可创建
Object singletonObject = this.singletonObjects.get(beanName);
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);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 调用上一步传进来的函数式接口,进行创建,即调用上面提到的createBean方法
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;
}
// 创建后工作,移出正在创建集合
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 如果是新bean,加入到单例bean缓存中,注意这里已经是初始化完成状态的bean
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
在这个getSingleton
方法中,将完成:
createBean
方法,获得新创建的bean这里我在想,还没有用到之前提到的三个缓存,但是最后步骤已经将结果放在了单例bean缓存的singletonObjects
中,即初始化完成的bean集合。
也就是这里只是创建bean的外层结构,真正实例化、解决循环依赖、初始化的过程应该在真正的创建bean过程,也就是传进来的函数式接口中,也就是我们还要回到doGetBean
方法找到上面那个createBean
方法,但是因为单例循环依赖等要在本类DefaultSingletonBeanRegistry
中解决,所以大胆猜测,一会儿我们还会回来调用添加入早期引用工厂方法缓存中和创建早期引用,移除早期引用工厂缓存,加入早期引用缓存等方法。
createBean
方法顾名思义,createBean
将完成bean的创建,也就是BeanDefinition
要加载出bean了,这个方法定义在我们在介绍BeanFactory
实现类中提到的重要实现类**AbstractAutowireCapableBeanFactory
,它完成了初始化前代理检查**、构造、属性依赖注入、初始化等重要操作。
首先,完成一些前期工作,覆盖方法即为后面的lookup-method
和replace-method
功能做准备。
// 获得类型放在bean定义中,应该是要后面反射实例化用的吧
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
mbdToUse.setBeanClass(resolvedClass);
// 准备覆盖方法功能
mbdToUse.prepareMethodOverrides();
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. InstantiationAwareBeanPostProcessor可以在实例化之前创建代理作为目标bean直接返回
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
// 真正的根据bean创建bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
resolveBeforeInstantiation
方法会检查bean是否有实例化前置处理器**InstantiationAwareBeanPostProcessor
**,它的postProcessBeforeInstantiation
方法可能会在bean实例化前便创建一些代理返回,这里如果创建了,将直接使用代理作为bean,并调用实例化的后置处理器,直接返回bean。后面重要的AOP
即通过这个实现。
真正的创建bean方法为doCreateBean
,可以发现规律了,do~才是做事情的方法。
这里将进入比较复杂的创建bean环节,代码量较大,希望我们能够一起读写下去。
深度遍历容易迷失那我们就广度遍历嘛,先来看下doCreateBean
整体上、大概上主要做哪些事情,一会儿再慢慢解析,暂时想不明白不要放弃,往下看完详细咱再回来捋一遍:
createBeanInstance
方法,将BeanDefinition
转换为BeanWrapper
,具体的后面详细说applyMergedBeanDefinitionPostProcessors
,bean定义合并后的处理器执行,Autowired
、已过时的@Required
等注解通过此方法实现注入类型的预解析。populateBean
,属性填充BeanPostProcessor
处理器的bean初始化前方法,调用initMethod
DisposableBean
,如果配置了destroy-method,注册一下下面再把以上doCreateBean
做的事情详细分析下:
createBeanInstance
尝试instanceSupplier
(具体的我也不知道在那里配置)、工厂方法不为空,使用它们创建:
return obtainFromSupplier(instanceSupplier, beanName);
return instantiateUsingFactoryMethod(beanName, mbd, args);
尝试使用解析过的构造方法创建,因为一个类有多个构造函数时,需要根据参数和类型判断最终使用那个构造函数,判断过程比较消耗性能,这里会缓存解析结果。
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
如果没有缓存的解析过的构造方法,说明是第一次,或者解析构造参数和方法调用,或者调用无参构造器:
// 传入参数使用
return autowireConstructor(beanName, mbd, ctors, args);
// 默认参数
return autowireConstructor(beanName, mbd, ctors, null);
// 无参构造器
return instantiateBean(beanName, mbd);
autowireConstructor
带参数实例化参数确定相关逻辑极其复杂:
而无参构造器,则直接根据实例化策略实例化即完成。
关于实例化策略,如果bean没有定义覆盖方法(即前面提到的lookup-method,replace-method),则直接反射实例化,如果定义了,则需要Cglib
动态代理动态织入方法拦截器,返回动态代理。在后续讲AOP时再详细讲。
此时,便成功实例化了Bean。
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// getEarlyBeanReference方法会在需要时,如AOP,生成代理对象
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
单例 & 允许循环依赖 & 当前正在创建,将获得其早期引用的函数式结构放在单例bean工厂缓存中。
当A与B循环依赖时,A的早期引用创建函数被放在工厂中,设置A的属性时会创建B,B设置属性时,会获得到A的早期引用以完成创建,然后B创建完成后,A的属性设置成功,也完成创建。
循环依赖简单的说是这样,但是详细的三级缓存的变化,要结合前面尝试从缓存中获取bean和看完后面联系在一起深入理解。
至于这里为什么不直接创建早期引用放入早期引用缓存,而是使用了一个早期引用工厂缓存存储早期引用创建函数,因为getEarlyBeanReference
里可能使用AbstractAutoProxyCreator
会尝试创建代理对象并缓存起来,后面的初始化操作后置处理器也会使用AbstractAutoProxyCreator
尝试创建代理对象(只有在传入的bean与缓存代理两者不同时才创建),后面会判断如果初始化后置处理器没有创建代理对象,将早期引用执行了后置处理器的代理对象直接覆盖最后要返回的bean,即使用三级缓存解决了代理创建问题和循环依赖问题。
三级缓存使没有循环依赖的bean不需要创建早期引用,因为只将工厂放入了缓存。
populateBean
populateBean(beanName, mbd, instanceWrapper);
属性注入方法也是一个比较复杂的方法,主要做了以下事情:
InstantiationAwareBeanPostProcessor
,如果有将调用postProcessAfterInstantiation
,完成实例化后置处理,可以控制是否继续进行属性填充(联想doCreateBean
调用前是不是类似调用过该BeanPostProcessor
的实例化前置处理)beanName
或type进行自动注入,存入PropertyValues
InstantiationAwareBeanPostProcessor
的postProcessProperties
方法,对属性获取完毕填充前处理属性,已过时的@Required
即在这里完成。applyPropertyValues
将PropertyValues
中的属性填充入BeanWrapper
关于自动注入:
autowireByName
,通过名称注入,即递归的调用我们这篇文章的入口 getBean
autowireByType
,通过类型注入,使用resolveDependency
方法根据类型匹配依赖,根据属性的类型如数组、集合、映射进行不同处理。applyPropertyValues
将进行必要类型转换后设置到BeanWrapper
上。
// 初始化传入原始bean,可能返回代理对象
exposedObject = initializeBean(beanName, exposedObject, mbd);
bean的实例化完成,依赖及相关属性设置完成之后,进入初始化阶段,
BeanNameAware
,BeanFactoryAware
BeanPostProcessor
的初始化前方法init
方法,InitializingBean
的afterPropertiesSet
方法,xml
中的init-method
属性BeanPostProcessor
的初始化后方法if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 后置处理器只会执行一次代理对象创建的,要么在早期引用创建,
// 如果初始化未代理,则直接使用早期引用,因为此时早期引用创建了代理对象
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
// 如果两者不相等,说明初始化创建了代理对象,需要检查依赖?没明白
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
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 " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
回想下前面的早期引用创建工厂,即循环依赖处理节,getEarlyReference
方法,当使用产生循环依赖且AOP
代理时,其将创建返回一个代理对象,而此时bean的初始化initializeBean
方法不会再创建代理(只创建一次),这里需要校验循环依赖做的就是,判断如果没创建代理,直接使用可能创建了代理对象的早期引用,如果初始化过程创建了,需要检查依赖。
保证了使用代理时最后getBean
得到的是需要的代理对象。
DisposableBean
如果该bean实现了DisposableBean
接口或AutoCloseable
接口,或定义了destory-method
方法,将根据它的Scope,使用DisposableBeanAdapter
注册为在销毁时会调用其销毁方法。
createBean
方法调用完成这是我们的createBean
方法调用完成,还记得我们从哪里进来的吗,翻到前面标题为加载单例Bean的一节:
singletonObject = singletonFactory.getObject();
afterSingletonCreation(beanName);
addSingleton(beanName, singletonObject);
创建完成后,将执行移除正在创建集合,将单例bean加入到一级缓存中去,并移除二级和三级缓存。
prototype scope的bean和其它scope的bean其实都调用了上面详细介绍的createBean
方法。
只是prototye
时每次加载bean都重新创建,而scope是使用了Scope接口的不同实现传入包含createBean
的函数接口根据上下文决定是否要创建还是返回,类似于单例bean由**DefaultSingletonBeanRegistry
**管理单例bean,只是管理方式不同,可能有request,session等不同实现管理。
需要注意的是,除了单例bean,其它scope的bean不允许存在循环依赖,因为没有对应的缓存来解决这些问题,如果有循环依赖,创建过程会抛出异常。
至此getBean
方法我们便暂时分析完成了,其中一定还有许多没有详细分析的地方,因为spring是一个功能很强大、实现很复杂的东西,以后也许会再来更新其内容。
《Spring源码深度解析》第二版
spring源码 5.1.x
spring core 文档:
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#spring-core
关于循环依赖与三级缓存:
https://blog.csdn.net/f641385712/article/details/92801300
https://www.despairyoke.com/2019/12/17/2019/2019-12-17-spring-depand/