spring的factoryBean原理

spring容器管理的bean中,有一种比较特殊的类型FactoryBean,这种类型的bean,自身即是一个bean对象,也能产生其他类型的bean对象,通过接口方法便可得知

//从工厂拿到bean对象
T getObject() throws Exception
//工厂产生bean的类型
Class<?> getObjectType()
//是否为单例的
default boolean isSingleton() {
        return true;
    }

为何需要FactoryBean来产生bean对象,而不是直接定义好呢,这个主要是在一些特殊的场景,比如bean对象的产生过程比较复杂,需要根据一些配置信息等等。

如何从容器中获取一个bean对象?这是spring容器内部的一个实现逻辑,其中主要有三个变量要注意区分

  • name,外部要获取bean对象传入的名称
  • beanName,spring容器内部维护的bean对应的beanName,通常情况下name与beanName是一样的,除了FactoryBean的情况
  • shareInstance ,beanName对应的bean实例
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
//这里主要是为了处理FactoryBean的场景
    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}

普通的bean,根据beanName获取到的shareInstance对象就是最终要返回的对象,而FactoryBean就不太一样了
例如:程序中定义了一个User对象,还有一个UserFactoryBean,User对象没有注入到spring容器,UserFactoryBean注入了,然后UserFactoryBean的工厂方法getObject返回的是一个User对象。
那么,如果通过spring容器要获取到UserFactoryBean自身的bean实例,传入的name是&userFactoryBean,要获取User的bean实例,传入的name是userFactoryBean,若name传的是user,那么会找不到bean对象,这个也很好理解,User是通过UserFactoryBean产生的,所以传入userFactoryBean。

接下来看下spring源码是如何处理这种情况的

protected Object getObjectForBeanInstance(
        Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    //根据name判断要获取的bean是FactoryBean自身,还是其工厂方法产生的,判断的依据就是name是否有&前缀
    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;
    }

    //假如beanInstance自身就是一个普通的bean对象,那么直接返回
    if (!(beanInstance instanceof FactoryBean)) {
        return beanInstance;
    }

    Object object = null;
    if (mbd != null) {
        mbd.isFactoryBean = true;
    }
    else {
//根据beanName获取缓存中工厂方法产生的对象
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        if (mbd == null && containsBeanDefinition(beanName)) {
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        boolean synthetic = (mbd != null && mbd.isSynthetic());
//获取工厂方法产生的bean对象,若是Singleton的,那么还会将对象缓存起来
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

这个方法逻辑很简单,如果beanInstance是普通的bean,那么直接返回;如果是FactoryBean的话,那么根据name判断程序是要获取FactoryBean自身的bean实例,还是其工厂方法产生的bean实例。若是自身的,那么直接返回;若是要获取工厂产生的,那么先看缓存有没,有的话直接拿,没有的话,再进入工厂方法创建bean。

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                object = doGetObjectFromFactoryBean(factory, beanName);
                // Only post-process and store if not put there already during getObject() call above
                // (e.g. because of circular reference processing triggered by custom getBean calls)
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    if (shouldPostProcess) {
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            // Temporarily return non-post-processed object, not storing it yet..
                            return object;
                        }
                        beforeSingletonCreation(beanName);
                        try {
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                    "Post-processing of FactoryBean's singleton object failed", ex);
                        }
                        finally {
                            afterSingletonCreation(beanName);
                        }
                    }
                    if (containsSingleton(beanName)) {
                        this.factoryBeanObjectCache.put(beanName, object);
                    }
                }
            }
            return object;
        }
    }
    else {
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (shouldPostProcess) {
            try {
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
            }
        }
        return object;
    }
}

这个方法不仅仅是执行工厂方法,有几个点要注意

  1. 调用完工厂方法拿到对象后,并不是直接返回,还会调用BeanPostProcessor的postProcessAfterInitialization方法。spring内置的几个processor要执行
  2. 会判断工厂产生的bean是否为singleton,若是的话,还会加入到factoryBeanObjectCache,这样每次获取到的bean就是单例的,不会重新走工厂方法来创建一个新对象。

