再读Spring源码之二 FactoryBean

  1. FactoryBean简介
  2. FactoryBean使用方法
    1. 在XML配置中使用FactoryBean
    2. 在注解中使用FactoryBean
  3. FactoryBean在spring中的实现原理
  4. 总结
  5. 参考资料

本文是《再读Spring源码》系列第二篇,一起来探讨下Spring框架常见扩展点FactoryBean,了解它的使用场景和实现原理。

FactoryBean简介

正如名字所言,FactoryBean本质上一个是工厂类,用于构造用户所需的Bean。通过FactoryBean可以隐藏到构造某些Bean的复杂参数以及构建逻辑,暴露更加简化的接口。

FactoryBean的接口方法非常简单:

public interface FactoryBean<T> {
  // 获取对象
  T getObject() throws Exception;
  // 获取对象类型
  Class<T> getObjectType();
  // 判断是否为单例
  boolean isSingleton();
}

FactoryBean使用方法

如下样例,我们可以通过StudentFactoryBean来构建Student实例:

public class Student extends Person {
    public Student(DateTime birthday, String name) {
        super(birthday, name);
    }
}

@Component
public class StudentFactoryBean implements FactoryBean<Student> {

    private String name;

    private Integer age;

    @Override
    public Student getObject() throws Exception {
        DateTime birthday = new DateTime().minusYears(age);
        return new Student(birthday, name);
    }

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

这里只是演示下用法,所以StudentFactoryBean的构建逻辑很简单。实际应用中,FactoryBean一般包含大量复杂业务逻辑。

在XML配置中使用FactoryBean

在老版本的spring框架里,常常通过xml配置注入bean。FactoryBean的常见用法是这样的:

<bean class = "a.b.c.StudentFactoryBean" id = "bob">
    <property name = "name" value ="Bob"/>
    <property name = "age" value ="20"/>
</bean>
<bean class = "a.b.c.Teacher" id = "Tom">
    <property name = "student" ref = "bob"/>
</bean>

这里bob实例的类型虽然为StudentFactoryBean,但在实例化的时候,Spring会调用StudentFactoryBean#getObject进行实例化,并返回Student对象。因此bob实例的最终类型是Student。

在注解中使用FactoryBean

在较新版本的Spring中,一般使用注解代替xml配置注入。Factory在注解注入方式的使用上稍有不同。一般如下:

@Bean
public Student getStudent() throws Exception {
    return new StudentFactoryBean().setAge(20).setName("Bob").getObject();
}

需要显式地调用getObject方法返回bean对象,并通过 @Bean 注解将类型为Student的bean注册到spring容器中。

FactoryBean在spring中的实现原理

FactoryBean会在spring容器启动过程中进行加载,可以跟踪以下代码:

-> SpringApplicationBuilder#run(String... args)
--> SpringApplicationBuilder#refreshContext(ConfigurableApplicationContext context)
---> SpringApplicationBuilder#refresh(ApplicationContext applicationContext)
----> SpringApplicationBuilder#refresh(ConfigurableApplicationContext applicationContext)
-----> AbstractApplicationContext#refresh()
------> AbstractApplicationContext#finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)
-------> DefaultListableBeanFactory#preInstantiateSingletons()

到最后我们会发现FactoryBean的初始化逻辑:

public void preInstantiateSingletons() throws BeansException {
// ...
    // Trigger initialization of all non-lazy singleton beans...
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            // 处理FactoryBean
            if (isFactoryBean(beanName)) {
                // 实例化该工厂bean(注意工厂bean的名字以&开头)
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof FactoryBean) {
                    FactoryBean<?> factory = (FactoryBean<?>) bean;
                    boolean isEagerInit;
                    // SmartFactoryBean是FactoryBean的特殊类型;
                    // 可以通过isEagerInit方法来判断其内部的bean是否需要提前实例化。
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(
                                (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
                                getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    // 如果需要提前实例化内部实例,则调用getBean进行实例化。(注意内部实例没有&前缀)
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
            }
            else {
                getBean(beanName);
            }
        }
    }
// ......
}

从上边我们可以有以下结论:

  • 1.工厂bean可以分为FactoryBean和SmartFactoryBean两种,后者的唯一区别是可以指示是否在初始化工厂Bean之后立即初始化内部实例;
  • 2.工厂bean的名字以&为前缀,去掉该前缀则为内部实例的默认名字。例如:”&studentFactoryBean”为工厂bean名,而”studentFactoryBean”是内部实例bean名;

从getBean方法跟踪进去,我们会发现实例化的时候针对工厂bean有特殊的初始化逻辑:

protected Object getObjectForBeanInstance(
            Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    // Don't let calling code try to dereference the factory if the bean isn't a factory.
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        }
        if (mbd != null) {
            mbd.isFactoryBean = true;
        }
        return beanInstance;
    }

    // 如果不是FactoryBean,则直接返回,不执行以下逻辑
    if (!(beanInstance instanceof FactoryBean)) {
        return beanInstance;
    }

    Object object = null;
    if (mbd != null) {
        mbd.isFactoryBean = true;
    }
    else {
        // 工厂bean的内部实例初始化后会有缓存,先尝试通过缓存获取
        object = getCachedObjectForFactoryBean(beanName);
    }

    if (object == null) {
        // 没有内部实例缓存,则尝试初始化
        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);
    }
    return object;
}

内部实例的初始化过程与其他单例的初始化过程类似:

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    if (factory.isSingleton() && containsSingleton(beanName)) {
        // 单例情况下,需要先加锁,然后检查缓存中是否已存在实例
        synchronized (getSingletonMutex()) {
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // 通过工厂bean获取实例
                object = doGetObjectFromFactoryBean(factory, beanName);
                // Only post-process and store if not put there already during getObject() call above
                // (e.g. because of circular reference processing triggered by custom getBean calls)
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    if (shouldPostProcess) {
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            // Temporarily return non-post-processed object, not storing it yet..
                            return object;
                        }
                        beforeSingletonCreation(beanName);
                        try {
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                    "Post-processing of FactoryBean's singleton object failed", ex);
                        }
                        finally {
                            afterSingletonCreation(beanName);
                        }
                    }
                    if (containsSingleton(beanName)) {
                        // 加入缓存
                        this.factoryBeanObjectCache.put(beanName, object);
                    }
                }
            }
            return object;
        }
    }
    else {
        // 非单例情况下,直接获取新实例
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (shouldPostProcess) {
            try {
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
            }
        }
        return object;
    }
}

从上我们可以得知,如果是单例的FactoryBean,实例化后的内部实例会放进缓存中,反复复用;而非单例的FactoryBean,每次实例化都会获取新建新的内部实例。

总结

本文详细介绍了FactoryBean的使用方法、实现原理和应用场景。

参考资料


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 duval1024@gmail.com

×

喜欢就点赞,疼爱就打赏