Spring中FactoryBean的做用和實現原理

BeanFactory與FactoryBean,相信不少剛翻看Spring源碼的同窗跟我同樣很好奇這倆貨怎麼長得這麼像,分別都是幹啥用的。BeanFactory是Spring中Bean工廠的頂層接口,也是咱們常說的SpringIOC容器,它定下了IOC容器的一些規範和經常使用方法並管理着Spring中全部的Bean,今天咱們不講它,咱們看一下後面那個FactoryBean。html

先說下FactoryBean和其做用再開始分析:首先它是一個Bean,但又不只僅是一個Bean。它是一個能生產或修飾對象生成的工廠Bean,相似於設計模式中的工廠模式和裝飾器模式。它能在須要的時候生產一個對象,且不只僅限於它自身,它能返回任何Bean的實例。java


首發地址:https://www.guitu18.com/post/2019/04/28/33.htmlspring

上面的解釋有點抽象,那麼咱們瀏覽一遍FactoryBean在Spring中是怎麼應用的就好懂了。咱們都知道在Spring中咱們的Bean都會被Spring的IOC容器所管理,在AbstractApplicationContext中有一個很重要的方法:refresh(),項目啓動或重啓的時候refresh()會調用getBean()初始化全部的Bean,這個getBean()最終會指向AbstractBeanFactory中的getBean()方法。設計模式

在AbstractBeanFactory中,不論是根據名稱仍是根據類型,getBean()最終都會調用doGetBean(),在doGetBean()方法中一開始就獲取了名稱beanName和實例sharedInstance,這個方法太長,這裏就貼前面兩行。框架

String beanName = this.transformedBeanName(name);
        Object sharedInstance = this.getSingleton(beanName);

transformedBeanName(name)是爲了獲取Bean真正的名稱,它會去掉name前面的'&',而getSingleton(beanName)是從父類容器singletonObjects中取的這個Bean的實例。在Spring中還有不少這樣的容器,好比DefaultListableBeanFactory中的beanDefinitionMap,它就是的IOC容器真正保存Bean的地方,它是一個HashMap。相似的還有FactoryBeanRegistrySupport中的factoryBeanObjectCache等。ide

回到doGetBean()方法中,拿到sharedInstance後,後面的一大堆操做作了單例、多例等判斷,最終會走到this.getObjectForBeanInstance(),關鍵部分就在這個方法中,進入方法代碼。post

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, 
                                          @Nullable RootBeanDefinition mbd) {
        if (BeanFactoryUtils.isFactoryDereference(name)) {
            if (beanInstance instanceof NullBean) {
                return beanInstance;
            }
            if (!(beanInstance instanceof FactoryBean)) {
                throw new BeanIsNotAFactoryException(this.transformedBeanName(name), beanInstance.getClass());
            }
        }
        if (beanInstance instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
            Object object = null;
            if (mbd == null) {
                object = this.getCachedObjectForFactoryBean(beanName);
            }
            if (object == null) {
                FactoryBean<?> factory = (FactoryBean)beanInstance;
                if (mbd == null && this.containsBeanDefinition(beanName)) {
                    mbd = this.getMergedLocalBeanDefinition(beanName);
                }
                boolean synthetic = mbd != null && mbd.isSynthetic();
                object = this.getObjectFromFactoryBean(factory, beanName, !synthetic);
            }
            return object;
        } else {
            return beanInstance;
        }
    }

在上面的代碼中有兩個判斷分別是beanInstance instanceof FactoryBeanBeanFactoryUtils.isFactoryDereference(name),前面判斷的是beanInstance是否屬於FactoryBean或其子類的實例,後面這個方法判斷name是否不爲空且以&開頭。性能

public static boolean isFactoryDereference(@Nullable String name) {
        return name != null && name.startsWith("&");
    }

若是beanInstance不屬於FactoryBean或其子類的實例,或者name是以&開頭就直接返回實例對象beanInstance,不然進入到if分支中。在if分支裏的各類if .. ==null判斷是爲了提升性能,我們只挑關鍵部分看:object = this.getObjectFromFactoryBean(factory, beanName, !synthetic); 繼續跟蹤進去看代碼。測試

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
        if (factory.isSingleton() && this.containsSingleton(beanName)) {
            synchronized(this.getSingletonMutex()) {
                Object object = this.factoryBeanObjectCache.get(beanName);
                if (object == null) {
                    object = this.doGetObjectFromFactoryBean(factory, beanName);
                    Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                    if (alreadyThere != null) {
                        object = alreadyThere;
                    } else {
                        if (shouldPostProcess) {
                            if (this.isSingletonCurrentlyInCreation(beanName)) {
                                return object;
                            }
                            this.beforeSingletonCreation(beanName);
                            try {
                                object = this.postProcessObjectFromFactoryBean(object, beanName);
                            } catch (Throwable var14) {
                                throw new BeanCreationException(beanName, 
                                            "Post-processing of FactoryBean's singleton object failed", var14);
                            } finally {
                                this.afterSingletonCreation(beanName);
                            }
                        }
                        if (this.containsSingleton(beanName)) {
                            this.factoryBeanObjectCache.put(beanName, object);
                        }
                    }
                }
                return object;
            }
        } else {
            Object object = this.doGetObjectFromFactoryBean(factory, beanName);
            if (shouldPostProcess) {
                try {
                    object = this.postProcessObjectFromFactoryBean(object, beanName);
                } catch (Throwable var17) {
                    throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", var17);
                }
            }
            return object;
        }
    }

