踩坑记:根据类型获取Spring容器中的Bean
Posted 木叶之荣
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了踩坑记:根据类型获取Spring容器中的Bean相关的知识,希望对你有一定的参考价值。
在项目开发中遇到了这样的一个问题:有同事在BeanFactoryPostProcessor的实现方法中写了类似这样的一段代码:
@Component
public class BeanFactoryPostProcessorImpl implements BeanFactoryPostProcessor
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
//就是这一段代码
beanFactory.getBean(GetBeanByType.class);
然后导致了一些实现了FactoryBean接口的Bean被提前创建了,当时感觉很奇怪,我只是根据传入的Class会获取Bean,为什么会导致一些实现了FactoryBean接口的Bean也被创建呢?其实即使我们是根据类型从Spring容器中获取Bean,但是Spring容器在实现的时候,最底层还是会调用getBean(java.lang.String, java.lang.Class, java.lang.Object…)这个方法获取Bean,那么当我们根据类型从Spring容器中获取Bean的时候就先找到这个类型对应的在Spring容器中的beanName,问题就出在获取这个beanName的地方。真正出问题的是AbstractBeanFactory#isTypeMatch(java.lang.String, org.springframework.core.ResolvableType)这个方法。下面我们来分一下获取beanName的这个过程。
其入口方法为:DefaultListableBeanFactory#getBeanNamesForType(java.lang.Class
public String[] getBeanNamesForType(Class<?> type)
//这里传入的三个参数 type是bean的Class 第二个参数的意思是 是否包含非单例的bean
//第三个参数很重要 这里传入的值为true 这里传入true 代表允许提前初始化Bean
//如果这个值为false的话 代表不允许提前初始化Bean 问题就出在这个值为true!!!
return getBeanNamesForType(type, true, true);
@Override
public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit)
//如果是在容器启动过程中 这个值为false 如果在容器启动完成之后 这个值会变为true 这个值的更改在
//DefaultListableBeanFactory#freezeConfiguration 可以看一下它的调用关系
//下面的内容就很简单了 我们主要分析doGetBeanNamesForType这个方法
if (!isConfigurationFrozen() || type == null || !allowEagerInit)
return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
//这里是从缓存中获取,includeNonSingletons是否包含非单例的方法
Map<Class<?>, String[]> cache =
(includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
String[] resolvedBeanNames = cache.get(type);
if (resolvedBeanNames != null)
return resolvedBeanNames;
resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
if (ClassUtils.isCacheSafe(type, getBeanClassLoader()))
cache.put(type, resolvedBeanNames);
return resolvedBeanNames;
DefaultListableBeanFactory#doGetBeanNamesForType
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit)
List<String> result = new ArrayList<String>();
// Check all bean definitions.
// 注意这里是循环Spring容器中所有的BeanDefinition
for (String beanName : this.beanDefinitionNames)
// Only consider bean as eligible if the bean name
// is not defined as alias for some other bean.
//beanName不是别名
if (!isAlias(beanName))
try
//这里你可以暂时理解为根据BeanName获取BeanDefinition
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Only check bean definition if it is complete.
//下面这个判断比较长 一个一个的来说一下
//mbd.isAbstract()判断这个类是不是抽象类 默认是false
//allowEagerInit 是否允许提前初始化 这个值是传入的值 我们这里传入的值是true 即允许提前初始化
//mbd.hasBeanClass() 是否已经有BeanClass了
//mbd.isLazyInit()是否允许延迟初始化 默认false
//isAllowEagerClassLoading() 是否允许提前加载 上面这四个值是或的关系 只有有一个为true就可以
//requiresEagerInitForType 检查特殊的Bean是否需要提前被初始化 这里传入的是工厂方法的名字
//我们把这个条件总结一下 如果这个Bean不是一个抽象类,并且(被允许提前初始化 或者 (这个Bean的
//BeanDefinition设置了BeanClass了 或者 这个Bean没有设置延迟加载 或者 即使这个Bean标注为延迟加载了,也可以被提前加载(全局变量) 并且 BeanDefinition中的FactoryBeanName是否允许被提前初始化))
//根据我们前面的分析 allowEagerInit 的值为true 这个Bean也不是个抽象类,所以会继续下面的流程
if (!mbd.isAbstract() && (allowEagerInit ||
((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&
!requiresEagerInitForType(mbd.getFactoryBeanName())))
// In case of FactoryBean, match object created by FactoryBean.
// 判断是不是FactoryBean
boolean isFactoryBean = isFactoryBean(beanName, mbd);
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
// 这个地方是判断是不是找到匹配的beanName 这里关键的是isTypeMatch这个方法
// 在这个方法中会导致一些FactoryBean被提前实例化 所以这里给我们的一个提示是:
// 在Spring容器的启动阶段,调用isTypeMatch这个方法去判断Bean的类型的时候要慎重一些。
// 我们要重点分析isTypeMatch这个方法
boolean matchFound =
(allowEagerInit || !isFactoryBean ||
(dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
(includeNonSingletons ||
(dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
isTypeMatch(beanName, type);
//如果类型不匹配 并且还是FactoryBean类型的BeanDefinition
if (!matchFound && isFactoryBean)
// In case of FactoryBean, try to match FactoryBean instance itself next.
//这里讲beanName变为&beanName
beanName = FACTORY_BEAN_PREFIX + beanName;
//继续判断类型匹配不匹配
matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
//如果类型相匹配的话 则将beanName放入到结果集中
if (matchFound)
result.add(beanName);
catch (CannotLoadBeanClassException ex)
catch (BeanDefinitionStoreException ex)
//这一部分的处理流程大致相同
// Check manually registered singletons too.
for (String beanName : this.manualSingletonNames)
try
// In case of FactoryBean, match object created by FactoryBean.
if (isFactoryBean(beanName))
if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type))
result.add(beanName);
// Match found for this bean: do not match FactoryBean itself anymore.
continue;
// In case of FactoryBean, try to match FactoryBean itself next.
beanName = FACTORY_BEAN_PREFIX + beanName;
// Match raw bean instance (might be raw FactoryBean).
if (isTypeMatch(beanName, type))
result.add(beanName);
catch (NoSuchBeanDefinitionException ex)
return StringUtils.toStringArray(result);
下面我们来看看isTypeMatch这个方法的内容
@Override
public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException
//这里是对beanName进行转换 如:如果传入的beanName是&beanName,则将&去掉。如果传入的beanName有别名的话,则替换为别名
String beanName = transformedBeanName(name);
// Check manually registered singletons.
//根据beanName获取bean的实例 前提是其对应的bean已经被实例化了 或者正在实例化中
Object beanInstance = getSingleton(beanName, false);
if (beanInstance != null)
//如果是FactoryBean类型的实例
if (beanInstance instanceof FactoryBean)
//是否要获取在FactoryBean中创建的Bean实例 调用 getObject getObjectType方法
//这个地方的判断条件是 传入的beanName是否以&开头 如果以&开头,则返回true
if (!BeanFactoryUtils.isFactoryDereference(name))
//这里是调用FactoryBean的getObjectType方法 来获取bean的类型
Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
return (type != null && typeToMatch.isAssignableFrom(type));
else
//直接获取FactoryBean类型的实例
return typeToMatch.isInstance(beanInstance);
//判断beanName是否以&开头,如果以&开头的话 则返回true
else if (!BeanFactoryUtils.isFactoryDereference(name))
//如果beanName不是以&开头 则直接进行类型比较
if (typeToMatch.isInstance(beanInstance))
// Direct match for exposed instance?
return true;
//泛型的处理
else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName))
// Generics potentially only match on the target class, not on the proxy...
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
Class<?> targetType = mbd.getTargetType();
if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance) &&
typeToMatch.isAssignableFrom(targetType))
// Check raw class match as well, making sure it's exposed on the proxy.
Class<?> classToMatch = typeToMatch.resolve();
return (classToMatch == null || classToMatch.isInstance(beanInstance));
return false;
else if (containsSingleton(beanName) && !containsBeanDefinition(beanName))
// null instance registered
return false;
// No singleton instance found -> check bean definition.
//这里是从父BeanFactory中进行查找匹配 查找匹配的过程是一样的。
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName))
// No bean definition found in this factory -> delegate to parent.
return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch);
// Retrieve corresponding bean definition.
//根据beanName重新检索BeanDefinition 这里返回一个RootBeanDefinition 如果父子容器中存在相同类型的bean会进行合并
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
//解析出 class的类型
Class<?> classToMatch = typeToMatch.resolve();
if (classToMatch == null)
classToMatch = FactoryBean.class;
//如果传入的class类型为FactoryBean类型 则直接返回FactoryBean类型的数组 否则会将传入的class的类型和FactoryBean组装在一起
Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
new Class<?>[] classToMatch : new Class<?>[] FactoryBean.class, classToMatch);
// Check decorated bean definition, if any: We assume it'll be easier
// to determine the decorated bean's type than the proxy's type.
//这个地方是获取最原始的BeanDefinition 不是组装之后的 RootBeanDefinition
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
//如果dbd 存在并且 beanName不是以&开头
if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name))
RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
//这里是取BeanDefinition中的bean类型
Class<?> targetClass = predictBeanType(dbd.getBeanName(), tbd, typesToMatch);
if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass))
return typeToMatch.isAssignableFrom(targetClass);
//获取目标类型 通常是去BeanDefinition中的resolvedTargetType
Class<?> beanType = predictBeanType(beanName, mbd, typesToMatch);
if (beanType == null)
return false;
// Check bean class whether we're dealing with a FactoryBean.
//如果BeanDefinition中的beanType是FactoryBean类型
if (FactoryBean.class.isAssignableFrom(beanType))
if (!BeanFactoryUtils.isFactoryDereference(name))
// If it's a FactoryBean, we want to look at what it creates, not the factory class.
//就是这个地方 会将FactoryBean类型的Bean进行实例化
//我们来分析这个方法
beanType = getTypeForFactoryBean(beanName, mbd);
if (beanType == null)
return false;
else if (BeanFactoryUtils.isFactoryDereference(name))
// Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean
// type but we nevertheless are being asked to dereference a FactoryBean...
// Let's check the original bean class and proceed with it if it is a FactoryBean.
//这个地方需要注意的是 SmartInstantiationAwareBeanPostProcessor类型的BeanPostProcessor会对返回的Bean类型做修改
beanType = predictBeanType(beanName, mbd, FactoryBean.class);
if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType))
return false;
ResolvableType resolvableType = mbd.targetType;
if (resolvableType == null)
//工厂方法创建的Bean
resolvableType = mbd.factoryMethodReturnType;
if (resolvableType != null && resolvableType.resolve() == beanType)
return typeToMatch.isAssignableFrom(resolvableType);
return typeToMatch.isAssignableFrom(beanType);
isTypeMatch这个方法就是进行各种Bean类型的判断,如是已经实例化的FactoryBean可能会调用它的getObjectType方法获取Bean的类型,或者从BeanDefinition中获取Bean的类型,并且如果是未实例化的FactoryBean,为了进行Bean类型的判断会导致FactoryBean的实例化。
下面我们来看看org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getTypeForFactoryBean这个方法的内容:
protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd)
//工程Bean和工厂方法名 根据工厂方法创建Bean
String factoryBeanName = mbd.getFactoryBeanName();
String factoryMethodName = mbd.getFactoryMethodName();
if (factoryBeanName != null)
if (factoryMethodName != null)
// Try to obtain the FactoryBean's object type from its factory method declaration
// without instantiating the containing bean at all.
BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
if (fbDef instanceof AbstractBeanDefinition)
AbstractBeanDefinition afbDef = (AbstractBeanDefinition) fbDef;
if (afbDef.hasBeanClass())
Class<?> result = getTypeForFactoryBeanFromMethod(afbDef.getBeanClass(), factoryMethodName);
if (result != null)
return result;
// If not resolvable above and the referenced factory bean doesn't exist yet,
// exit here - we don't want to force the creation of another bean just to
// obtain a FactoryBean's object type...
if (!isBeanEligibleForMetadataCaching(factoryBeanName))
return null;
// Let's obtain a shortcut instance for an early getObjectType() call...
//getSingletonFactoryBeanForTypeCheck和getNonSingletonFactoryBeanForTypeCheck这两个方法是创建FactoryBean的实例了
FactoryBean<?> fb = (mbd.isSingleton() ?
getSingletonFactoryBeanForTypeCheck(beanName, mbd) :
getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));
if (fb != null)
// Try to obtain the FactoryBean's object type from this early stage of the instance.
//调用getObjectType方法
Class<?> result = getTypeForFactoryBean(fb);
if (result != null)
return result;
else
// No type found for shortcut FactoryBean instance:
// fall back to full creation of the FactoryBean instance.
return super.getTypeForFactoryBean(beanName, mbd);
if (factoryBeanName == null && mbd.hasBeanClass())
// No early bean instantiation possible: determine FactoryBean's type from
// static factory method signature or from class inheritance hierarchy...
if (factoryMethodName != null)
return getTypeForFactoryBeanFromMethod(mbd.getBeanClass(), factoryMethodName);
else
return GenericTypeResolver.resolveTypeArgument(mbd.getBeanClass(), FactoryBean.class);
return null;
上面这个方法的内容是:如果是以工厂方法来创建Bean的话,则从工厂方法中返回bean类型,如果是FactoryBean类型的Bean的话,会实例化FactoryBean类型的Bean。实例化是在getSingletonFactoryBeanForTypeCheck或getNonSingletonFactoryBeanForTypeCheck方法中完成的。
private FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd)
synchronized (getSingletonMutex())
//先从缓存中获取
BeanWrapper bw = this.factoryBeanInstanceCache.get(beanName);
if (bw != null)
return (FactoryBean<?>) bw.getWrappedInstance();
//这个bean是不是正在创建中
if (isSingletonCurrentlyInCreation(beanName) ||
(mbd.getFactoryBeanName() != null && isSingletonCurrentlyInCreation(mbd.getFactoryBeanName())))
return null;
Object instance = null;
try
// Mark this bean as currently in creation, even if just partially.
//判断是不是正在创建中的bean
beforeSingletonCreation(beanName);
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
//Bean实例化之前的一些处理 由Spring容器中InstantiationAwareBeanPostProcessor类型的实例来完成
instance = resolveBeforeInstantiation(beanName, mbd);
if (instance == null)
//创建Bean的实例 看到这里就很明了了
bw = createBeanInstance(beanName, mbd, null);
instance = bw.getWrappedInstance();
finally
// Finished partial creation of this bean.
afterSingletonCreation(beanName);
FactoryBean<?> fb = getFactoryBean(beanName, instance);
if (bw != null)
this.factoryBeanInstanceCache.put(beanName, bw);
return fb;
按照我们上面的分析,在doGetBeanNamesForType这个方法中,
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit)
allowEagerInit是一个非常重要的参数,这个参数可以控制要不要提前实例化一些Bean。为什么这样说呢?因为要不要调用isTypeMatch这个方法在很大程度上是由这个条件控制的
if (!mbd.isAbstract() && (allowEagerInit ||((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&!requiresEagerInitForType(mbd.getFactoryBeanName())))
通常我们的BeanDefinition都不是抽象类的,所以第一个条件为true,我们来看第二个条件,就是后面的那一大堆
(allowEagerInit ||((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&!requiresEagerInitForType(mbd.getFactoryBeanName()))
这个条件分为两部分,是一个或的条件。如果allowEagerInit 为true的话,则整个条件为true,如果allowEagerInit 为false呢?mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()这三个条件基本上也是为true但是这个地方是一个与的条件,那么还要判断一下requiresEagerInitForType(mbd.getFactoryBeanName())这个方法是返回的true还是false。
private boolean requiresEagerInitForType(String factoryBeanName)
//factoryBeanName不为null isFactoryBean(factoryBeanName) 是否为FactoryBeanBean
//containsSingleton(factoryBeanName)) 如果这个Bean已经被实例化了 这个返回的值为true
return (factoryBeanName != null && isFactoryBean(factoryBeanName) && !containsSingleton(factoryBeanName));
因为我们在这篇文章中的分析是根据类型获取Bean会导致FactoryBean类型的Bean被提前实例化,所以factoryBeanName不为null,isFactoryBean(factoryBeanName) 为true,!containsSingleton(factoryBeanName)同样为true。综合requiresEagerInitForType这个方法返回true,那么
((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&!requiresEagerInitForType(mbd.getFactoryBeanName())
会返回false,那这个这一大段的逻辑也就返回false了。
所以我们在根据类型获取Bean的时候要谨慎一点,如果整个Spring容器已经启动完成之后,根据类型获取Bean是没有问题的,如果是在Spring容器的启动过程中根据类型获取Bean可能会导致一些意想不到的结果。
以上是关于踩坑记:根据类型获取Spring容器中的Bean的主要内容,如果未能解决你的问题,请参考以下文章
Spring cloud stream 3.1 rocketmq踩坑记录
Spring 的 BeanUtils 踩坑记,你是不是遇到过这些问题?