说到spring,相信对于BeanFactory大家肯定都不陌生,百度上随便一搜,99都会说什么字如其意:bean工厂啊,容器,管理bean的。。。对于BeanFactory其实就是这样子的,这里我说的肯定没有别人好,这边就不一一赘述了。今天这边主要来谈谈FactoryBean。
先来看一个颠覆你三观的案例:
@Service
public class FactoryBeanTest implements FactoryBean<TestSingleton> {
@Override
public TestSingleton getObject() throws Exception {
return new TestSingleton();
}
@Override
public Class<?> getObjectType() {
return TestSingleton.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
我们交给spring管理了一个bean:FactoryBeanTest(beanid:factoryBeanTest ) ,但是我们根据annotationConfigApplicationContext去get这个bean的时候,拿到的却是:TestSingleton
如果我们在beanid前加一个 & ,就可以拿到FactoryBeanTest这个对象了。
细心的哥哥们可能注意到了,我们的FactoryBeanTest 实现了:FactoryBean 接口,那么我们就来分析下,这个FactoryBean 接口,以及spring容器在初始化时,是怎么处理这个接口的。
这个接口有三个方法:
//指定创建的具体bean对象的类型
@Nullable
Class<?> getObjectType();
//设置创建的对象是否单例,可以不重写默认是单例的,如果需要设置原型需要重写此方法,返回false
default boolean isSingleton() {
return true;
}
//创建对象bean 具体创建具体对象是由此getObject()方法来创建并返回的
@Nullable
T getObject() throws Exception;
那么问题来了:
1,spring在初始化的时候到底做了什么,将原本是FactoryBeanTest的bean替换成了TestSingleton
2,为什么annotationConfigApplicationContext.getBean("&factoryBeanTest")可以拿到FactoryBeanTest
下面来看看spring容器初始化的时候到底做了什么:
首先看下单例的FactoryBean
在spring容器初始化完成,创建bean实例的时候,方法调用入口如下:
org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
可以看到,我们这里有13个beanname,5个是我们自己配置的,另外八个事spring内置的一些bean,我们不做关注,这边直接看
factoryBeanTest。
String FACTORY_BEAN_PREFIX = "&";
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
上面这段是初始化源码,很容易看到了,在根据beanname获取到这个bean的BeanDefinition后,会判断这个bean是不是一个factoryBean,如果是,则使用 &+beanname去做getBean操作。至于这个getBean细节不做详细赘述,意思是如果根据beanname可以获取到Object则直接返回,如果get不到,会基于当前beanname去做一个createBean操作,这样就解释了上述问题2:为什么annotationConfigApplicationContext.getBean("&factoryBeanTest")可以拿到FactoryBeanTest
那么我们在回到第一个问题,在annotationConfigApplicationContext.getBean(“factoryBeanTest”)的时候,为什么拿到的是TestSingleton,继续往下断点,
org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
在这里我们单独拿出这个getObjectFromFactoryBean(factory, beanName, !synthetic);方法,发现出来的就是我们的TestSingleton对象了,那么继续往下看:
org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean
org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean
关键的地方找到了,我来解释下,就是再容器初始话的时候调用getbean的时候会去创建一次bean,再创建的时候发现如果这个bean是FactoryBean类型的,则会调用FactoryBean的getObject方法,得到这个方法返回的对象,并且如果这个bean是单例的,会缓存到一个名为factoryBeanObjectCache的ConcurrentHashMap中,key(beanname)就是当前FactoryBean的beanname,下次拿的时候如果是原型的,则每次都会去调用FactoryBean的getObject方法拿到原型对象,如果是单例的,则直接在缓存factoryBeanObjectCache中根据beanname去拿到单例对象并返回。
据此,我们问题一也通过源码很好的解释了。(补充一句,在我们的spring-mybatis的核心源码中就使用了FactoryBean,有时间再来和大家分享一下!)
好了今天弄清楚了FactoryBean的相关问题,以及从源码的角度去分析了FactoryBean的作用以及spring内部是如何运行的,希望对大家有所帮助,同时有不足之处还望大家多多指正!