Spring扩展点-FactoryBean

1.了解Spring中对于FactoryBean接口设计和使用

首先我们来看FactoryBean的源码:

public interface FactoryBean<T> {
    T getObject() throws Exception;

    Class<?> getObjectType();

    default boolean isSingleton() {
        return true;
    }
}

然后看它的一个子接口SmartFactoryBean

public interface SmartFactoryBean<T> extends FactoryBean<T> {
    default boolean isPrototype() {
        return false;
    }

    default boolean isEagerInit() {
        return false;
    }
}

FactoryBean的作用是什么?从FactoryBean这个类的名字我们可以知道其实是一个FactoryBean,也就是一个工厂Bean,既然是工厂Bean,肯定是用来造Bean的对吧!不错FactoryBean的作用就是给容器中导入组件。

对它的方法进行简单的介绍(既然SmartFactoryBeanFactoryBean的扩展,下面也一起讲解):

  • 1.getObject方法就是用来导入组件的,你可以在这里new一个对象并进行相关的配置,然后return就行了。
  • 2.getObjectType方法很明显嘛,就是你要导入的组件的类型,泛型T也就是你要导入的组件的类型。
  • 3.isSingleton方法用来决定这个Bean是否是单例的,isPrototype同理。
  • 4.isEagerInit方法决定即将导入这个Bean是否是懒加载的,如果return true,那么在Spring IOC容器启动的过程中就会将getObject导入的Bean进行实例化和初始化,如果return false,只要当你显式进行getBean时才会对导入的Bean进行实例化和初始化。

下面来看一个简单的FactoryBean的使用

@Component
public class MyFactoryBean implements SmartFactoryBean<User> {
    @Override
    public User getObject() throws Exception {
        return new User();
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }

    @Override
    public boolean isEagerInit() {
        return true;
    }
}

我们通过上面的简单代码就可以为容器中导入一个FactoryBean对象,而在FactoryBean中我们又给导入了一个User对象,因为我们配置了isEagerInitreturn true,因此在IOC容器启动的过程中就把user对象完成了实例化和初始化工作。为了方便进行说明,我们把myFactoryBean这个对象称作FactoryBean,把user这个对象成为FactoryBeanObject

2. 了解FactoryBeanSpring源码当中的实现?

2.1 在源码当中去找到在哪去创建FactoryBeanFactoryBeanObject

首先我们知道Spring IOC容器启动的核心流程都在AbstractApplicationContextrefresh方法中被定义了,因此我们无论看Spring的哪一块代码,都可以从refresh方法作为入口点。

image.png

首先我们找到实例化所有的单实例Bean的方法finishBeanFactoryInitialization,这个方法的最后一步是beanFactory.preInstantiateSingletons();,我们主要关注的就是这里的代码,这个方法的实现在它的子类DefaultListableBeanFactory当中。

image.png

2.2 看看FactoryBean是如何导入FactoryBeanObject的?

我们从上面的图当中可以很清楚的看到,这里FactoryBean和非FactoryBean就走到了不同的逻辑。

  • 1.如果是非FactoryBean,直接就getBean去创建Bean了。
  • 2.如果是FactoryBean,那么就把beanName加上前缀FACTORY_BEAN_PREFIX(也就是&),然后调用getBean从容器中获取/创建对象,比如上面我导入的myFactoryBean组件,这里getBean就会使用&getFactoryBean作为beanName

但是我们容器中真的有&getFactoryBean这个beanNameBean吗?很显然没有,我们来看getBean方法的逻辑。真正的逻辑在AbstractBeanFactory.doGetBean当中。

2.2.1 通过getBean获取FactoryBean

image.png

首先,进来第一步是transformedBeanName,它做了什么呢?

    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;
        });
    }

我们可以看到:

  • 1.如果name不是以&开头的,那么根本就不会进行转换,直接return
  • 2.如果名字是以&开头呢,就把名字前面的所有的&都去掉。

我们这里就明白了,transfromedBeanName其实就是把名字做一个转换,如果名字前有&的都给去掉,比如我们之前的&myFactoryBean就在这里变成了myFactoryBean

接着第二步

Object sharedInstance = getSingleton(beanName);

