9Spring 源码学习 ~ Bean 的加载
Posted 戴泽supp
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了9Spring 源码学习 ~ Bean 的加载相关的知识,希望对你有一定的参考价值。
Bean 的加载
一、整体时序图
1、入口:
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beanfactory-test.xml"));
TestBean testBean = (TestBean) beanFactory.getBean("testBean");
其中获取 bean 的函数 beanFactory#getBean 就是我们的入口,跟踪入口,可以看到源码如下:
AbstractBeanFactory#getBean
@Override
public Object getBean(String name) throws BeansException
return doGetBean(name, null, null, false);
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException
//提取对应的 beanName
String beanName = transformedBeanName(name);
Object beanInstance;
// Eagerly check singleton cache for manually registered singletons.
// 检查缓存中或者实例工厂中是否有对应的实例
// 为什么首先会使用这段代码呢?
// 因为在创建单例 bean 的时候,会存在依赖注入的情况,而创建依赖的时候,为了避免循环依赖
// Spring 创建 bean 的原则是不等 bean 创建完成,就会将创建 bean 的 ObjectFactory 提早曝光
// 也就是将 ObjectFactory 加入到缓存中,一旦下个 bean 创建的时候,需要依赖上个 bean 则直接使用 ObjectFactory
// 下面代码是尝试直接从缓存中获取或者 singletonFactories 中的 ObjectFactory 中获取
Object sharedInstance = getSingleton(beanName);
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 + "'");
//返回对应的实例,有时候存在诸如 BeanFactory 的情况,并不是直接返回实例本身,而是返回指定方法返回的实例
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
else
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
//只有单例情况下,才会尝试解决循环依赖.
//如果存在 A 中有属性 B,B 中又有属性 A,那么当依赖注入的时候,
//就会产生当 A 还未创建完成的时候,由于创建 A 需要先创建 B,而创建 B 又需要先创建 A,
//就造成了循环依赖,也就是下面 isPrototypeCurrentlyInCreation(beanName) 为 true
if (isPrototypeCurrentlyInCreation(beanName))
throw new BeanCurrentlyInCreationException(beanName);
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
//如果 beanDefinitionMap 中(即所有已加载的类中)不包括 beanName,
//则尝试从 parentBeanFactory 中检测
if (parentBeanFactory != null && !containsBeanDefinition(beanName))
// Not found -> check parent.
//递归到 BeanFactory 中寻找
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.
//无参,委托给标准的 getBean 方法
return parentBeanFactory.getBean(nameToLookup, requiredType);
else
return (T) parentBeanFactory.getBean(nameToLookup);
//如果不是仅仅做类型检查,而是创建 bean,这里要进行记录
if (!typeCheckOnly)
markBeanAsCreated(beanName);
StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
.tag("beanName", name);
try
if (requiredType != null)
beanCreation.tag("beanType", requiredType::toString);
//将存储 XML 配置文件的 GernericBeanDefinition 转换为 RootBeanDefinition,
//如果指定 beanName 是子 Bean 的话,同时会合并父类的相关属性
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
//保证当前 bean 所依赖的 bean 的初始化
String[] dependsOn = mbd.getDependsOn();
//若存在依赖,则需要递归实例化依赖的 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 + "'");
//缓存依赖调用
registerDependentBean(dep, beanName);
try
getBean(dep);
catch (NoSuchBeanDefinitionException ex)
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
// Create bean instance.
//实例化依赖的 bean 后,便可以实例化 mbd 本身了
//singleton 模式的创建
if (mbd.isSingleton())
//单例模式是线程共享的
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;
);
beanInstance = 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);
beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
else
//指定的 scope 上实例化 bean
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName))
throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
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, () ->
beforePrototypeCreation(beanName);
try
return createBean(beanName, mbd, args);
finally
afterPrototypeCreation(beanName);
);
beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
catch (IllegalStateException ex)
throw new ScopeNotActiveException(beanName, scopeName, ex);
catch (BeansException ex)
beanCreation.tag("exception", ex.getClass().toString());
beanCreation.tag("message", String.valueOf(ex.getMessage()));
cleanupAfterBeanCreationFailure(beanName);
throw ex;
finally
beanCreation.end();
return adaptBeanInstance(name, beanInstance, requiredType);
<T> T adaptBeanInstance(String name, Object bean, @Nullable Class<?> requiredType)
// Check if required type matches the type of the actual bean instance.
//检查需要的类型师傅符合 bean 的实际类型
if (requiredType != null && !requiredType.isInstance(bean))
try
Object convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null)
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
return (T) 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;
仅从代码量就能看出来,bean 的加载是一个相当复杂的过程。
2、时序图
下面我们来一步步分析 Spring 加载 bean 的整个过程。
二、转换对应的 beanName
String beanName = transformedBeanName(name);
传入参数 name,它有很多种形式,可能是别名,也可能是 FactoryBean,因此需要进行一系列的解析。如下:
- 1、去除 FactoryBean 的修饰符,也就是如果 name=“&aa”,name 会去掉 &,而使 name=“aa”
- 2、取指定 alias 所表示的最终 beanName,如别名 A 指向名称为 B 的 bean,则返回B,若别名 A 指向别名 B,别名 B 又指向名称为 C 的 bean,则返回 C
源码如下:
protected String transformedBeanName(String name)
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
//canonical -> 典范(规定的名称)
public String canonicalName(String name)
String canonicalName = name;
// Handle aliasing...
String resolvedName;
do
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null)
canonicalName = resolvedName;
while (resolvedName != null);
return canonicalName;
BeanFactoryUtils#transformedBeanName 源码如下:
private static final Map<String, String> transformedBeanNameCache = new ConcurrentHashMap<>();
//String FACTORY_BEAN_PREFIX = "&";
public static String transformedBeanName(String name)
Assert.notNull(name, "'name' must not be null");
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX))
return name;
return transformedBeanNameCache.computeIfAbsent(name, beanName ->
do
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
);
三、尝试从缓存中加载单例
Object sharedInstance = getSingleton(beanName);
单例在 Spring 的容器中只会被创建一次,后续获取直接从单例缓存中获得。这里是首先尝试从缓存中加载,如果不成功则再次尝试从 singletonFactories 中加载。为什么这么处理呢?主要是为了解决循环依赖的问题,后面会有单独章节重点介绍这一块,暂时先保留这个疑问。
源码如下:
DefaultSingletonBeanRegistry.java
@Override
@Nullable
public Object getSingleton(String beanName)
return getSingleton(beanName, true);
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference)
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName))
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference)
synchronized (this.singletonObjects)
// Consistent creation of early reference within full singleton lock
//为保证一致性,加锁创建单例实例,担心有其他线程已创建,故加锁后重新查询一遍
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null)
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null)
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null)
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
return singletonObject;
四、bean 的实例化
如果从缓存中得到了 bean 的原始状态,就需要对 bean 进行实例化。这里返回的只是 bean 最原始的状态。
举例说明:
假如我们需要对工厂 bean 进行处理,这里得到的是工厂 bean 的初始状态,而我们真正需要的是工厂 bean 中定义的 factory-method 方法中返回的 bean。而 getObjectForBeanInstance 就是完成这个工作的,后续会详解。
源码如下:
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd)
// Don't let calling code try to dereference the factory if the bean isn't a factory.
if (BeanFactoryUtils.isFactoryDereference(name))
if (beanInstance instanceof NullBean)
return beanInstance;
if (!(beanInstance instanceof FactoryBean))
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
if (mbd != null)
mbd.isFactoryBean = true;
return beanInstance;
五、原型模式的依赖检查
只有在单例模式的情况下,才会尝试解决循环依赖。
循环依赖
如果存在 A 中有属性 B,B 中又有属性 A,那么当依赖注入的时候,就会产生当 A 还未创建完成的时候,由于创建 A 需要先创建 B,而创建 B 又需要先创建 A,就造成了循环依赖,即 isPrototypeCurrentlyInCreation(beanName) 为 true。
if (isPrototypeCurrentlyInCreation(beanName))
throw new BeanCurrentlyInCreationException(beanName);
protected boolean isPrototypeCurrentlyInCreation(String beanName)
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(以上是关于9Spring 源码学习 ~ Bean 的加载的主要内容,如果未能解决你的问题,请参考以下文章