FactoryBean和BeanFactory的区别-CSDN博客

50 阅读4分钟

1. 前言

“BeanFactory和FactoryBean的区别是什么???”
这是Spring非常高频的一道面试题,BeanFactory是Spring bean容器的顶级接口,负责创建和维护容器内所有的bean对象。而FactoryBean是用来创建一类bean的接口,通过实现FactoryBean接口,重写FactoryBean#getObject()方法来生成bean对象。可以这么说,原本由Spring负责的创建bean的过程,通过实现FactoryBean接口就可以将创建bean对象的过程交给开发者自己来完成。
比如MyBatis在整合Spring的时候,Mapper接口是无法被实例化的,因此就算把Mapper注册到Spring容器,Spring也无法实例化Mapper对应的bean对象。MyBatis的做法是就是通过实现FactoryBean接口来手动生成Mapper接口的代理对象,对应的类是MapperFactoryBean。

2. FactoryBean

FactoryBean接口定义很简单,getObjectType()返回bean的类型,getObject()用来生成bean对象。

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

    @Nullable
    Class<?> getObjectType();

    default boolean isSingleton() {
        return true;
    }
}

FactoryBean本身也会被当成一个特殊的bean注册到Spring容器中,为了区分普通bean和FactoryBean,Spring的作法是给FactoryBean的beanName前面拼接一个固定的&字符。
例如现在有一个用来产生Person对象的FactoryBean,那么在容器内就会有两个bean。名称为"person"对应的是Person这个bean对象,名称为"&person"对应的是PersonFactoryBean对象。

@Component("person")
public class PersonFactoryBean implements FactoryBean<Person> {

    public PersonFactoryBean() {
        System.err.println("PersonFactoryBean");
    }

    @Override
    public Person getObject() throws Exception {
        return new Person();
    }

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

通过FactoryBean接口来生成bean的话,还有一个特殊的点需要注意:Spring容器启动时,默认会实例化FactoryBean对象,但是getObject()方法只有在需要用到bean时才会被调用。

3. 源码探究

Spring启动时默认会调用DefaultListableBeanFactory#preInstantiateSingletons()方法来实例化容器内所有非Lazy的单例bean,方式很简单,就是遍历容器内所有非Lazy的单例beanName,然后依次调用getBean()方法来创建bean。
这里Spring会进行判断,如果bean是FactoryBean的话,不会直接去创建bean本身,而仅仅是创建FactoryBean。
image.png
getBean("&person")时,由于容器内不存在"&person"所以会通过createBean()方法来创建bean,注意这里创建的仅仅是PersonFactoryBean对象,还没有创建Person。此时,Spring一级缓存里的bean还是:

[singletonObjects]
	"person" -> PersonFactoryBean

getBean("&person")由于beanName有&前缀,所以Spring认为我们仅仅是要获取FactoryBean对象,而不是要获取真正的bean,所以会直接返回FactoryBean,而不会去调用getObject()方法创建bean。
image.png
当我们要获取Person这个bean时,只需要取消beanName的&前缀,或者直接根据类型获取即可。

context.getBean(Person.class);
context.getBean("person");

此时,Spring会去一级缓存里拿"person"对应的bean,也就是PersonFactoryBean。但是PersonFactoryBean并不是我们要的啊,别着急,Spring会通过方法AbstractBeanFactory#getObjectForBeanInstance()来判断到底是要给你FactoryBean还是真正的bean对象。

protected Object getObjectForBeanInstance(
        Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    // 是否是FactoryBeanName? &前缀
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        // &前缀,但不是FactoryBean类型,抛异常
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        }
    }
    /**
     * 到这一步,beanInstance 要么是普通Bean,要么是FactoryBean
     * 1.如果想获取FactoryBean对象,请在name前加&前缀,这里会直接返回
     * 2.name没有&前缀,但是beanInstance是FactoryBean,则会走后续流程,基于FactoryBean生成Bean
     */
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }
    // 基于FactoryBean生成Bean
    Object object = null;
    if (mbd == null) {
        //从Bean工厂缓存中获取给定名称的Bean实例对象
        object = getCachedObjectForFactoryBean(beanName);
    }
    //让Bean工厂生产给定名称的Bean对象实例
    if (object == null) {
        // Return bean instance from factory.
        // 到这里已经明确知道beanInstance一定是FactoryBean类型
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // Caches object obtained from FactoryBean if it is a singleton.
        // 如果是单例对象,则缓存从FactoryBean获得的对象。
        // containsBeanDefinition(beanName) 检测beanDefinitionMap中 也就是在所有已经加载的类中检测是否定义beanName
        if (mbd == null && containsBeanDefinition(beanName)) {
            // 将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition
            //从容器中获取指定名称的Bean定义,如果继承基类,则合并基类相关属性
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        //如果从容器得到Bean定义信息,并且Bean定义信息是用户定义的而不是应用程序本身定义的,则让FactoryBean生产Bean实例对象
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        //调用FactoryBeanRegistrySupport类的getObjectFromFactoryBean方法,实现工厂Bean生产Bean对象实例的过程
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

如果name有&前缀就返回FactoryBean,否则通过FactoryBeanRegistrySupport#doGetObjectFromFactoryBean()方法去调用FactoryBean对象的getObject()方法来获取真正的bean对象,为了确保单例语义,FactoryBean#getObject()只能被调用一次,所以Spring会把首次生成的bean对象缓存到factoryBeanObjectCacheMap容器中,后续再获取bean时直接从缓存里返回即可。
image.png