一文給你講清楚BeanFactory 和 FactoryBean 的關聯與區別

本文分享自華爲雲社區 《BeanFactory 和 FactoryBean 的關聯與區別》,作者:戰斧。

一、概括性的回答

兩者其實都是Spring提供的接口,如下

public interface FactoryBean<T> {
	T getObject() throws Exception;
	Class<?> getObjectType();
	boolean isSingleton();
}
public interface BeanFactory {
	String FACTORY_BEAN_PREFIX = "&";
	Object getBean(String name) throws BeansException;
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
	<T> T getBean(Class<T> requiredType) throws BeansException;
	Object getBean(String name, Object... args) throws BeansException;
	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
	boolean containsBean(String name);
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
	String[] getAliases(String name);
}

BeanFactory 就是我們常說的Spring容器,其內包含着大量的Bean,我們可以從BeanFactory 獲取到想要的Bean,或者查詢Bean的一些信息。

而FactoryBean則是衆多Bean裏的一種,只不過這種Bean是一種輔助Bean或者說中間人,它的作用是爲你提供另一個/一些Bean。

1.png

兩者一個比較形象的比喻就是BeanFactory就是一家工廠,我們可以通過提供物品名字,從工廠中得到各式各樣的物品,比如桌椅板凳,鍵盤鼠標 等等。而除此之外,我們還能獲取一種比較特殊的物品——生產線(FactoryBean),一般情況下,我們獲取生產線當然不是爲了它本身,而是爲了利用生產線生產出產品,所以當你提供生產線的名字,得到的其實是生產線生產的產品。當然,如果你就是想取這個生產線本身,那你提供的名字就得是 “&” + 生產線名。

二、FactoryBean

FactoryBean示例

我們先來看一下FactoryBean的基礎用法,簡而言之就是實現FactoryBean接口,然後重寫其中的getObject方法,如下:

public class ConfigLoaderFactoryBean implements FactoryBean<ConfigLoader> {
    private String configLocation;

    public void setConfigLocation(String configLocation) {
        this.configLocation = configLocation;
    }

    @Override
    public ConfigLoader getObject() throws Exception {
        if (configLocation.startsWith("file:")) {
            LocalConfigLoader configLoader = new LocalConfigLoader();
            configLoader.setFilePath(configLocation.substring(5));
            return configLoader;
        } else if (configLocation.startsWith("http:")) {
            RemoteConfigLoader configLoader = new RemoteConfigLoader();
            configLoader.setServerUrl(configLocation);
            return configLoader;
        } else {
            throw new IllegalArgumentException("Unsupported config location: " + configLocation);
        }
    }

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

然後把這個factoryBean放入容器中,你可以採用xml或者@Bean等形式注入。

<bean id="configLoader" class="com.example.ConfigLoaderFactoryBean">
    <property name="configLocation" value="http://example.com/config.json"/>
</bean

FactoryBean的必要性

我們上面介紹過,FactoryBean其實相當於一箇中間人,我們獲取它,往往不是需要它本身,而是希望通過它獲得另一個Bean,自然的我們會產生疑問,爲什麼要多此一舉?如果我們通過它是爲了獲得另一個Bean,那麼爲什麼不直接實例化另一個Bean然後放入Spring容器呢?比如在方法上使用@Bean註解。

2.png

這種想法無可厚非,主要是因爲factoryBean接口的誕生更早,所以早期很多的結構採用了這種方式。後續有了@Bean註解以後,在方法上使用@Bean註解也能實現複雜Bean的創建了。

那是不是所有情況都能使用@Bean來替代factoryBean呢?比如我們想每次獲取的Bean都是實時的,又比如我們需要一個計時器Bean,但你注入的Bean都被固定了,只有通過工廠,才能每次獲取都能得到一個實時的新Bean。同樣的,使用factoryBean還有一個懶加載的作用,對於某些複雜的Bean能在獲取時再進行實例化。

 

三、BeanFactory

BeanFactory與ApplicationContext

提及BeanFactory,自然而然的我們會想到Spring的重要特性IOC,IOC要求有一個能管理所有Bean的管家,而管家需要一個盛放這些Bean的容器,這個容器就是BeanFactory。
3.png

 

 

儘管我們在日常項目中,使用的容器是具有更全功能的ApplicationContext,但ApplicationContext也是BeanFactory的子接口,其除了單純的容器功能外,還有配置元信息,應用事件機制,資源管理等功能,所以我們可以說ApplicationContext是BeanFactory的增強版本。

BeanFactory的使用

在早期的spring項目中,我們經常會在代碼中指定使用某種BeanFactory ,並且使用如下方式去加載資源。

//讀取核心的配置文件
ClassPathResource resource = new ClassPathResource("MyContext.xml");
BeanFactory BeanFactory = new XmlBeanFactory(resource);

顧名思義XmlBeanFactory就是能夠讀取並解析xml資源,解析出各種Bean後存入自身,而在後期,springboot的大規模使用後,其內置的工廠可以解析xml、properties以及註解等多種配置來源。

當然,其實Spring本身就有相當的自動化程度,比如當我們在啓動類上使用。

@ImportResource(location = {"classpath:MyContext.xml"})

它也能導入內容,並根據資源後綴是否爲".groovy"判斷是使用GroovyBeanDefinitionReader.class 還是 XmlBeanDefinitionReader.class,對資源解析完成後,把Bean定義註冊進BeanFactory中。

四、總結

我們應當發現了:BeanFactory 和 FactoryBean 除了名字相似、都能包含一些Bean實例之外,其實沒有什麼相同的地方。前者是SpringIOC的核心,是存放一切Bean的容器;後者只不過是對複雜Bean的一種包裝,比如我們常用的myBatis組件,針對各個mapper級接口生成的Bean實例,就是以FactoryBean的形式存在Spring容器中的。

點擊關注,第一時間瞭解華爲雲新鮮技術~

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章