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 的加载的主要内容,如果未能解决你的问题,请参考以下文章

Spring源码学习bean的加载

Spring Ioc 源码分析之Bean的加载和构造

10Spring 源码学习 ~ Bean 的加载步骤详解

Spring源码学习~11Bean 的加载步骤详解

Spring源码学习~11Bean 的加载步骤详解

Spring源码学习笔记——Bean加载