spring源码日记09: getBean

所有文章已迁移至csdn,csdn个人主页https://blog.csdn.net/chaitoudaren

MyBean myTestBean = (MyBean) bf.getBean("myBean");

@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
    return doGetBean(name, requiredType, null, false);
}

先打个预防针,以下代码第一次看肯定会一头雾水。这部分代码更像是总纲,很多地方都需要在后面详细讲解。因此一头雾水不要紧,多看几遍,然后往后面看,之后回过头来会清楚的多

// AbstractBeanFactory.java
    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

        /**
         * 1. 转换beanName,存在一下2种情况
         *      1.当获取的是工厂而非bean时,beanName会带上&,此时需要先去掉&
         *      2.如果是别名,将别名转化为真实的beanName
         */
        final String beanName = transformedBeanName(name);
        Object bean;

        // Eagerly check singleton cache for manually registered singletons.
        // 2. 尝试从缓存中获取bean
        Object sharedInstance = getSingleton(beanName);
        // 存在缓存的情况
        if (sharedInstance != null && args == null) {
            if (logger.isTraceEnabled()) {
                // 当前bean正在循环引用的创建过程中
                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");
                }
                // 该bean直接通过缓存获取到,且不在循环引用的创建过程中
                else {
                    logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            /**
             * 返回对应的最终bean
             * 例如当一个类继承了FactoryBean,xml文件配置如下:
             *  <bean id="car" class="CarFactoryBean">
             * 则返回的应该是CarFactoryBean中getObject()的值,而非CarFactoryBean本身
             */
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

        else {
            // Fail if we're already creating this bean instance:
            // We're assumably within a circular reference.
            // 3. spring只能尝试解决单例的循环依赖,原型模式下循环依赖是无解的,只能直接报错
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            // Check if bean definition exists in this factory.
            BeanFactory parentBeanFactory = getParentBeanFactory();
            // 4. 当parentBeanFactory不为空,且该当前bean还从未被加载过也就是说xml里面根本就没有这个beanName(这一句是非常关键的),则尝试去parentBeanFactory中找找看
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // Not found -> check parent.
                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.
                    // 参数不为空,则委托parentBeanFactory使用显式参数调动
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else if (requiredType != null) {
                    // No args -> delegate to standard getBean method.
                    // 参数为空,则使用标准的getBean方法获取bean
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
                else {
                    // 否则使用默认的getBean方法
                    return (T) parentBeanFactory.getBean(nameToLookup);
                }
            }

            // 当前目的不单做类型检测还要创建bean,记录bean被创建
            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }

            try {
                // 5. 在第一阶段生产的数据均为GenericBeanDefinition,beanName完全有可能是子bean,在这里将会合并父bean并转换成RootBeanDefinition再进行创建
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // Guarantee initialization of beans that the current bean depends on.
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    // 6. 如果存在依赖,则递归的调用该方法进行创建依赖的bean
                    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 {
                            // 递归调用创建依赖的bean
                            getBean(dep);
                        }
                        catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                        }
                    }
                }

                // Create bean instance.
                // 7. 单例情况下
                if (mbd.isSingleton()) {
                    /**
                     * 核心代码,spring4.x中是这么写的,会更容易理解一点
                     * sharedInstance = getSingleton(beanName, new ObjectFactory<object>() {
                     *      getObject() {
                     *          return createBean(beanName, mbd, args);
                     *      }
                     *  }
                     *  即getSingleton第二个参数为ObjectFactory对象
                     */
                    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;
                        }
                    });
                    /**
                     * 8. 返回对应的最终bean
                     * 例如当一个类继承了FactoryBean,xml文件配置如下:
                     *  <bean id="car" class="CarFactoryBean">
                     * 则返回的应该是CarFactoryBean中getObject()的值,而非CarFactoryBean本身
                     */
                    bean = 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);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }

                // 指定scope模式的创建
                else {
                    String scopeName = mbd.getScope();
                    final 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);
                            }
                        });
                        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    }
                    catch (IllegalStateException ex) {
                        throw new BeanCreationException(beanName,
                                "Scope '" + scopeName + "' is not active for the current thread; consider " +
                                "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                ex);
                    }
                }
            }
            catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }

        // Check if required type matches the type of the actual bean instance.
        // 9. 检查bean是否是配置的类型
        if (requiredType != null && !requiredType.isInstance(bean)) {
            try {
                // 尝试转换类型
                T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
                if (convertedBean == null) {
                    throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
                }
                return 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;
    }
  1. 转换为真实beanName
    由于传入的beanName可能带&前缀需要获取工厂,也有可能是别名。因此需要做以下转换,找出真实的beanName
    protected String transformedBeanName(String name) {
        // 先调用transformedBeanName去除前缀,再调用canonicalName替换别名
        return canonicalName(BeanFactoryUtils.transformedBeanName(name));
    }

    // 去除前缀
    public static String transformedBeanName(String name) {
        Assert.notNull(name, "'name' must not be null");
        // FACTORY_BEAN_PREFIX = "&",不包含&前缀直接返回
        if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
            return name;
        }
        return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
            do {
                // 去除FACTORY_BEAN_PREFIX前缀,知道不包含前缀
                beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
            }
            while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
            return beanName;
        });
    }

    // 替换别名
    public String canonicalName(String name) {
        String canonicalName = name;
        // Handle aliasing...
        String resolvedName;
        do {
            // 通过aliasMap获取beanName,直到不再有别名
            resolvedName = this.aliasMap.get(canonicalName);
            // 可能存在A->B->C->D,所以需要一直循环直到没有别名位置
            if (resolvedName != null) {
                canonicalName = resolvedName;
            }
        }
        while (resolvedName != null);
        return canonicalName;
    }
  1. 尝试从缓存中获取bean,缓存主要有2个作用
    2.1 创建bean很耗性能,因此spring默认模式就是单例模式。单例模式下spring创建bean以后还会将其缓存到Map中以提高性能,当然也是为了确保单例模式下bean的唯一性。详见:spring源码日记10: spring从缓存中获取bean

