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。
getBean("&person")
时,由于容器内不存在"&person"
所以会通过createBean()
方法来创建bean,注意这里创建的仅仅是PersonFactoryBean对象,还没有创建Person。此时,Spring一级缓存里的bean还是:
[singletonObjects]
"person" -> PersonFactoryBean
getBean("&person")
由于beanName有&
前缀,所以Spring认为我们仅仅是要获取FactoryBean对象,而不是要获取真正的bean,所以会直接返回FactoryBean,而不会去调用getObject()
方法创建bean。
当我们要获取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对象缓存到factoryBeanObjectCache
Map容器中,后续再获取bean时直接从缓存里返回即可。