就是从三级缓存中拿对象(也就是我们背的八股文的循环依赖的解决方案的三级缓存)。但是我们这里根本就没有创建过myFactoryBean这个对象,从三级缓存中肯定是拿不到的,就要走后续的逻辑去创建myFactoryBean对象,创建的过程我们暂时不管,忽略掉。

2.2.2 FactoryBeanObject的创建

image.png

然后就是判断,这个Bean是否是SmartFactoryBean,如果是的话,还要考虑是否isEagerInit,这个我们在之前已经讲述过,我们这里配置了true,因此这里会去执行getBean,传入的beanNamemyFactoryBean

是不是很奇怪?按照我们刚刚的逻辑,已经创建了myFactoryBean这个对象了,而且还保存到bean对象当中了。这里还调用getBean干嘛,不是白干活吗?我们继续看doGetBean的逻辑。

        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

因为我们之前已经创建过了myFactoryBean对象,因此getSingleton就可以从三级缓存当中去拿到对象myFactoryBean,因此sharedInstance就拿到非空,就走到getObjectForBeanInstance这个方法当中去了,而FactoryBeanObject(user)的创建,就是在这个方法中去进行创建的,这也就是为什么我们要两次调用getBean的原因,因为在这里会触发不同的逻辑。

我们下面就来看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;
        }

        // Now we have the bean instance, which may be a normal bean or a FactoryBean.
        // If it's a FactoryBean, we use it to create a bean instance, unless the
        // caller actually wants a reference to the factory.
        if (!(beanInstance instanceof FactoryBean)) {
            return beanInstance;
        }

        Object object = null;
        if (mbd != null) {
            mbd.isFactoryBean = true;
        }
        else {
            object = getCachedObjectForFactoryBean(beanName);
        }
        if (object == null) {
            // Return bean instance from factory.
            FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
            // Caches object obtained from FactoryBean if it is a singleton.
            if (mbd == null && containsBeanDefinition(beanName)) {
                mbd = getMergedLocalBeanDefinition(beanName);
            }
            boolean synthetic = (mbd != null && mbd.isSynthetic());
            object = getObjectFromFactoryBean(factory, beanName, !synthetic);
        }
        return object;
    }

下面,我们进行挨个步骤的拆解分析

(1) getObjectFromFactoryBean步骤1-判断你调用getBean时传递的参数是否以&作为前缀

        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;
        }

这个部分的目的是为了什么?它其实就是判断,name是否是以&开头的,如果是的话,直接return beanInstance,也就是直接return FactoryBean。如果name不是以&开头的,那么不会进入这里,直接进入后续的逻辑。(需要注意的是:这里传递的参数beanName一定是去掉了&的,而name是没有去掉&的原生beanName)

(2) getObjectFromFactoryBean步骤2-判断getSingleton拿到的对象是否是FactoryBean

接着是下面这段代码

        if (!(beanInstance instanceof FactoryBean)) {
            return beanInstance;
        }

因为getSingleton返回非空的情况,都会进入这里,因此beanInstance有可能是一个FactoryBean,也可能就是一个普通的Bean,因此这里需要过滤掉所有的普通的Bean

(3) getObjectFromFactoryBean步骤3-尝试从缓存当中去获取FactoryBeanObject

        Object object = null;
        if (mbd != null) {
            mbd.isFactoryBean = true;
        }
        else {
            object = getCachedObjectForFactoryBean(beanName);
        }

这里传递的mbdnull,因此走else逻辑,而这个else中调用的方法的意图,从名字上我们就很清晰的看到,是从缓存中根据FactoryBeanbeanName去拿到它导入的FactoryBeanObject。(具体代码为return this.factoryBeanObjectCache.get(beanName);)

如果第一次来,这里很显然为拿不到,因此会走下面的逻辑;而如果已经创建过了,第二次来这里,直接就从缓存当中拿出来对象了。

(4) getObjectFromFactoryBean步骤4-创建FactoryBeanObject并加入到缓存当中

        if (object == null) {
            // Return bean instance from factory.
            FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
            // Caches object obtained from FactoryBean if it is a singleton.
            if (mbd == null && containsBeanDefinition(beanName)) {
                mbd = getMergedLocalBeanDefinition(beanName);
            }
            boolean synthetic = (mbd != null && mbd.isSynthetic());
            object = getObjectFromFactoryBean(factory, beanName, !synthetic);
        }
        return object;