2.2 解决循环依赖(仅限单例),即A依赖B,B又依赖A造成无限循环的问题。Spring的做法是在A实例化以后,填充属性以及初始化之前,就将A的引用提前暴露到缓存中。之后A执行填充属性,发现需要依赖B所以创建B,当B创建时发现需要依赖A则直接取A提前曝光的引用完成创建。而后A则继续执行自己的初始化完成创建。(循环依赖时spring源码的重点,也是面试最喜欢问的东西,内容比较多,这里看的一头雾水不要紧,后面会有专门讲解,详见:spring源码日记13: 循环依赖

  1. 原型模式循环依赖检测
    spring只会处理单例情况下的循环依赖,原型模式是无解的,只能抛出异常。详见:spring源码日记13: 循环依赖
    既然该模式无解,那么spring是如何检测?我们假设存在2个原型模式的类A跟类B,他们互相引用
    循环依赖检测.jpg
        // 3. spring只能尝试解决单例的循环依赖,原型模式下循环依赖是无解的,只能直接报错
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    protected boolean isPrototypeCurrentlyInCreation(String beanName) {
        // 获取原型模式正在创建的所有beanName
        Object curVal = this.prototypesCurrentlyInCreation.get();
        // 只要当前的beanName存在curVal中,说明发生循环依赖
        return (curVal != null &&
                (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
    }
  1. 尝试从parentBeanFactory获取
    这里的判断条件很重要,if (parentBeanFactory != null && !containsBeanDefinition(beanName)),第一个条件没什么好说的,如果parentBeanFactory都不存在那还获取个龟龟。第二个条件说明beanName不在BeanDefinition集合中,说明没有被解析过,也就是在xml或者注解中就找不到这个beanName,那么我们也只能去父类中递归寻找了,那不然也莫得办法了啊
  2. 转换GenericBeanDefinition
    第一阶段产生的BeanDefinition均是GenericBeanDefinition这种类型,接下去要进行bean的创建了。这里的BeanDefinition完全有可能是子类,缺少父类的信息,并不是究极完整体,因此我们需要合并继承父类,做完合并后转成RootBeanDefiniton,因为后续都是用RootBeanDefiniton进行创建的
  3. 创建依赖
    这里的依赖跟我们所说的依赖注入@Autowired是两码事,不要混淆。这里指的是@DependOn或者depend-on标签所标记的依赖。该标签是为了控制bean的创建顺序,比如我们的Dao层创建顺序肯定要在DataSource之后,因此在我们的bean还未create的时候,所depend-on的bean就应该先调用getBean进行创建。详细参考:denpend-on标签
// 6. 如果存在依赖,则递归的调用该方法进行创建依赖的bean
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 {
        // 递归调用创建依赖的bean
        因此,虽然递归调用创建了依赖的bean,但是并没有去获取它进行注入,比如:Object depBean = getBean(dep)
        我们的目的只是为了确保依赖的dep能够优先于当前bean创建
        getBean(dep);
    }
    catch (NoSuchBeanDefinitionException ex) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
    }
}
  1. bean的创建
    根据不同的scope会有不同的创建策略,这里我们专注单例模式的创建。这里也是spring最最核心的部分。详见spring 获取单例
  2. 获取最终的bean
    当我们获取到缓存中的bean时,这时候的bean有可能继承FactoryBean,而我们要的真正的bean则是getObject()方法所返回的bean,因此需要做一步转换。详见:spring源码日记11: spring获取单例
  3. 类型转换
    假设我们新建完的bean是String类型,而我们要求返回的bean是int类型,则spring会尽量帮我们转换成所需要的类型。spring做了很多转换器供使用,当然我们也可以自己定义。这里不做太多介绍。

spring创建bean是一个很复杂的过程,接下去将把重点放在循环依赖,和bean的创建上面。看完这部分源码感觉怎么样?是不是有一种被几百个人锤了的感觉?源码就是这样,被锤习惯了就好了...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,874评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,102评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,676评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,911评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,937评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,935评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,860评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,660评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,113评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,363评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,506评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,238评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,861评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,486评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,674评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,513评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,426评论 2 352

推荐阅读更多精彩内容

  • Spring源码解析——Bean的加载前奏 User user = (User)context.getBean("...
    仗剑诗篇阅读 867评论 0 2
  • 1. 详解Spring 中如何控制2个bean中的初始化顺序   开发过程中有这样一个场景,2个 bean 初始化...
    未名枯草阅读 1,299评论 0 1
  • 又是一大早在这里等车,自从来到这个快节奏的广州,每天六点爬起来,匆忙洗漱,去赶公交,一路上都操心着路...
    紫郁0720阅读 2,784评论 7 9
  • 当一个地方长期干旱,此时下雨叫甘霖;当一个地方已经洪涝,此时下雨叫灾难。 这正如一个人需要你的爱时,你对他的爱就是...
    泠风思语阅读 231评论 0 6
  • 【模型名称】 沉没成本 【模型说明】 排队才买到的电影票,看了一会才发现是超级大烂片,你看,还是不看?双十一抢到到...
    zerocards阅读 433评论 0 8