以上就是从spring容器中获取FactoryBean和其工厂产生的bean的逻辑,细心的可能会发现,程序中有时并不是根据name来获取bean,而是通过Class对象,那么如果通过class对象来获取bean是如何处理的,跟踪源码会发现,也是需要根据Class类型获取到对应的name,再走上述讲的逻辑。所以重点就看下如果通过Class获取到name

private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
    List<String> result = new ArrayList<>();

    // Check all bean definitions.
    for (String beanName : this.beanDefinitionNames) {
        // Only consider bean as eligible if the bean name is not defined as alias for some other bean.
        if (!isAlias(beanName)) {
            try {
                RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                // Only check bean definition if it is complete.
                if (!mbd.isAbstract() && (allowEagerInit ||
                        (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
                                !requiresEagerInitForType(mbd.getFactoryBeanName()))) {
                    boolean isFactoryBean = isFactoryBean(beanName, mbd);
                    BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
                    boolean matchFound = false;
                    boolean allowFactoryBeanInit = (allowEagerInit || containsSingleton(beanName));
                    boolean isNonLazyDecorated = (dbd != null && !mbd.isLazyInit());
                    if (!isFactoryBean) {
                        if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) {
                            matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
                        }
                    }
                    else {
                        if (includeNonSingletons || isNonLazyDecorated ||
                                (allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) {
                            matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
                        }
                        if (!matchFound) {
                            // In case of FactoryBean, try to match FactoryBean instance itself next.
                            beanName = FACTORY_BEAN_PREFIX + beanName;
                            matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
                        }
                    }
                    if (matchFound) {
                        result.add(beanName);
                    }
                }
            }
            catch (CannotLoadBeanClassException | BeanDefinitionStoreException ex) {
                
            }
            catch (NoSuchBeanDefinitionException ex) {
            
            }
        }
    }
        //省略.......
    return StringUtils.toStringArray(result);
}

这个方法的主要逻辑就是,循环spring容器的所有beanName,然后再根据isTypeMatch方法来判断当前的Class类型是否能匹配上这个beanName,若是的话,会返回。
这里不管当前beanName对应的bean是否为FactoryBean,都走的是isTypeMatch方法。所以问题的关键就在这方法内部,但是会发现如果isFactoryBean是true的话,可能需要执行两次isTypeMatch,这结合isTypeMatch方法逻辑便可知道了

    protected boolean isTypeMatch(String name, ResolvableType typeToMatch, boolean allowFactoryBeanInit)
            throws NoSuchBeanDefinitionException {

        String beanName = transformedBeanName(name);
        boolean isFactoryDereference = BeanFactoryUtils.isFactoryDereference(name);

        // Check manually registered singletons.
        Object beanInstance = getSingleton(beanName, false);
        if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
            if (beanInstance instanceof FactoryBean) {
                if (!isFactoryDereference) {
                    Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
                    return (type != null && typeToMatch.isAssignableFrom(type));
                }
                else {
                    return typeToMatch.isInstance(beanInstance);
                }
            }
            else if (!isFactoryDereference) {
                if (typeToMatch.isInstance(beanInstance)) {
                    // Direct match for exposed instance?
                    return true;
                }

这个方法比较长,只挑前面一部分代码就可以大概的理解逻辑了
根据beanName获取到bean对象,如果bean对象只是普通类型的,那么就直接对比对象是否为ResolvableType 的,若是则匹配成功(实际会复杂一些)
若bean对象是FactoryBean的,那么根据传进来的name是否要获取FactoryBean自身的,若是的话,也是直接比较(和普通类型的一样)。若不是,说明要获取的是工厂方法产生的bean,那么需要调用FactoryBean的getObjectType方法,来判断工厂返回的类型能否匹配上ResolvableType

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