Spring IOC - FactoryBean (不是Bean

2020-05-02  本文已影响0人  overflowedstack
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的逻辑。

上一篇下一篇

猜你喜欢

热点阅读