這裏面也是作了不少判斷,我們也只挑關鍵部分看。這裏說一下我的想法,看源碼的時候若是咱們一直追究全部的細節那會讓咱們會越陷越深,掉入細節的無底洞,稍不留神腦回路跟不上就會蒙圈。咱們要學會找源碼中的關鍵部分看,弄懂主要流程和本次看源碼的目的的那部分就行。等咱們對Spring總體有了一個很好的理解以後,再回頭看以前不懂的代碼就會豁然開朗。在上面這個方法中不論是走上面的if分支仍是到下邊的else中,關鍵部分就是object = this.doGetObjectFromFactoryBean(factory, beanName)這段代碼調用,繼續點進去。ui

private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
        Object object;
        try {
            if (System.getSecurityManager() != null) {
                AccessControlContext acc = this.getAccessControlContext();
                try {
                    object = AccessController.doPrivileged(factory::getObject, acc);
                } catch (PrivilegedActionException var6) {
                    throw var6.getException();
                }
            } else {
                object = factory.getObject();
            }
        } catch (FactoryBeanNotInitializedException var7) {
            throw new BeanCurrentlyInCreationException(beanName, var7.toString());
        } catch (Throwable var8) {
            throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", var8);
        }
        if (object == null) {
            if (this.isSingletonCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName, 
                        "FactoryBean which is currently in creation returned null from getObject");
            }
            object = new NullBean();
        }
        return object;
    }

在這個方法中有一行關鍵代碼:object = factory.getObject(); 這個factory就是咱們傳入的beanInstance實例。繞了這麼一大圈,getBean方法返回的竟然是咱們實現FactoryBean接口定義的getObject方法。

那麼跟一開始對FactoryBean的描述印證了,FactoryBean是一個能生產或修飾對象生成的工廠Bean。一個Bean若是實現了FactoryBean接口,那麼根據該Bean的名稱獲取到的其實是getObject()返回的對象,而不是這個Bean自身實例,若是要獲取這個Bean自身實例,那麼須要在名稱前面加上'&'符號

通常狀況下,Spring經過反射機制利用 的class屬性指定實現類實例化Bean,在某些狀況下,實例化Bean過程比較複雜,若是按照傳統的方式,則須要在 中提供大量的配置信息。配置方式的靈活性是受限的,這時採用編碼的方式可能會獲得一個簡單的方案。Spring爲此提供了一個org.springframework.bean.factory.FactoryBean的工廠類接口,用戶能夠經過實現該接口定製實例化Bean的邏輯。FactoryBean接口對於Spring框架來講佔用重要的地位,Spring自身就提供了70多個FactoryBean的實現。它們隱藏了實例化一些複雜Bean的細節,給上層應用帶來了便利。從Spring3.0開始,FactoryBean開始支持泛型,即接口聲明改成FactoryBean 的形式


原理弄明白了,下面經過代碼測試驗證上面的流程,先定義一個Bean實現FactoryBean接口。

@Component
public class MyBean implements FactoryBean {
    private String message;
    public MyBean() {
        this.message = "經過構造方法初始化實例";
    }
    @Override
    public Object getObject() throws Exception {
        MyBean myBean = new MyBean();
        myBean.message = "經過FactoryBean.getObject()建立實例";
        // 這裏並不必定要返回MyBean自身的實例,能夠是其餘任何對象的實例
        return myBean;
    }
    @Override
    public Class<?> getObjectType() {
        return MyBean.class;
    }
    public String getMessage() {
        return message;
    }
}

MyBean實現了FactoryBean接口的兩個方法,getObject()是能夠返回任何對象的實例的,這裏測試就返回MyBean自身實例,且返回前給message字段賦值。同時在構造方法中也爲message賦值。而後測試代碼中先經過名稱獲取Bean實例,打印message的內容,再經過'&'+名稱獲取實例並打印message內容。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class FactoryBeanTest {
    @Autowired
    private ApplicationContext context;
    @Test
    public void test() {
        MyBean myBean1 = (MyBean) context.getBean("myBean");
        System.out.println("myBean1 = " + myBean1.getMessage());
        MyBean myBean2 = (MyBean) context.getBean("&myBean");
        System.out.println("myBean2 = " + myBean2.getMessage());
        System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
    }
}
myBean1 = 經過FactoryBean.getObject()初始化實例
myBean2 = 經過構造方法初始化實例
myBean1.equals(myBean2) = false

經過測試咱們發現獲取的兩個實例中的message的值不同,對比兩個對象的引用也不相同。上述所講關於FactoryBean的內容印證完畢,本文結束。

相關文章
相關標籤/搜索