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。

两者一个比较形象的比喻就是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注解。

这种想法无可厚非,主要是因为factoryBean接口的诞生更早,所以早期很多的结构采用了这种方式。后续有了@Bean注解以后,在方法上使用@Bean注解也能实现复杂Bean的创建了。

那是不是所有情况都能使用@Bean来替代factoryBean呢?比如我们想每次获取的Bean都是实时的,又比如我们需要一个计时器Bean,但你注入的Bean都被固定了,只有通过工厂,才能每次获取都能得到一个实时的新Bean。同样的,使用factoryBean还有一个懒加载的作用,对于某些复杂的Bean能在获取时再进行实例化。

三、BeanFactory

BeanFactory与ApplicationContext

提及BeanFactory,自然而然的我们会想到Spring的重要特性IOC,IOC要求有一个能管理所有Bean的管家,而管家需要一个盛放这些Bean的容器,这个容器就是BeanFactory。

尽管我们在日常项目中,使用的容器是具有更全功能的ApplicationContext,但ApplicationContext也是BeanFactory的子接口,其除了单纯的容器功能外,还有配置元信息,应用事件机制,资源管理等功能,所以我们可以说ApplicationContext是BeanFactory的增强版本。

BeanFactory的使用

在早期的spring项目中,我们经常会在代码中指定使用某种BeanFactory ,并且使用如下方式去加载资源。

展开阅读全文

本文系作者在时代Java发表,未经许可,不得转载。

如有侵权,请联系nowjava@qq.com删除。

编辑于

关注时代Java

关注时代Java