这里意图很清晰嘛,就是调用getObjectFromFactoryBean去完成FactoryBeanObject对象的创建,具体的逻辑如下。

    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.如果是FactoryBeanObject是单例的,那么就加上锁,然后从缓存中拿对象,如果拿到了,那么就return,如果拿不到,就去调用FactoryBean.getObject方法去创建一个,然后再放入缓存当中。
  • 2.如果FactoryBean不是单例的?那么直接新创建一个并return就行了。

我们还要来看看doGetObjectFromFactoryBean中是如何创建FactoryBeanObject的?

    private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
        Object object;
        try {
            if (System.getSecurityManager() != null) {
                AccessControlContext acc = getAccessControlContext();
                try {
                    object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                object = factory.getObject();
            }
        }
        catch (FactoryBeanNotInitializedException ex) {
            throw new BeanCurrentlyInCreationException(beanName, ex.toString());
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
        }

        // Do not accept a null value for a FactoryBean that's not fully
        // initialized yet: Many FactoryBeans just return null then.
        if (object == null) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(
                        beanName, "FactoryBean which is currently in creation returned null from getObject");
            }
            object = new NullBean();
        }
        return object;
    }

其实很简单,就是简单调用了一下FactoryBean.getObject方法罢了。

(5) getObjectFromFactoryBean步骤5-对FactoryBeanObject的后置处理工作(postProcessObjectFromFactoryBean)

在创建完FactoryBeanObject之后,我们的Bean只是完成了实例化,并未完成初始化工作,比如有些特定的Bean需要完成动态代理,就会在这里进行回调。

postProcessObjectFromFactoryBean在子类当中的实现如下:

    protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
        return applyBeanPostProcessorsAfterInitialization(object, beanName);
    }

接着往下看

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

我们可以看到,它是遍历所有的BeanPostProcessor,执行它的postProcessAfterInitialization方法,也就是说BeanPostProcessor有机会在这里对FactoryBeanObject去完成动态代理,我们知道Spring AOP用到的BeanPostProcessor实现动态代理的逻辑,其实就是在postProcessAfterInitialization方法当中!

3. FactoryBean的更多知识

3.1 我们如何拿到FactoryBeanFactoryBeanObject

我们通过上面的源码分析可以知道:

  • 1.如果你在getBean时使用了&作为前缀(比如&myFactoryBean),那么在getSingleton拿到了FactoryBean之后,就会因为&前缀,导致不会从缓存当中去获取FactoryBeanObject,因此拿到的就是FactoryBean对象本身(比如myFactoryBean)。
  • 2.如果你在getBean时没加前缀&(比如myFactoryBean),那么在getSingleton之后,就会因为拿到的这个对象是FactoryBean,就会直接从缓存中拿到FactoryBean导入的FactoryBeanObject对象(如果是懒加载的话,在第一次获取时,会先进行创建)。
  • 3.那么IOC容器(三级缓存)中究竟有没有user作为beanNameBean,我们可以从源码当中可以看到,创建和导入User对象时,都没有使用到user,都使用到的FactoryBean本身的beanName,因此很显然缓存当中并没有user作为beanName的对象,你通过usergetBean肯定是获取不到的。

我们有很多种方式可以获取到FactoryBean/FactoryBeanObject

直接注入ApplicationContext/BeanFactory(或者实现相应的XXXAware接口),然后通过ApplicationContext/BeanFactory去调用getBean获取

    @Autowired
    ApplicationContext applicationContext;

    @Autowired
    BeanFactory beanFactory;

然后使用beanFactory.getBean("myFactoryBean")可以获取到我们创建的User对象,使用beanFactory.getBean("&myFactoryBean")可以获取到我们创建的FactoryBean对象本身,也就是myFactoryBean这个对象。

当然,也可以直接@Qualifier直接进行注入

    @Autowired
    @Qualifier("myFactoryBean")
    User user;

3.2 FactoryBean的应用?

MyBatis中去导入SqlSessionFactory这个核心组件时,就使用到了SqlSessionFactoryBean,而这个对象就是FactoryBean,当然其实MyBatis中的MapperFactoryBean也是使用的FactoryBean。其实整个MyBatis就是通过SpringFactoryBean机制为容器中导入Bean,从而实现相关的功能的。

个人博客:http://wanna1314y.top:8090/

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

推荐阅读更多精彩内容