Spring IOC - FactoryBean (不是Bean
1. 什么是FactoryBean
在Spring IOC容器初始化时,很多地方有判断当前bean是否FactoryBean的逻辑,对FactoryBean和普通Bean有不同的处理。
那么什么是FactoryBean呢?
FactoryBean是一个接口,这个接口提供了下面几个方法。实现这个接口,可以让我们自定义Bean的创建过程。
public interface FactoryBean<T> {
@Nullable
T getObject() throws Exception;
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
2. Demo
先来看一个demo。
main 方法:
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyFactoryBean myFactoryBean = (MyFactoryBean) context.getBean("&myFactoryBean");
System.out.println(myFactoryBean);
User user = (User) context.getBean("myFactoryBean");
System.out.println(user);
}
MyBeanFactory:
@Component
public class MyFactoryBean implements FactoryBean{
@Override
public Object getObject() throws Exception {
// TODO Auto-generated method stub
return new User();
}
@Override
public Class getObjectType() {
// TODO Auto-generated method stub
return null;
}
}
User:
public class User {
}
AppConfig:
@ComponentScan("io.github.dunwu.spring.core.bean.factorybean")
public class AppConfig {
}
运行,输出如下:
io.github.dunwu.spring.core.bean.factorybean.MyFactoryBean@38c5cc4c
io.github.dunwu.spring.core.bean.factorybean.User@37918c79
可以看到,当以&为前缀,去get bean的时候,返回的是MyFactoryBean对象。而不带这个前缀时,返回的是在factory bean中自定义创建的user bean。
为什么会有这种结果呢,来看一下底层实现,一切就会变得明朗了。
3. Spring容器初始化FactoryBean对象
Spring容器初始化时,会扫描到myFactoryBean对象,并创建这个bean。
在DefaultListableBeanFactory.preInstantiateSingletons() 中,会判断是否是factory bean,如果是,则对bean name加上前缀&,创建bean,并以myFactoryBean为key(去除前缀),将创建好的bean放进singletonObjects中。
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
//省略此处代码
} else {}
后面从spring容器中get &myFactoryBean时,先通过beanName = transformedBeanName(name)去除前缀,以myFactoryBean为key,就能直接从singletonObjects中拿到已经创建好的factory bean。
4. 通过FactoryBean创建自定义Bean
当调用context.getBean("myFactoryBean")时,先通过getSingleton(beanName)拿到缓存的myFactoryBean。接下来getObjectForBeanInstance(sharedInstance, name, beanName, null)会最终调用如下逻辑:
Object object = null;
if (mbd == null) {
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
它会先从factoryBeanObjectCache里查看,是否已经有此bean的cache。
/** Cache of singleton objects created by FactoryBeans: FactoryBean name to object. */
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);
如果没有,那么getObjectFromFactoryBean就会最终调用此factory bean的getObject方法,创建bean。
5. 这不是BeanFactory
虽然它们名字像,但是作用完全不一样!
BeanFactory是个Factory,也就是IOC容器或对象工厂。在Spring中,BeanFactory是IOC容器的核心接口。
6. 应用场景
在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。