17

再读Spring源码之二 FactoryBean

 3 years ago
source link: https://blog.duval.top/2020/10/07/%E5%86%8D%E8%AF%BBSpring%E6%BA%90%E7%A0%81%E4%B9%8B%E4%BA%8C-FactoryBean/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文是《再读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的使用方法、实现原理和应用场景。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK